diff --git a/demo/server/src/main.rs b/demo/server/src/main.rs index f7fa4a76..4a2a9505 100644 --- a/demo/server/src/main.rs +++ b/demo/server/src/main.rs @@ -537,7 +537,7 @@ fn partition_svg_paths(request: Json) &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), diff --git a/font-renderer/src/lib.rs b/font-renderer/src/lib.rs index 52318bba..f4b10d71 100644 --- a/font-renderer/src/lib.rs +++ b/font-renderer/src/lib.rs @@ -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 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, + /// The total size of the glyph. pub size: Size2D, + /// 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, } diff --git a/path-utils/src/cubic.rs b/path-utils/src/cubic.rs index ede1be23..f6269b3b 100644 --- a/path-utils/src/cubic.rs +++ b/path-utils/src/cubic.rs @@ -118,7 +118,7 @@ impl Iterator for CubicPathCommandApproxStream where I: Iterator; 2], + /// The control point of the curve. pub control_point: Point2D, } impl Curve { + /// Creates a new quadratic Bézier curve from the given endpoints and control point. #[inline] pub fn new(endpoint_0: &Point2D, control_point: &Point2D, endpoint_1: &Point2D) -> 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 { 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, Option) { 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(&self, other: &T) -> Option> where T: Intersect { ::intersect(self, other) diff --git a/path-utils/src/monotonic.rs b/path-utils/src/monotonic.rs index 9d95e3db..781d89f2 100644 --- a/path-utils/src/monotonic.rs +++ b/path-utils/src/monotonic.rs @@ -66,8 +66,8 @@ impl Iterator for MonotonicPathCommandStream where I: Iterator 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 Iterator for MonotonicPathCommandStream where I: Iterator