Write more API documentation

This commit is contained in:
Patrick Walton 2018-01-05 15:52:15 -08:00
parent 32c20ec649
commit c68f16f6ba
5 changed files with 77 additions and 17 deletions

View File

@ -537,7 +537,7 @@ fn partition_svg_paths(request: Json<PartitionSvgPathsRequest>)
&endpoint_1); &endpoint_1);
last_point = endpoint_1; last_point = endpoint_1;
stream.extend(cubic.approx_curve(CUBIC_ERROR_TOLERANCE) stream.extend(cubic.approx_curve(CUBIC_ERROR_TOLERANCE)
.map(|curve| curve.to_path_segment())); .map(|curve| curve.to_path_command()));
} }
'Z' => stream.push(PathCommand::ClosePath), 'Z' => stream.push(PathCommand::ClosePath),
_ => return Err(PartitionSvgPathsError::UnknownSvgPathCommandType), _ => return Err(PartitionSvgPathsError::UnknownSvgPathCommandType),

View File

@ -8,6 +8,15 @@
// 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.
//! Reads outlines from OpenType fonts into Pathfinder path formats.
//!
//! Use this crate in conjunction with `pathfinder_partitioner` in order to create meshes for
//! rendering.
//!
//! To reduce dependencies and to match the system as closely as possible, this crate uses the
//! native OS font rendering infrastructure as much as it can. Backends are available for FreeType,
//! Core Graphics/Quartz on macOS, and DirectWrite on Windows.
extern crate app_units; extern crate app_units;
extern crate euclid; extern crate euclid;
extern crate libc; extern crate libc;
@ -64,14 +73,20 @@ mod directwrite;
#[cfg(any(target_os = "linux", feature = "freetype"))] #[cfg(any(target_os = "linux", feature = "freetype"))]
mod freetype; mod freetype;
const SUBPIXEL_GRANULARITY: u8 = 4; /// The number of subpixels that each pixel is divided into for the purposes of subpixel glyph
/// positioning.
///
/// Right now, each glyph is snapped to the nearest quarter-pixel.
pub const SUBPIXEL_GRANULARITY: u8 = 4;
/// An opaque handle that represents a loaded font.
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)]
pub struct FontKey { pub struct FontKey {
id: usize, id: usize,
} }
impl FontKey { impl FontKey {
/// Constructs a new opaque font key distinct from all others.
pub fn new() -> FontKey { pub fn new() -> FontKey {
static NEXT_FONT_KEY_ID: AtomicUsize = ATOMIC_USIZE_INIT; static NEXT_FONT_KEY_ID: AtomicUsize = ATOMIC_USIZE_INIT;
FontKey { FontKey {
@ -80,12 +95,14 @@ impl FontKey {
} }
} }
/// An opaque font that represents a loaded font *at one specific size*.
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)]
pub struct FontInstanceKey { pub struct FontInstanceKey {
id: usize, id: usize,
} }
impl FontInstanceKey { impl FontInstanceKey {
/// Creates a new opaque font instance key distinct from all others.
#[inline] #[inline]
pub fn new() -> FontInstanceKey { pub fn new() -> FontInstanceKey {
static NEXT_FONT_INSTANCE_KEY_ID: AtomicUsize = ATOMIC_USIZE_INIT; static NEXT_FONT_INSTANCE_KEY_ID: AtomicUsize = ATOMIC_USIZE_INIT;
@ -95,19 +112,20 @@ impl FontInstanceKey {
} }
} }
/// A font at one specific size.
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)]
pub struct FontInstance { pub struct FontInstance {
/// The opaque font key that this font instance represents.
pub font_key: FontKey, pub font_key: FontKey,
// The font size is in *device* pixels, not logical pixels. /// The size of the font.
// It is stored as an Au since we need sub-pixel sizes, but ///
// we can't store an f32 due to use of this type as a hash key. /// This is in app units (1/60 pixels) to eliminate floating point error.
// TODO(gw): Perhaps consider having LogicalAu and DeviceAu
// or something similar to that.
pub size: Au, pub size: Au,
} }
impl FontInstance { impl FontInstance {
/// Creates a new instance of a font at the given size.
#[inline] #[inline]
pub fn new(font_key: &FontKey, size: Au) -> FontInstance { pub fn new(font_key: &FontKey, size: Au) -> FontInstance {
FontInstance { FontInstance {
@ -117,6 +135,7 @@ impl FontInstance {
} }
} }
/// A subpixel offset, from 0 to `SUBPIXEL_GRANULARITY`.
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct SubpixelOffset(pub u8); pub struct SubpixelOffset(pub u8);
@ -134,13 +153,17 @@ impl Into<f64> for SubpixelOffset {
} }
} }
/// A handle to the resolution-independent image of a single glyph in a single font.
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct GlyphKey { pub struct GlyphKey {
/// The OpenType glyph index.
pub glyph_index: u32, pub glyph_index: u32,
/// The subpixel offset, from 0 to `SUBPIXEL_GRANULARITY`.
pub subpixel_offset: SubpixelOffset, pub subpixel_offset: SubpixelOffset,
} }
impl GlyphKey { impl GlyphKey {
/// Creates a new glyph key from the given index and subpixel offset.
#[inline] #[inline]
pub fn new(glyph_index: u32, subpixel_offset: SubpixelOffset) -> GlyphKey { pub fn new(glyph_index: u32, subpixel_offset: SubpixelOffset) -> GlyphKey {
GlyphKey { GlyphKey {
@ -150,15 +173,25 @@ impl GlyphKey {
} }
} }
/// The resolution-independent dimensions of a glyph, in font units.
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct GlyphDimensions { pub struct GlyphDimensions {
/// The origin of the glyph.
pub origin: Point2D<i32>, pub origin: Point2D<i32>,
/// The total size of the glyph.
pub size: Size2D<u32>, pub size: Size2D<u32>,
/// The advance of the glyph: that is, the distance from this glyph to the next one.
pub advance: f32, pub advance: f32,
} }
/// A bitmap image of a glyph.
pub struct GlyphImage { pub struct GlyphImage {
/// The dimensions of this image.
pub dimensions: GlyphDimensions, pub dimensions: GlyphDimensions,
/// The actual pixels.
///
/// This is 8 bits per pixel grayscale when grayscale antialiasing is in use and 24 bits per
/// pixel RGB when subpixel antialiasing is in use.
pub pixels: Vec<u8>, pub pixels: Vec<u8>,
} }

View File

@ -118,7 +118,7 @@ impl<I> Iterator for CubicPathCommandApproxStream<I> where I: Iterator<Item = Cu
loop { loop {
if let Some(ref mut approx_curve_iter) = self.approx_curve_iter { if let Some(ref mut approx_curve_iter) = self.approx_curve_iter {
if let Some(curve) = approx_curve_iter.next() { if let Some(curve) = approx_curve_iter.next() {
return Some(curve.to_path_segment()) return Some(curve.to_path_command())
} }
} }
self.approx_curve_iter = None; self.approx_curve_iter = None;

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.
//! Geometry utilities for Bézier curves. //! Geometry utilities for quadratic Bézier curves.
use euclid::approxeq::ApproxEq; use euclid::approxeq::ApproxEq;
use euclid::Point2D; use euclid::Point2D;
@ -18,13 +18,17 @@ use PathCommand;
use intersection::Intersect; use intersection::Intersect;
use line::Line; use line::Line;
/// A quadratic Bézier curve.
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
pub struct Curve { pub struct Curve {
/// The start and end points of the curve, respectively.
pub endpoints: [Point2D<f32>; 2], pub endpoints: [Point2D<f32>; 2],
/// The control point of the curve.
pub control_point: Point2D<f32>, pub control_point: Point2D<f32>,
} }
impl Curve { impl Curve {
/// Creates a new quadratic Bézier curve from the given endpoints and control point.
#[inline] #[inline]
pub fn new(endpoint_0: &Point2D<f32>, control_point: &Point2D<f32>, endpoint_1: &Point2D<f32>) pub fn new(endpoint_0: &Point2D<f32>, control_point: &Point2D<f32>, endpoint_1: &Point2D<f32>)
-> Curve { -> Curve {
@ -34,12 +38,16 @@ impl Curve {
} }
} }
/// Returns the curve point at time `t` (0.0 to 1.0).
#[inline] #[inline]
pub fn sample(&self, t: f32) -> Point2D<f32> { pub fn sample(&self, t: f32) -> Point2D<f32> {
let (p0, p1, p2) = (&self.endpoints[0], &self.control_point, &self.endpoints[1]); let (p0, p1, p2) = (&self.endpoints[0], &self.control_point, &self.endpoints[1]);
Point2D::lerp(&p0.lerp(*p1, t), p1.lerp(*p2, t), t) Point2D::lerp(&p0.lerp(*p1, t), p1.lerp(*p2, t), t)
} }
/// De Casteljau subdivides this curve into two at time `t` (0.0 to 1.0).
///
/// Returns the two resulting curves.
#[inline] #[inline]
pub fn subdivide(&self, t: f32) -> (Curve, Curve) { pub fn subdivide(&self, t: f32) -> (Curve, Curve) {
let (p0, p1, p2) = (&self.endpoints[0], &self.control_point, &self.endpoints[1]); let (p0, p1, p2) = (&self.endpoints[0], &self.control_point, &self.endpoints[1]);
@ -48,6 +56,10 @@ impl Curve {
(Curve::new(p0, &ap1, &ap2bp0), Curve::new(&ap2bp0, &bp1, p2)) (Curve::new(p0, &ap1, &ap2bp0), Curve::new(&ap2bp0, &bp1, p2))
} }
/// Divides this curve into two at the point with *x* coordinate equal to `x`.
///
/// Results are undefined if there is not exactly one point on the curve with *x* coordinate
/// equal to `x`.
pub fn subdivide_at_x(&self, x: f32) -> (Curve, Curve) { pub fn subdivide_at_x(&self, x: f32) -> (Curve, Curve) {
let (prev_part, next_part) = self.subdivide(self.solve_t_for_x(x)); let (prev_part, next_part) = self.subdivide(self.solve_t_for_x(x));
if self.endpoints[0].x <= self.endpoints[1].x { if self.endpoints[0].x <= self.endpoints[1].x {
@ -57,11 +69,15 @@ impl Curve {
} }
} }
/// A convenience method that constructs a `CurveTo` path command from this curve's control
/// point and second endpoint.
#[inline] #[inline]
pub fn to_path_segment(&self) -> PathCommand { pub fn to_path_command(&self) -> PathCommand {
PathCommand::CurveTo(self.control_point, self.endpoints[1]) PathCommand::CurveTo(self.control_point, self.endpoints[1])
} }
/// Returns the times at which the derivative of the curve becomes 0 with respect to *x* and
/// *y* in that order.
pub fn inflection_points(&self) -> (Option<f32>, Option<f32>) { pub fn inflection_points(&self) -> (Option<f32>, Option<f32>) {
let inflection_point_x = Curve::inflection_point_x(self.endpoints[0].x, let inflection_point_x = Curve::inflection_point_x(self.endpoints[0].x,
self.control_point.x, self.control_point.x,
@ -72,9 +88,14 @@ impl Curve {
(inflection_point_x, inflection_point_y) (inflection_point_x, inflection_point_y)
} }
/// Uses the Citardauq Formula to avoid precision problems. /// Returns the time of the single point on this curve with *x* coordinate equal to `x`.
/// ///
/// https://math.stackexchange.com/a/311397 /// Internally, this method uses the [Citardauq Formula] to avoid precision problems.
///
/// If there is not exactly one point with *x* coordinate equal to `x`, the results are
/// undefined.
///
/// [Citardauq Formula]: https://math.stackexchange.com/a/311397
pub fn solve_t_for_x(&self, x: f32) -> f32 { pub fn solve_t_for_x(&self, x: f32) -> f32 {
let p0x = self.endpoints[0].x as f64; let p0x = self.endpoints[0].x as f64;
let p1x = self.control_point.x as f64; let p1x = self.control_point.x as f64;
@ -89,11 +110,16 @@ impl Curve {
t.max(0.0).min(1.0) as f32 t.max(0.0).min(1.0) as f32
} }
/// A convenience method that returns the *y* coordinate of the single point on this curve with
/// *x* coordinate equal to `x`.
///
/// Results are undefined if there is not exactly one point with *x* coordinate equal to `x`.
#[inline] #[inline]
pub fn solve_y_for_x(&self, x: f32) -> f32 { pub fn solve_y_for_x(&self, x: f32) -> f32 {
self.sample(self.solve_t_for_x(x)).y self.sample(self.solve_t_for_x(x)).y
} }
/// Returns a line segment from the start endpoint of this curve to the end of this curve.
#[inline] #[inline]
pub fn baseline(&self) -> Line { pub fn baseline(&self) -> Line {
Line::new(&self.endpoints[0], &self.endpoints[1]) Line::new(&self.endpoints[0], &self.endpoints[1])
@ -112,6 +138,7 @@ impl Curve {
} }
} }
/// Returns the point of intersection of this curve with the given curve.
#[inline] #[inline]
pub fn intersect<T>(&self, other: &T) -> Option<Point2D<f32>> where T: Intersect { pub fn intersect<T>(&self, other: &T) -> Option<Point2D<f32>> where T: Intersect {
<Curve as Intersect>::intersect(self, other) <Curve as Intersect>::intersect(self, other)

View File

@ -66,8 +66,8 @@ impl<I> Iterator for MonotonicPathCommandStream<I> where I: Iterator<Item = Path
(None, None) => Some(PathCommand::CurveTo(control_point, endpoint)), (None, None) => Some(PathCommand::CurveTo(control_point, endpoint)),
(Some(t), None) | (None, Some(t)) => { (Some(t), None) | (None, Some(t)) => {
let (prev_curve, next_curve) = curve.subdivide(t); let (prev_curve, next_curve) = curve.subdivide(t);
self.queue.push(next_curve.to_path_segment()); self.queue.push(next_curve.to_path_command());
Some(prev_curve.to_path_segment()) Some(prev_curve.to_path_command())
} }
(Some(mut t0), Some(mut t1)) => { (Some(mut t0), Some(mut t1)) => {
if t1 < t0 { if t1 < t0 {
@ -76,10 +76,10 @@ impl<I> Iterator for MonotonicPathCommandStream<I> where I: Iterator<Item = Path
let (curve_0, curve_12) = curve.subdivide(t0); let (curve_0, curve_12) = curve.subdivide(t0);
let (curve_1, curve_2) = curve_12.subdivide((t1 - t0) / (1.0 - t0)); let (curve_1, curve_2) = curve_12.subdivide((t1 - t0) / (1.0 - t0));
self.queue.push(curve_1.to_path_segment()); self.queue.push(curve_1.to_path_command());
self.queue.push(curve_2.to_path_segment()); self.queue.push(curve_2.to_path_command());
Some(curve_0.to_path_segment()) Some(curve_0.to_path_command())
} }
} }
} }