Add some documentation

This commit is contained in:
Patrick Walton 2017-02-07 17:19:36 -08:00
parent 0e3aeb782d
commit 9b6e8d1f41
4 changed files with 82 additions and 8 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.
//! Atlases, which hold rendered glyphs on the GPU.
//!
//! TODO(pcwalton): Make the atlas own the outline builder.
use error::GlError; use error::GlError;
use euclid::{Point2D, Rect, Size2D}; use euclid::{Point2D, Rect, Size2D};
use gl::types::{GLenum, GLsizei, GLsizeiptr, GLuint, GLvoid}; use gl::types::{GLenum, GLsizei, GLsizeiptr, GLuint, GLvoid};
@ -18,14 +22,32 @@ use std::mem;
use std::os::raw::c_void; use std::os::raw::c_void;
use std::u16; use std::u16;
/// Places glyphs in an atlas.
///
/// Atlases are composed of vertically-stacked "shelves" of uniform height. No glyphs may cross
/// shelves. Therefore, the shelf height must be tall enough to encompass all of the glyphs you
/// wish to render into the atlas.
///
/// Typically, when using Pathfinder, you first create an atlas builder, place all the glyphs into
/// it, generate the atlas, and then pass that glyph to a rasterizer for rendering on the GPU.
/// Afterward, you can retrieve the positions of each glyph in the atlas for final composition to
/// the screen.
pub struct AtlasBuilder { pub struct AtlasBuilder {
pub rect_packer: RectPacker, rect_packer: RectPacker,
image_descriptors: Vec<ImageDescriptor>, image_descriptors: Vec<ImageDescriptor>,
image_metadata: Vec<ImageMetadata>, image_metadata: Vec<ImageMetadata>,
} }
impl AtlasBuilder { impl AtlasBuilder {
/// FIXME(pcwalton): Including the shelf height here may be a bad API. /// Constructs a new atlas builder with the given width in pixels and shelf height.
///
/// The width can be any value at least as large as all glyphs in the font. It is recommended
/// to keep it fairly large in order to make efficient use of the space: 1024 or 2048 is a good
/// choice on modern GPUs.
///
/// The shelf height should be the maximum of all minimum shelf heights for all fonts you wish
/// to render into the atlas. You can retrive the minimum shelf height for a font with the
/// `Font::shelf_height()` method.
#[inline] #[inline]
pub fn new(available_width: u32, shelf_height: u32) -> AtlasBuilder { pub fn new(available_width: u32, shelf_height: u32) -> AtlasBuilder {
AtlasBuilder { AtlasBuilder {
@ -35,9 +57,17 @@ impl AtlasBuilder {
} }
} }
/// Returns an error if there is no space left for the glyph in the atlas. /// Places a glyph rendered in the outline builder into the atlas.
/// ///
/// FIXME(pcwalton): Support the same glyph drawn at multiple point sizes. /// The outline builder must contain the outlines for the glyph at the given index. Note that
/// this is a glyph *index* in the outline builder, not a glyph *ID*. Glyph indices are
/// assigned sequentially starting from 0 each time you call `OutlineBuilder::add_glyph()`.
///
/// You may not use multiple outline builders in the same
///
/// Returns an error if there is no space left for the glyph.
///
/// TODO(pcwalton): Support the same glyph drawn at multiple point sizes.
pub fn pack_glyph(&mut self, pub fn pack_glyph(&mut self,
outline_builder: &OutlineBuilder, outline_builder: &OutlineBuilder,
glyph_index: u32, glyph_index: u32,
@ -74,6 +104,10 @@ impl AtlasBuilder {
Ok(()) Ok(())
} }
/// Creates an atlas by uploading the atlas info to the GPU.
///
/// The supplied outline builder must be the same as the outline builder passed to
/// `Atlas::pack_glyph()`.
pub fn create_atlas(&mut self, outline_builder: &OutlineBuilder) -> Result<Atlas, GlError> { pub fn create_atlas(&mut self, outline_builder: &OutlineBuilder) -> Result<Atlas, GlError> {
self.image_metadata.sort_by(|a, b| a.glyph_index.cmp(&b.glyph_index)); self.image_metadata.sort_by(|a, b| a.glyph_index.cmp(&b.glyph_index));

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.
//! OpenType fonts.
use byteorder::{BigEndian, ReadBytesExt}; use byteorder::{BigEndian, ReadBytesExt};
use charmap::CodepointRange; use charmap::CodepointRange;
use glyph_range::GlyphRanges; use glyph_range::GlyphRanges;
@ -72,6 +74,10 @@ static SFNT_VERSIONS: [u32; 2] = [
((b't' as u32) << 24) | ((b'r' as u32) << 16) | ((b'u' as u32) << 8) | (b'e' as u32), ((b't' as u32) << 24) | ((b'r' as u32) << 16) | ((b'u' as u32) << 8) | (b'e' as u32),
]; ];
/// A handle to a font backed by a byte buffer containing the contents of the file (`.ttf`,
/// `.otf`), etc.
///
/// For optimum performance, consider using the `memmap` crate to provide the byte buffer.
pub struct Font<'a> { pub struct Font<'a> {
pub bytes: &'a [u8], pub bytes: &'a [u8],
@ -84,12 +90,17 @@ pub struct Font<'a> {
loca: Option<LocaTable<'a>>, loca: Option<LocaTable<'a>>,
} }
#[doc(hidden)]
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct FontTable<'a> { pub struct FontTable<'a> {
pub bytes: &'a [u8], pub bytes: &'a [u8],
} }
impl<'a> Font<'a> { impl<'a> Font<'a> {
/// Creates a new font from a byte buffer containing the contents of a file (`.ttf`, `.otf`,
/// etc.)
///
/// Returns the font on success or an error on failure.
pub fn new<'b>(bytes: &'b [u8]) -> Result<Font<'b>, Error> { pub fn new<'b>(bytes: &'b [u8]) -> Result<Font<'b>, Error> {
// Check magic number. // Check magic number.
let mut reader = bytes; let mut reader = bytes;
@ -123,7 +134,7 @@ impl<'a> Font<'a> {
} }
} }
pub fn from_otf<'b>(bytes: &'b [u8], offset: u32) -> Result<Font<'b>, Error> { fn from_otf<'b>(bytes: &'b [u8], offset: u32) -> Result<Font<'b>, Error> {
let mut reader = bytes; let mut reader = bytes;
try!(reader.jump(offset as usize).map_err(Error::eof)); try!(reader.jump(offset as usize).map_err(Error::eof));
@ -192,7 +203,7 @@ impl<'a> Font<'a> {
} }
/// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format /// https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
pub fn from_dfont<'b>(bytes: &'b [u8]) -> Result<Font<'b>, Error> { fn from_dfont<'b>(bytes: &'b [u8]) -> Result<Font<'b>, Error> {
let mut reader = bytes; let mut reader = bytes;
// Read the Mac resource file header. // Read the Mac resource file header.
@ -258,12 +269,18 @@ impl<'a> Font<'a> {
Font::from_otf(&reader[0..sfnt_size as usize], 0) Font::from_otf(&reader[0..sfnt_size as usize], 0)
} }
/// Returns the glyph IDs that map to the given ranges of Unicode codepoints.
///
/// The returned glyph ranges are in the same order as the codepoints.
#[inline] #[inline]
pub fn glyph_ranges_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange]) pub fn glyph_ranges_for_codepoint_ranges(&self, codepoint_ranges: &[CodepointRange])
-> Result<GlyphRanges, Error> { -> Result<GlyphRanges, Error> {
self.cmap.glyph_ranges_for_codepoint_ranges(codepoint_ranges) self.cmap.glyph_ranges_for_codepoint_ranges(codepoint_ranges)
} }
/// Calls the given callback for each point in the supplied glyph's contour.
///
/// This function is the primary method for accessing a glyph's outline.
#[inline] #[inline]
pub fn for_each_point<F>(&self, glyph_id: u16, callback: F) -> Result<(), Error> pub fn for_each_point<F>(&self, glyph_id: u16, callback: F) -> Result<(), Error>
where F: FnMut(&Point) { where F: FnMut(&Point) {
@ -280,6 +297,7 @@ impl<'a> Font<'a> {
} }
} }
/// Returns the boundaries of the given glyph in font units.
#[inline] #[inline]
pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBoundsI, Error> { pub fn glyph_bounds(&self, glyph_id: u16) -> Result<GlyphBoundsI, Error> {
match self.glyf { match self.glyf {
@ -295,6 +313,7 @@ impl<'a> Font<'a> {
} }
} }
/// Returns the minimum shelf height that an atlas containing glyphs from this font will need.
#[inline] #[inline]
pub fn shelf_height(&self, point_size: f32) -> u32 { pub fn shelf_height(&self, point_size: f32) -> u32 {
// Add 2 to account for the border. // Add 2 to account for the border.
@ -306,11 +325,20 @@ impl<'a> Font<'a> {
.height as u32 + 2 .height as u32 + 2
} }
/// Returns the number of font units per em.
///
/// An em is traditionally the width of the lowercase letter "m". A typical point size of a
/// font is expressed in number of pixels per em. Thus, in order to convert font units to
/// pixels, you can use an expression like `units * font_size / font.units_per_em()`.
#[inline] #[inline]
pub fn units_per_em(&self) -> u16 { pub fn units_per_em(&self) -> u16 {
self.head.units_per_em self.head.units_per_em
} }
/// Returns the horizontal metrics for the glyph with the given ID.
///
/// Horizontal metrics are important for text shaping, as they specify the number of units to
/// advance the pen after typesetting a glyph.
#[inline] #[inline]
pub fn metrics_for_glyph(&self, glyph_id: u16) -> Result<HorizontalMetrics, Error> { pub fn metrics_for_glyph(&self, glyph_id: u16) -> Result<HorizontalMetrics, Error> {
self.hmtx.metrics_for_glyph(&self.hhea, glyph_id) self.hmtx.metrics_for_glyph(&self.hhea, glyph_id)
@ -351,6 +379,7 @@ pub enum Error {
} }
impl Error { impl Error {
#[doc(hidden)]
#[inline] #[inline]
pub fn eof<T>(_: T) -> Error { pub fn eof<T>(_: T) -> Error {
Error::UnexpectedEof Error::UnexpectedEof

View File

@ -38,7 +38,9 @@ impl OutlineBuilder {
} }
} }
pub fn add_glyph(&mut self, font: &Font, glyph_id: u16) -> Result<(), otf::Error> { /// Adds a new glyph to the outline builder. Returns the glyph index, useful for calls to
/// `Atlas::pack_glyph()`.
pub fn add_glyph(&mut self, font: &Font, glyph_id: u16) -> Result<u16, otf::Error> {
let glyph_index = self.descriptors.len() as u16; let glyph_index = self.descriptors.len() as u16;
let mut point_index = self.vertices.len() as u32; let mut point_index = self.vertices.len() as u32;
@ -75,7 +77,7 @@ impl OutlineBuilder {
glyph_id: glyph_id, glyph_id: glyph_id,
}); });
Ok(()) Ok(glyph_index)
} }
/// Returns the glyph rectangle in units. /// Returns the glyph rectangle in units.

View File

@ -18,6 +18,12 @@ use glyph_range::GlyphRanges;
use otf::Font; use otf::Font;
use std::cmp; use std::cmp;
/// Shapes the given Unicode text in the given font, returning the proper position for each glyph.
///
/// See the description of this module for caveats.
///
/// For proper operation, the given `glyph_ranges` must include all the glyphs necessary to render
/// the string.
pub fn shape_text(font: &Font, glyph_ranges: &GlyphRanges, string: &str) -> Vec<GlyphPos> { pub fn shape_text(font: &Font, glyph_ranges: &GlyphRanges, string: &str) -> Vec<GlyphPos> {
string.chars().map(|ch| { string.chars().map(|ch| {
let glyph_id = glyph_ranges.glyph_for(ch as u32).unwrap_or(0); let glyph_id = glyph_ranges.glyph_for(ch as u32).unwrap_or(0);
@ -35,9 +41,12 @@ pub fn shape_text(font: &Font, glyph_ranges: &GlyphRanges, string: &str) -> Vec<
}).collect() }).collect()
} }
/// The position of a glyph after shaping.
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
pub struct GlyphPos { pub struct GlyphPos {
/// The glyph ID to emit.
pub glyph_id: u16, pub glyph_id: u16,
/// The amount to move the cursor forward *after* emitting this glyph.
pub advance: u16, pub advance: u16,
} }