Document the rest of `pathfinder_content`
This commit is contained in:
parent
bad735e88d
commit
4060baa80c
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue