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);
last_point = endpoint_1;
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),
_ => return Err(PartitionSvgPathsError::UnknownSvgPathCommandType),

View File

@ -8,6 +8,15 @@
// option. This file may not be copied, modified, or distributed
// 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 euclid;
extern crate libc;
@ -64,14 +73,20 @@ mod directwrite;
#[cfg(any(target_os = "linux", feature = "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)]
pub struct FontKey {
id: usize,
}
impl FontKey {
/// Constructs a new opaque font key distinct from all others.
pub fn new() -> FontKey {
static NEXT_FONT_KEY_ID: AtomicUsize = ATOMIC_USIZE_INIT;
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)]
pub struct FontInstanceKey {
id: usize,
}
impl FontInstanceKey {
/// Creates a new opaque font instance key distinct from all others.
#[inline]
pub fn new() -> FontInstanceKey {
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)]
pub struct FontInstance {
/// The opaque font key that this font instance represents.
pub font_key: FontKey,
// The font size is in *device* pixels, not logical pixels.
// 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.
// TODO(gw): Perhaps consider having LogicalAu and DeviceAu
// or something similar to that.
/// The size of the font.
///
/// This is in app units (1/60 pixels) to eliminate floating point error.
pub size: Au,
}
impl FontInstance {
/// Creates a new instance of a font at the given size.
#[inline]
pub fn new(font_key: &FontKey, size: Au) -> FontInstance {
FontInstance {
@ -117,6 +135,7 @@ impl FontInstance {
}
}
/// A subpixel offset, from 0 to `SUBPIXEL_GRANULARITY`.
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
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)]
pub struct GlyphKey {
/// The OpenType glyph index.
pub glyph_index: u32,
/// The subpixel offset, from 0 to `SUBPIXEL_GRANULARITY`.
pub subpixel_offset: SubpixelOffset,
}
impl GlyphKey {
/// Creates a new glyph key from the given index and subpixel offset.
#[inline]
pub fn new(glyph_index: u32, subpixel_offset: SubpixelOffset) -> GlyphKey {
GlyphKey {
@ -150,15 +173,25 @@ impl GlyphKey {
}
}
/// The resolution-independent dimensions of a glyph, in font units.
#[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct GlyphDimensions {
/// The origin of the glyph.
pub origin: Point2D<i32>,
/// The total size of the glyph.
pub size: Size2D<u32>,
/// The advance of the glyph: that is, the distance from this glyph to the next one.
pub advance: f32,
}
/// A bitmap image of a glyph.
pub struct GlyphImage {
/// The dimensions of this image.
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>,
}

View File

@ -118,7 +118,7 @@ impl<I> Iterator for CubicPathCommandApproxStream<I> where I: Iterator<Item = Cu
loop {
if let Some(ref mut approx_curve_iter) = self.approx_curve_iter {
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;

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! Geometry utilities for Bézier curves.
//! Geometry utilities for quadratic Bézier curves.
use euclid::approxeq::ApproxEq;
use euclid::Point2D;
@ -18,13 +18,17 @@ use PathCommand;
use intersection::Intersect;
use line::Line;
/// A quadratic Bézier curve.
#[derive(Clone, Copy, PartialEq, Debug)]
pub struct Curve {
/// The start and end points of the curve, respectively.
pub endpoints: [Point2D<f32>; 2],
/// The control point of the curve.
pub control_point: Point2D<f32>,
}
impl Curve {
/// Creates a new quadratic Bézier curve from the given endpoints and control point.
#[inline]
pub fn new(endpoint_0: &Point2D<f32>, control_point: &Point2D<f32>, endpoint_1: &Point2D<f32>)
-> Curve {
@ -34,12 +38,16 @@ impl Curve {
}
}
/// Returns the curve point at time `t` (0.0 to 1.0).
#[inline]
pub fn sample(&self, t: f32) -> Point2D<f32> {
let (p0, p1, p2) = (&self.endpoints[0], &self.control_point, &self.endpoints[1]);
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]
pub fn subdivide(&self, t: f32) -> (Curve, Curve) {
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))
}
/// 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) {
let (prev_part, next_part) = self.subdivide(self.solve_t_for_x(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]
pub fn to_path_segment(&self) -> PathCommand {
pub fn to_path_command(&self) -> PathCommand {
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>) {
let inflection_point_x = Curve::inflection_point_x(self.endpoints[0].x,
self.control_point.x,
@ -72,9 +88,14 @@ impl Curve {
(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`.
///
/// 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.
///
/// https://math.stackexchange.com/a/311397
/// [Citardauq Formula]: https://math.stackexchange.com/a/311397
pub fn solve_t_for_x(&self, x: f32) -> f32 {
let p0x = self.endpoints[0].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
}
/// 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]
pub fn solve_y_for_x(&self, x: f32) -> f32 {
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]
pub fn baseline(&self) -> Line {
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]
pub fn intersect<T>(&self, other: &T) -> Option<Point2D<f32>> where T: Intersect {
<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)),
(Some(t), None) | (None, Some(t)) => {
let (prev_curve, next_curve) = curve.subdivide(t);
self.queue.push(next_curve.to_path_segment());
Some(prev_curve.to_path_segment())
self.queue.push(next_curve.to_path_command());
Some(prev_curve.to_path_command())
}
(Some(mut t0), Some(mut t1)) => {
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_1, curve_2) = curve_12.subdivide((t1 - t0) / (1.0 - t0));
self.queue.push(curve_1.to_path_segment());
self.queue.push(curve_2.to_path_segment());
self.queue.push(curve_1.to_path_command());
self.queue.push(curve_2.to_path_command());
Some(curve_0.to_path_segment())
Some(curve_0.to_path_command())
}
}
}