Document `pathfinder_content` more

This commit is contained in:
Patrick Walton 2020-07-15 17:58:31 -07:00
parent 36c347afef
commit c24fdf318d
8 changed files with 129 additions and 7 deletions

View File

@ -8,6 +8,10 @@
// 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.
//! Clips polygons.
#![allow(deprecated)]
use crate::outline::{Contour, ContourIterFlags, PointFlags, PushSegmentFlags}; use crate::outline::{Contour, ContourIterFlags, PointFlags, PushSegmentFlags};
use crate::segment::{CubicSegment, Segment}; use crate::segment::{CubicSegment, Segment};
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
@ -310,16 +314,25 @@ enum EdgeRelativeLocation {
// 3D quad clipping // 3D quad clipping
/// Clips polygons in 3D homogeneous coordinates against the (-1, 1) normalized device coordinate
/// cube.
#[deprecated]
pub struct PolygonClipper3D { pub struct PolygonClipper3D {
subject: Vec<Vector4F>, subject: Vec<Vector4F>,
} }
impl PolygonClipper3D { impl PolygonClipper3D {
/// Creates a new polygon clipper with the vertices of the given polygon-to-be-clipped, in 3D
/// homogeneous coordinates.
#[deprecated]
#[inline] #[inline]
pub fn new(subject: Vec<Vector4F>) -> PolygonClipper3D { pub fn new(subject: Vec<Vector4F>) -> PolygonClipper3D {
PolygonClipper3D { subject } PolygonClipper3D { subject }
} }
/// Clips the subject polygon against the (-1, 1) normalized device coordinate cube and returns
/// the resulting vertices, in 3D homogeneous coordinates.
#[deprecated]
pub fn clip(mut self) -> Vec<Vector4F> { pub fn clip(mut self) -> Vec<Vector4F> {
// TODO(pcwalton): Fast path for completely contained polygon? // TODO(pcwalton): Fast path for completely contained polygon?

View File

@ -8,13 +8,14 @@
// 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.
//! Line dashing support. //! Transforms a stroke into a dashed stroke.
use crate::outline::{Contour, ContourIterFlags, Outline, PushSegmentFlags}; use crate::outline::{Contour, ContourIterFlags, Outline, PushSegmentFlags};
use std::mem; use std::mem;
const EPSILON: f32 = 0.0001; const EPSILON: f32 = 0.0001;
/// Transforms a stroke into a dashed stroke.
pub struct OutlineDash<'a> { pub struct OutlineDash<'a> {
input: &'a Outline, input: &'a Outline,
output: Outline, output: Outline,
@ -22,17 +23,34 @@ pub struct OutlineDash<'a> {
} }
impl<'a> OutlineDash<'a> { impl<'a> OutlineDash<'a> {
/// Creates a new outline dasher for the given stroke.
///
/// Arguments:
///
/// * `input`: The input stroke to be dashed. This must not yet been converted to a fill; i.e.
/// it is assumed that the stroke-to-fill conversion happens *after* this dashing process.
///
/// * `dashes`: The list of dashes, specified as alternating pixel lengths of lines and gaps
/// that describe the pattern. See
/// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/setLineDash.
///
/// * `offset`: The line dash offset, or "phase". See
/// https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/lineDashOffset.
#[inline] #[inline]
pub fn new(input: &'a Outline, dashes: &'a [f32], offset: f32) -> OutlineDash<'a> { pub fn new(input: &'a Outline, dashes: &'a [f32], offset: f32) -> OutlineDash<'a> {
OutlineDash { input, output: Outline::new(), state: DashState::new(dashes, offset) } OutlineDash { input, output: Outline::new(), state: DashState::new(dashes, offset) }
} }
/// Performs the dashing operation.
///
/// The results can be retrieved with the `into_outline()` method.
pub fn dash(&mut self) { pub fn dash(&mut self) {
for contour in &self.input.contours { for contour in &self.input.contours {
ContourDash::new(contour, &mut self.output, &mut self.state).dash() ContourDash::new(contour, &mut self.output, &mut self.state).dash()
} }
} }
/// Returns the resulting dashed outline.
pub fn into_outline(mut self) -> Outline { pub fn into_outline(mut self) -> Outline {
if self.state.is_on() { if self.state.is_on() {
self.output.push_contour(self.state.output); self.output.push_contour(self.state.output);

View File

@ -15,20 +15,28 @@ use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::vector::Vector2F; use pathfinder_geometry::vector::Vector2F;
use pathfinder_simd::default::F32x2; use pathfinder_simd::default::F32x2;
/// This intentionally does not precisely match what Core Graphics does (a /// A defringing kernel for LCD screens that approximates the macOS/iOS look.
/// Lanczos function), because we don't want any ringing artefacts. ///
/// This intentionally does not precisely match what Core Graphics does (a Lanczos function),
/// because we don't want any ringing artefacts.
pub static DEFRINGING_KERNEL_CORE_GRAPHICS: DefringingKernel = pub static DEFRINGING_KERNEL_CORE_GRAPHICS: DefringingKernel =
DefringingKernel([0.033165660, 0.102074051, 0.221434336, 0.286651906]); DefringingKernel([0.033165660, 0.102074051, 0.221434336, 0.286651906]);
/// A defringing kernel for LCD screens that approximates the FreeType look.
pub static DEFRINGING_KERNEL_FREETYPE: DefringingKernel = pub static DEFRINGING_KERNEL_FREETYPE: DefringingKernel =
DefringingKernel([0.0, 0.031372549, 0.301960784, 0.337254902]); DefringingKernel([0.0, 0.031372549, 0.301960784, 0.337254902]);
/// Stem darkening factors that approximate the macOS look.
///
/// Should match macOS 10.13 High Sierra. /// Should match macOS 10.13 High Sierra.
pub static STEM_DARKENING_FACTORS: [f32; 2] = [0.0121, 0.0121 * 1.25]; pub static STEM_DARKENING_FACTORS: [f32; 2] = [0.0121, 0.0121 * 1.25];
/// The maximum number of pixels we are willing to expand outlines by to match the macOS look.
///
/// Should match macOS 10.13 High Sierra. /// Should match macOS 10.13 High Sierra.
pub const MAX_STEM_DARKENING_AMOUNT: [f32; 2] = [0.3, 0.3]; pub const MAX_STEM_DARKENING_AMOUNT: [f32; 2] = [0.3, 0.3];
/// This value is a subjective cutoff. Above this ppem value, no stem darkening is performed. /// A subjective cutoff. Above this ppem value, no stem darkening is performed.
pub const MAX_STEM_DARKENING_PIXELS_PER_EM: f32 = 72.0; pub const MAX_STEM_DARKENING_PIXELS_PER_EM: f32 = 72.0;
/// The shader that should be used when compositing this layer onto its destination. /// The shader that should be used when compositing this layer onto its destination.

View File

@ -8,10 +8,16 @@
// 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.
//! Fill rules. //! The fill rule, which determines how self-intersecting paths are filled.
/// The fill rule, which determines how self-intersecting paths are filled.
///
/// Paths that don't intersect themselves (and have no holes) are unaffected by the choice of fill
/// rule.
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub enum FillRule { pub enum FillRule {
/// The nonzero rule: https://en.wikipedia.org/wiki/Nonzero-rule
Winding, Winding,
/// The even-odd rule: https://en.wikipedia.org/wiki/Even%E2%80%93odd_rule
EvenOdd, EvenOdd,
} }

View File

@ -8,6 +8,8 @@
// 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.
//! Gradient effects that paths can be filled with.
use crate::util; use crate::util;
use pathfinder_color::ColorU; use pathfinder_color::ColorU;
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;

View File

@ -33,6 +33,7 @@ pub struct Pattern {
flags: PatternFlags, flags: PatternFlags,
} }
/// 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 {
Image(Image), Image(Image),
@ -42,9 +43,10 @@ pub enum PatternSource {
} }
} }
/// RGBA, non-premultiplied. /// A raster image, in 32-bit RGBA (8 bits per channel), non-premultiplied form.
// FIXME(pcwalton): Hash the pixel contents so that we don't have to compare every pixel! // FIXME(pcwalton): Hash the pixel contents so that we don't have to compare every pixel!
// TODO(pcwalton): Should the pixels be premultiplied? // TODO(pcwalton): Should the pixels be premultiplied?
// TODO(pcwalton): Color spaces.
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq)]
pub struct Image { pub struct Image {
size: Vector2I, size: Vector2I,
@ -58,9 +60,15 @@ pub struct Image {
pub struct ImageHash(pub u64); pub struct ImageHash(pub u64);
bitflags! { bitflags! {
/// Various flags that determine behavior of a pattern.
pub struct PatternFlags: u8 { pub struct PatternFlags: u8 {
/// If set, the pattern repeats in the X direction. If unset, the base color is used.
const REPEAT_X = 0x01; const REPEAT_X = 0x01;
/// If set, the pattern repeats in the Y direction. If unset, the base color is used.
const REPEAT_Y = 0x02; const REPEAT_Y = 0x02;
/// If set, nearest-neighbor interpolation is used when compositing this pattern (i.e. the
/// image will be pixelated). If unset, bilinear interpolation is used when compositing
/// this pattern (i.e. the image will be smooth).
const NO_SMOOTHING = 0x04; const NO_SMOOTHING = 0x04;
} }
} }
@ -76,26 +84,37 @@ impl Pattern {
} }
} }
/// Creates a new pattern from the given image.
///
/// The transform is initialized to the identity transform. There is no filter.
#[inline] #[inline]
pub fn from_image(image: Image) -> Pattern { pub fn from_image(image: Image) -> Pattern {
Pattern::from_source(PatternSource::Image(image)) Pattern::from_source(PatternSource::Image(image))
} }
/// Creates a new pattern from the given render target with the given size.
///
/// The transform is initialized to the identity transform. There is no filter.
#[inline] #[inline]
pub fn from_render_target(id: RenderTargetId, size: Vector2I) -> Pattern { pub fn from_render_target(id: RenderTargetId, size: Vector2I) -> Pattern {
Pattern::from_source(PatternSource::RenderTarget { id, size }) Pattern::from_source(PatternSource::RenderTarget { id, size })
} }
/// Returns the affine transform applied to this pattern.
#[inline] #[inline]
pub fn transform(&self) -> Transform2F { pub fn transform(&self) -> Transform2F {
self.transform self.transform
} }
/// Applies the given transform to this pattern.
///
/// The transform is applied after any existing transform.
#[inline] #[inline]
pub fn apply_transform(&mut self, transform: Transform2F) { pub fn apply_transform(&mut self, transform: Transform2F) {
self.transform = transform * self.transform; self.transform = transform * self.transform;
} }
/// Returns the underlying pixel size of this pattern, not taking transforms into account.
#[inline] #[inline]
pub fn size(&self) -> Vector2I { pub fn size(&self) -> Vector2I {
match self.source { match self.source {
@ -104,51 +123,72 @@ impl Pattern {
} }
} }
/// Returns the filter attached to this pattern, if any.
#[inline] #[inline]
pub fn filter(&self) -> Option<PatternFilter> { pub fn filter(&self) -> Option<PatternFilter> {
self.filter self.filter
} }
/// Applies a filter to this pattern, replacing any previous filter if any.
#[inline] #[inline]
pub fn set_filter(&mut self, filter: Option<PatternFilter>) { pub fn set_filter(&mut self, filter: Option<PatternFilter>) {
self.filter = filter; self.filter = filter;
} }
/// Returns true if this pattern repeats in the X direction or false if the base color will be
/// used when sampling beyond the coordinates of the image.
#[inline] #[inline]
pub fn repeat_x(&self) -> bool { pub fn repeat_x(&self) -> bool {
self.flags.contains(PatternFlags::REPEAT_X) self.flags.contains(PatternFlags::REPEAT_X)
} }
/// Set to true if the pattern should repeat in the X direction or false if the base color
/// should be used when sampling beyond the coordinates of the image.
#[inline] #[inline]
pub fn set_repeat_x(&mut self, repeat_x: bool) { pub fn set_repeat_x(&mut self, repeat_x: bool) {
self.flags.set(PatternFlags::REPEAT_X, repeat_x); self.flags.set(PatternFlags::REPEAT_X, repeat_x);
} }
/// Returns true if this pattern repeats in the Y direction or false if the base color will be
/// used when sampling beyond the coordinates of the image.
#[inline] #[inline]
pub fn repeat_y(&self) -> bool { pub fn repeat_y(&self) -> bool {
self.flags.contains(PatternFlags::REPEAT_Y) self.flags.contains(PatternFlags::REPEAT_Y)
} }
/// Set to true if the pattern should repeat in the Y direction or false if the base color
/// should be used when sampling beyond the coordinates of the image.
#[inline] #[inline]
pub fn set_repeat_y(&mut self, repeat_y: bool) { pub fn set_repeat_y(&mut self, repeat_y: bool) {
self.flags.set(PatternFlags::REPEAT_Y, repeat_y); self.flags.set(PatternFlags::REPEAT_Y, repeat_y);
} }
/// Returns true if this pattern should use bilinear interpolation (i.e. the image will be
/// smooth) when scaled or false if this pattern should use nearest-neighbor interpolation
/// (i.e. the image will be pixelated).
#[inline] #[inline]
pub fn smoothing_enabled(&self) -> bool { pub fn smoothing_enabled(&self) -> bool {
!self.flags.contains(PatternFlags::NO_SMOOTHING) !self.flags.contains(PatternFlags::NO_SMOOTHING)
} }
/// Set to true if the pattern should use bilinear interpolation (i.e. should be smooth) when
/// scaled or false if this pattern should use nearest-neighbor interpolation (i.e. should be
/// pixelated).
#[inline] #[inline]
pub fn set_smoothing_enabled(&mut self, enable: bool) { pub fn set_smoothing_enabled(&mut self, enable: bool) {
self.flags.set(PatternFlags::NO_SMOOTHING, !enable); self.flags.set(PatternFlags::NO_SMOOTHING, !enable);
} }
/// Returns true if this pattern is obviously fully opaque.
///
/// This is a best-effort quick check, so it might return false even if the image is actually
/// opaque.
#[inline] #[inline]
pub fn is_opaque(&self) -> bool { pub fn is_opaque(&self) -> bool {
self.source.is_opaque() self.source.is_opaque()
} }
/// Returns the underlying source of the pattern.
#[inline] #[inline]
pub fn source(&self) -> &PatternSource { pub fn source(&self) -> &PatternSource {
&self.source &self.source
@ -156,6 +196,8 @@ impl Pattern {
} }
impl Image { impl Image {
/// Creates a new image with the given device pixel size and pixel store, as 32-bit RGBA (8
/// bits per channel), RGBA, linear color space, nonpremultiplied.
#[inline] #[inline]
pub fn new(size: Vector2I, pixels: Arc<Vec<ColorU>>) -> Image { pub fn new(size: Vector2I, pixels: Arc<Vec<ColorU>>) -> Image {
assert_eq!(size.x() as usize * size.y() as usize, pixels.len()); assert_eq!(size.x() as usize * size.y() as usize, pixels.len());
@ -168,6 +210,7 @@ impl Image {
Image { size, pixels, pixels_hash, is_opaque } Image { size, pixels, pixels_hash, is_opaque }
} }
/// A convenience function to create a new image with the given image from the `image` crate.
#[cfg(feature = "pf-image")] #[cfg(feature = "pf-image")]
pub fn from_image_buffer(image_buffer: RgbaImage) -> Image { pub fn from_image_buffer(image_buffer: RgbaImage) -> Image {
let (width, height) = image_buffer.dimensions(); let (width, height) = image_buffer.dimensions();
@ -175,21 +218,29 @@ impl Image {
Image::new(vec2i(width as i32, height as i32), Arc::new(pixels)) Image::new(vec2i(width as i32, height as i32), Arc::new(pixels))
} }
/// Returns the device pixel size of the image.
#[inline] #[inline]
pub fn size(&self) -> Vector2I { pub fn size(&self) -> Vector2I {
self.size self.size
} }
/// Returns the pixel buffer of this image as 32-bit RGBA (8 bits per channel), RGBA, linear
/// color space, nonpremultiplied.
#[inline] #[inline]
pub fn pixels(&self) -> &Arc<Vec<ColorU>> { pub fn pixels(&self) -> &Arc<Vec<ColorU>> {
&self.pixels &self.pixels
} }
/// Returns true if this image is obviously opaque.
///
/// This is a best-guess quick check, and as such it might return false even if the image is
/// fully opaque.
#[inline] #[inline]
pub fn is_opaque(&self) -> bool { pub fn is_opaque(&self) -> bool {
self.is_opaque self.is_opaque
} }
/// Returns a non-cryptographic hash of the image, which should be globally unique.
#[inline] #[inline]
pub fn get_hash(&self) -> ImageHash { pub fn get_hash(&self) -> ImageHash {
let mut hasher = DefaultHasher::new(); let mut hasher = DefaultHasher::new();
@ -199,6 +250,10 @@ impl Image {
} }
impl PatternSource { impl PatternSource {
/// Returns true if this pattern is obviously opaque.
///
/// This is a best-guess quick check, and as such it might return false even if the pattern is
/// fully opaque.
#[inline] #[inline]
pub fn is_opaque(&self) -> bool { pub fn is_opaque(&self) -> bool {
match *self { match *self {

View File

@ -8,7 +8,7 @@
// 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.
//! Line or curve segments, optimized with SIMD. //! Single line or Bézier curve segments, optimized with SIMD.
use pathfinder_geometry::line_segment::LineSegment2F; use pathfinder_geometry::line_segment::LineSegment2F;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
@ -17,15 +17,26 @@ use pathfinder_geometry::vector::{Vector2F, vec2f};
use pathfinder_simd::default::F32x4; use pathfinder_simd::default::F32x4;
use std::f32::consts::SQRT_2; use std::f32::consts::SQRT_2;
/// A single line or Bézier curve segment, with explicit start and end points.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
pub struct Segment { pub struct Segment {
/// The start and end points of the curve.
pub baseline: LineSegment2F, pub baseline: LineSegment2F,
/// The control point or points.
///
/// If this is a line (which can be determined by examining the segment kind), this field is
/// ignored. If this is a quadratic Bézier curve, the start point of this line represents the
/// control point, and the endpoint of this line is ignored. Otherwise, if this is a cubic
/// Bézier curve, both the start and endpoints are used.
pub ctrl: LineSegment2F, pub ctrl: LineSegment2F,
/// The type of segment this is: invalid, line, quadratic, or cubic Bézier curve.
pub kind: SegmentKind, pub kind: SegmentKind,
/// Various flags that describe information about this segment in a path.
pub flags: SegmentFlags, pub flags: SegmentFlags,
} }
impl Segment { impl Segment {
/// Returns an invalid segment.
#[inline] #[inline]
pub fn none() -> Segment { pub fn none() -> Segment {
Segment { Segment {
@ -36,6 +47,7 @@ impl Segment {
} }
} }
/// Returns a segment representing a straight line.
#[inline] #[inline]
pub fn line(line: LineSegment2F) -> Segment { pub fn line(line: LineSegment2F) -> Segment {
Segment { Segment {
@ -226,12 +238,17 @@ impl Segment {
} }
} }
/// The type of line segment this is.
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq)]
#[repr(u8)] #[repr(u8)]
pub enum SegmentKind { pub enum SegmentKind {
/// An invalid segment.
None, None,
/// A line segment.
Line, Line,
/// A quadratic Bézier curve.
Quadratic, Quadratic,
/// A cubic Bézier curve.
Cubic, Cubic,
} }

View File

@ -16,6 +16,8 @@ use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F; use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::transform3d::Perspective; use pathfinder_geometry::transform3d::Perspective;
use pathfinder_geometry::vector::{Vector2F, Vector4F}; use pathfinder_geometry::vector::{Vector2F, Vector4F};
#[allow(deprecated)]
use pathfinder_content::clip::PolygonClipper3D; use pathfinder_content::clip::PolygonClipper3D;
/// A sink for the render commands that scenes build. /// A sink for the render commands that scenes build.
@ -85,6 +87,7 @@ impl Default for RenderTransform {
} }
impl RenderTransform { impl RenderTransform {
#[allow(deprecated)]
fn prepare(&self, bounds: RectF) -> PreparedRenderTransform { fn prepare(&self, bounds: RectF) -> PreparedRenderTransform {
let perspective = match self { let perspective = match self {
RenderTransform::Transform2D(ref transform) => { RenderTransform::Transform2D(ref transform) => {