Write more API documentation
This commit is contained in:
parent
32c20ec649
commit
c68f16f6ba
|
@ -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),
|
||||||
|
|
|
@ -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>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue