Add basic native rasterization for Core Graphics and match the WebRender

`FontContext` API more
This commit is contained in:
Patrick Walton 2017-10-26 17:55:17 -07:00
parent f8c950d39a
commit 775ee2b526
6 changed files with 209 additions and 44 deletions

View File

@ -31,7 +31,7 @@ extern crate serde_derive;
use app_units::Au; use app_units::Au;
use euclid::{Point2D, Transform2D}; use euclid::{Point2D, Transform2D};
use lru_cache::LruCache; use lru_cache::LruCache;
use pathfinder_font_renderer::{FontContext, FontInstanceKey, FontKey, GlyphKey}; use pathfinder_font_renderer::{FontContext, FontInstance, FontKey, GlyphKey, SubpixelOffset};
use pathfinder_partitioner::mesh_library::MeshLibrary; use pathfinder_partitioner::mesh_library::MeshLibrary;
use pathfinder_partitioner::partitioner::Partitioner; use pathfinder_partitioner::partitioner::Partitioner;
use pathfinder_path_utils::cubic::CubicCurve; use pathfinder_path_utils::cubic::CubicCurve;
@ -270,7 +270,7 @@ fn partition_font(request: Json<PartitionFontRequest>)
// Parse glyph data. // Parse glyph data.
let font_key = FontKey::new(); let font_key = FontKey::new();
let font_instance_key = FontInstanceKey { let font_instance = FontInstance {
font_key: font_key, font_key: font_key,
size: Au::from_f64_px(request.point_size), size: Au::from_f64_px(request.point_size),
}; };
@ -289,12 +289,12 @@ fn partition_font(request: Json<PartitionFontRequest>)
// Read glyph info. // Read glyph info.
let mut path_buffer = PathBuffer::new(); let mut path_buffer = PathBuffer::new();
let subpath_indices: Vec<_> = request.glyphs.iter().map(|glyph| { let subpath_indices: Vec<_> = request.glyphs.iter().map(|glyph| {
let glyph_key = GlyphKey::new(glyph.id); let glyph_key = GlyphKey::new(glyph.id, SubpixelOffset(0));
let first_subpath_index = path_buffer.subpaths.len(); let first_subpath_index = path_buffer.subpaths.len();
// This might fail; if so, just leave it blank. // This might fail; if so, just leave it blank.
if let Ok(glyph_outline) = font_context.glyph_outline(&font_instance_key, &glyph_key) { if let Ok(glyph_outline) = font_context.glyph_outline(&font_instance, &glyph_key) {
let stream = Transform2DPathStream::new(glyph_outline.into_iter(), &glyph.transform); let stream = Transform2DPathStream::new(glyph_outline.into_iter(), &glyph.transform);
let stream = MonotonicPathCommandStream::new(stream); let stream = MonotonicPathCommandStream::new(stream);
path_buffer.add_stream(stream) path_buffer.add_stream(stream)

View File

@ -12,6 +12,8 @@ app_units = "0.5"
euclid = "0.15" euclid = "0.15"
libc = "0.2" libc = "0.2"
log = "0.3" log = "0.3"
serde = "1.0"
serde_derive = "1.0"
[dependencies.freetype-sys] [dependencies.freetype-sys]
version = "0.6" version = "0.6"
@ -27,11 +29,9 @@ freetype-sys = "0.6"
[target.'cfg(target_os = "macos")'.dependencies.core-graphics] [target.'cfg(target_os = "macos")'.dependencies.core-graphics]
git = "https://github.com/pcwalton/rust-core-graphics.git" git = "https://github.com/pcwalton/rust-core-graphics.git"
branch = "cg-data-provider-fix"
[target.'cfg(target_os = "macos")'.dependencies.core-text] [target.'cfg(target_os = "macos")'.dependencies.core-text]
git = "https://github.com/pcwalton/rust-core-text.git" git = "https://github.com/pcwalton/rust-core-text.git"
branch = "cg-data-provider-fix"
[target.'cfg(target_os = "windows")'.dependencies] [target.'cfg(target_os = "windows")'.dependencies]
dwrite-sys = "0.2" dwrite-sys = "0.2"

View File

@ -8,6 +8,9 @@
// 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.
use core_graphics_sys::base::{kCGImageAlphaNoneSkipFirst, kCGBitmapByteOrder32Little};
use core_graphics_sys::color_space::CGColorSpace;
use core_graphics_sys::context::{CGContext, CGTextDrawingMode};
use core_graphics_sys::data_provider::CGDataProvider; use core_graphics_sys::data_provider::CGDataProvider;
use core_graphics_sys::font::{CGFont, CGGlyph}; use core_graphics_sys::font::{CGFont, CGGlyph};
use core_graphics_sys::geometry::{CG_AFFINE_TRANSFORM_IDENTITY, CGPoint, CGRect}; use core_graphics_sys::geometry::{CG_AFFINE_TRANSFORM_IDENTITY, CGPoint, CGRect};
@ -15,21 +18,36 @@ use core_graphics_sys::geometry::{CGSize, CG_ZERO_POINT};
use core_graphics_sys::path::CGPathElementType; use core_graphics_sys::path::CGPathElementType;
use core_text::font::CTFont; use core_text::font::CTFont;
use core_text; use core_text;
use euclid::{Point2D, Size2D}; use euclid::{Point2D, Size2D, Vector2D};
use pathfinder_path_utils::cubic::{CubicPathCommand, CubicPathCommandApproxStream}; use pathfinder_path_utils::cubic::{CubicPathCommand, CubicPathCommandApproxStream};
use pathfinder_path_utils::PathCommand; use pathfinder_path_utils::PathCommand;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::btree_map::Entry; use std::collections::btree_map::Entry;
use std::sync::Arc; use std::sync::Arc;
use {FontInstanceKey, FontKey, GlyphDimensions, GlyphKey}; use {FontInstance, FontKey, GlyphDimensions, GlyphKey};
const CG_ZERO_RECT: CGRect = CGRect {
origin: CG_ZERO_POINT,
size: CGSize {
width: 0.0,
height: 0.0,
},
};
const CURVE_APPROX_ERROR_BOUND: f32 = 0.1; const CURVE_APPROX_ERROR_BOUND: f32 = 0.1;
// A conservative overestimate of the amount of font dilation that Core Graphics performs, as a
// fraction of ppem.
//
// The actual amount as of High Sierra is 0.0121 in the X direction and 0.015125 in the Y
// direction.
const FONT_DILATION_AMOUNT: f32 = 0.02;
pub type GlyphOutline = Vec<PathCommand>; pub type GlyphOutline = Vec<PathCommand>;
pub struct FontContext { pub struct FontContext {
core_graphics_fonts: BTreeMap<FontKey, CGFont>, core_graphics_fonts: BTreeMap<FontKey, CGFont>,
core_text_fonts: BTreeMap<FontInstanceKey, CTFont>, core_text_fonts: BTreeMap<FontInstance, CTFont>,
} }
impl FontContext { impl FontContext {
@ -66,49 +84,68 @@ impl FontContext {
} }
} }
fn ensure_core_text_font(&mut self, font_instance_key: &FontInstanceKey) fn ensure_core_text_font(&mut self, font_instance: &FontInstance) -> Result<CTFont, ()> {
-> Result<CTFont, ()> { match self.core_text_fonts.entry(*font_instance) {
match self.core_text_fonts.entry(*font_instance_key) {
Entry::Occupied(entry) => Ok((*entry.get()).clone()), Entry::Occupied(entry) => Ok((*entry.get()).clone()),
Entry::Vacant(entry) => { Entry::Vacant(entry) => {
let core_graphics_font = match self.core_graphics_fonts let core_graphics_font = match self.core_graphics_fonts
.get(&font_instance_key.font_key) { .get(&font_instance.font_key) {
None => return Err(()), None => return Err(()),
Some(core_graphics_font) => core_graphics_font, Some(core_graphics_font) => core_graphics_font,
}; };
let core_text_font = try!(font_instance_key.instantiate(&core_graphics_font)); let core_text_font = try!(font_instance.instantiate(&core_graphics_font));
entry.insert(core_text_font.clone()); entry.insert(core_text_font.clone());
Ok(core_text_font) Ok(core_text_font)
} }
} }
} }
pub fn glyph_dimensions(&self, font_instance: &FontInstanceKey, glyph_key: &GlyphKey) pub fn glyph_dimensions(&self, font_instance: &FontInstance, glyph_key: &GlyphKey)
-> Option<GlyphDimensions> { -> Result<GlyphDimensions, ()> {
let core_graphics_font = match self.core_graphics_fonts.get(&font_instance.font_key) { let core_graphics_font = match self.core_graphics_fonts.get(&font_instance.font_key) {
None => return None, None => return Err(()),
Some(core_graphics_font) => core_graphics_font, Some(core_graphics_font) => core_graphics_font,
}; };
let glyph = glyph_key.glyph_index as CGGlyph; let glyph = glyph_key.glyph_index as CGGlyph;
let mut bounding_boxes = [CGRect::new(&CG_ZERO_POINT, &CGSize::new(0.0, 0.0))]; let mut bounding_boxes = [CG_ZERO_RECT];
let mut advances = [0]; let mut advances = [0];
if !core_graphics_font.get_glyph_b_boxes(&[glyph], &mut bounding_boxes) || if !core_graphics_font.get_glyph_b_boxes(&[glyph], &mut bounding_boxes) ||
!core_graphics_font.get_glyph_advances(&[glyph], &mut advances) { !core_graphics_font.get_glyph_advances(&[glyph], &mut advances) {
return None return Err(())
} }
Some(GlyphDimensions { // FIXME(pcwalton): Vertical subpixel offsets.
origin: Point2D::new(bounding_boxes[0].origin.x.round() as i32, let subpixel_offset = Point2D::new(glyph_key.subpixel_offset.into(), 0.0);
bounding_boxes[0].origin.y.round() as i32),
size: Size2D::new(bounding_boxes[0].size.width.round() as u32, // Round out to pixel boundaries.
bounding_boxes[0].size.height.round() as u32), let bounding_box = &bounding_boxes[0];
let mut lower_left = Point2D::new(bounding_box.origin.x.floor() as i32,
bounding_box.origin.y.floor() as i32);
let mut upper_right = Point2D::new((bounding_box.origin.x + bounding_box.size.width +
subpixel_offset.x).ceil() as i32,
(bounding_box.origin.y + bounding_box.size.height +
subpixel_offset.y).ceil() as i32);
// Core Graphics performs font dilation to expand the outlines a bit. As of High Sierra,
// the values seem to be 1.21% in the X direction and 1.5125% in the Y direction. Make sure
// that there's enough room to account for this. We round the values up to 2% to account
// for the possibility that Apple might tweak this later.
let font_dilation_radius = (font_instance.size.to_f32_px() * FONT_DILATION_AMOUNT *
0.5).ceil() as i32;
lower_left += Vector2D::new(-font_dilation_radius, -font_dilation_radius);
upper_right += Vector2D::new(font_dilation_radius, font_dilation_radius);
Ok(GlyphDimensions {
origin: lower_left,
size: Size2D::new((upper_right.x - lower_left.x) as u32,
(upper_right.y - lower_left.y) as u32),
advance: advances[0] as f32, advance: advances[0] as f32,
}) })
} }
pub fn glyph_outline(&mut self, font_instance: &FontInstanceKey, glyph_key: &GlyphKey) pub fn glyph_outline(&mut self, font_instance: &FontInstance, glyph_key: &GlyphKey)
-> Result<GlyphOutline, ()> { -> Result<GlyphOutline, ()> {
let core_text_font = try!(self.ensure_core_text_font(font_instance)); let core_text_font = try!(self.ensure_core_text_font(font_instance));
let path = try!(core_text_font.create_path_for_glyph(glyph_key.glyph_index as CGGlyph, let path = try!(core_text_font.create_path_for_glyph(glyph_key.glyph_index as CGGlyph,
@ -147,10 +184,93 @@ impl FontContext {
Point2D::new(core_graphics_point.x as f32, core_graphics_point.y as f32) Point2D::new(core_graphics_point.x as f32, core_graphics_point.y as f32)
} }
} }
/// Uses the native Core Graphics library to rasterize a glyph on CPU.
pub fn rasterize_glyph_with_native_rasterizer(&self,
font_instance: &FontInstance,
glyph_key: &GlyphKey)
-> Result<GlyphImage, ()> {
let core_graphics_font = match self.core_graphics_fonts.get(&font_instance.font_key) {
None => return Err(()),
Some(core_graphics_font) => core_graphics_font,
};
let dimensions = try!(self.glyph_dimensions(font_instance, glyph_key));
// TODO(pcwalton): Add support for non-subpixel render modes.
let bitmap_context_flags = kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipFirst;
let mut core_graphics_context =
CGContext::create_bitmap_context(None,
dimensions.size.width as usize,
dimensions.size.height as usize,
8,
dimensions.size.width as usize * 4,
&CGColorSpace::create_device_rgb(),
bitmap_context_flags);
// TODO(pcwalton): Add support for non-subpixel render modes.
let (antialias, smooth, bg_color) = (true, true, 1.0);
// Use subpixel positioning. But don't let Core Graphics quantize, because we do that
// ourselves.
core_graphics_context.set_allows_font_subpixel_positioning(true);
core_graphics_context.set_should_subpixel_position_fonts(true);
core_graphics_context.set_allows_font_subpixel_quantization(false);
core_graphics_context.set_should_subpixel_quantize_fonts(false);
// Set up antialiasing flags.
core_graphics_context.set_allows_font_smoothing(smooth);
core_graphics_context.set_should_smooth_fonts(smooth);
core_graphics_context.set_allows_antialiasing(antialias);
core_graphics_context.set_should_antialias(antialias);
// Set up the background.
core_graphics_context.set_rgb_fill_color(bg_color, bg_color, bg_color, bg_color);
core_graphics_context.fill_rect(CGRect {
origin: CG_ZERO_POINT,
size: CGSize {
width: dimensions.size.width as f64,
height: dimensions.size.height as f64,
},
});
// Set up the text color.
core_graphics_context.set_rgb_fill_color(0.0, 0.0, 0.0, 1.0);
core_graphics_context.set_text_drawing_mode(CGTextDrawingMode::CGTextFill);
// Set up the font.
core_graphics_context.set_font(core_graphics_font);
core_graphics_context.set_font_size(font_instance.size.to_f64_px());
// Compute the rasterization origin.
// TODO(pcwalton): Vertical subpixel positioning.
let subpixel_offset = Point2D::new(glyph_key.subpixel_offset.into(), 0.0);
let origin = CGPoint {
x: -dimensions.origin.x as f64 + subpixel_offset.x,
y: -dimensions.origin.y as f64,
};
// Draw the glyph, and extract the pixels.
core_graphics_context.show_glyphs_at_positions(&[glyph_key.glyph_index as CGGlyph],
&[origin]);
let pixels = core_graphics_context.data().to_vec();
// Return the image.
Ok(GlyphImage {
dimensions: dimensions,
pixels: pixels,
})
}
} }
impl FontInstanceKey { impl FontInstance {
fn instantiate(&self, core_graphics_font: &CGFont) -> Result<CTFont, ()> { fn instantiate(&self, core_graphics_font: &CGFont) -> Result<CTFont, ()> {
Ok(core_text::font::new_from_CGFont(core_graphics_font, self.size.to_f64_px())) Ok(core_text::font::new_from_CGFont(core_graphics_font, self.size.to_f64_px()))
} }
} }
pub struct GlyphImage {
pub dimensions: GlyphDimensions,
pub pixels: Vec<u8>,
}

View File

@ -34,7 +34,7 @@ use winapi::{IDWriteFontFileStreamVtbl, IDWriteGdiInterop, IDWriteGeometrySink,
use winapi::{IUnknownVtbl, TRUE, UINT16, UINT32, UINT64, UINT}; use winapi::{IUnknownVtbl, TRUE, UINT16, UINT32, UINT64, UINT};
use self::com::{PathfinderCoclass, PathfinderComObject, PathfinderComPtr}; use self::com::{PathfinderCoclass, PathfinderComObject, PathfinderComPtr};
use {FontInstanceKey, FontKey, GlyphDimensions, GlyphKey}; use {FontInstance, FontKey, GlyphDimensions, GlyphKey};
mod com; mod com;
@ -182,7 +182,7 @@ impl FontContext {
self.dwrite_font_faces.remove(font_key); self.dwrite_font_faces.remove(font_key);
} }
pub fn glyph_dimensions(&self, font_instance: &FontInstanceKey, glyph_key: &GlyphKey) pub fn glyph_dimensions(&self, font_instance: &FontInstance, glyph_key: &GlyphKey)
-> Option<GlyphDimensions> { -> Option<GlyphDimensions> {
unsafe { unsafe {
let font_face = match self.dwrite_font_faces.get(&font_instance.font_key) { let font_face = match self.dwrite_font_faces.get(&font_instance.font_key) {
@ -207,7 +207,7 @@ impl FontContext {
} }
} }
pub fn glyph_outline(&mut self, font_instance: &FontInstanceKey, glyph_key: &GlyphKey) pub fn glyph_outline(&mut self, font_instance: &FontInstance, glyph_key: &GlyphKey)
-> Result<GlyphOutline, ()> { -> Result<GlyphOutline, ()> {
unsafe { unsafe {
let font_face = match self.dwrite_font_faces.get(&font_instance.font_key) { let font_face = match self.dwrite_font_faces.get(&font_instance.font_key) {

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.
use {FontKey, FontInstanceKey, GlyphDimensions, GlyphKey}; use {FontKey, FontInstance, GlyphDimensions, GlyphKey};
use app_units::Au; use app_units::Au;
use euclid::{Point2D, Size2D}; use euclid::{Point2D, Size2D};
use freetype_sys::{FT_BBox, FT_Done_Face, FT_F26Dot6, FT_Face, FT_GLYPH_FORMAT_OUTLINE}; use freetype_sys::{FT_BBox, FT_Done_Face, FT_F26Dot6, FT_Face, FT_GLYPH_FORMAT_OUTLINE};
@ -90,14 +90,14 @@ impl FontContext {
self.faces.remove(font_key); self.faces.remove(font_key);
} }
pub fn glyph_dimensions(&self, font_instance: &FontInstanceKey, glyph_key: &GlyphKey) pub fn glyph_dimensions(&self, font_instance: &FontInstance, glyph_key: &GlyphKey)
-> Option<GlyphDimensions> { -> Option<GlyphDimensions> {
self.load_glyph(font_instance, glyph_key).and_then(|glyph_slot| { self.load_glyph(font_instance, glyph_key).and_then(|glyph_slot| {
self.glyph_dimensions_from_slot(font_instance, glyph_key, glyph_slot) self.glyph_dimensions_from_slot(font_instance, glyph_key, glyph_slot)
}) })
} }
pub fn glyph_outline<'a>(&'a mut self, font_instance: &FontInstanceKey, glyph_key: &GlyphKey) pub fn glyph_outline<'a>(&'a mut self, font_instance: &FontInstance, glyph_key: &GlyphKey)
-> Result<GlyphOutline<'a>, ()> { -> Result<GlyphOutline<'a>, ()> {
self.load_glyph(font_instance, glyph_key).ok_or(()).map(|glyph_slot| { self.load_glyph(font_instance, glyph_key).ok_or(()).map(|glyph_slot| {
unsafe { unsafe {
@ -109,7 +109,7 @@ impl FontContext {
}) })
} }
fn load_glyph(&self, font_instance: &FontInstanceKey, glyph_key: &GlyphKey) fn load_glyph(&self, font_instance: &FontInstance, glyph_key: &GlyphKey)
-> Option<FT_GlyphSlot> { -> Option<FT_GlyphSlot> {
let face = match self.faces.get(&font_instance.font_key) { let face = match self.faces.get(&font_instance.font_key) {
None => return None, None => return None,
@ -134,7 +134,7 @@ impl FontContext {
} }
fn glyph_dimensions_from_slot(&self, fn glyph_dimensions_from_slot(&self,
font_instance: &FontInstanceKey, font_instance: &FontInstance,
glyph_key: &GlyphKey, glyph_key: &GlyphKey,
glyph_slot: FT_GlyphSlot) glyph_slot: FT_GlyphSlot)
-> Option<GlyphDimensions> { -> Option<GlyphDimensions> {
@ -160,7 +160,7 @@ impl FontContext {
// Returns the bounding box for a glyph, accounting for subpixel positioning as appropriate. // Returns the bounding box for a glyph, accounting for subpixel positioning as appropriate.
// //
// TODO(pcwalton): Subpixel positioning. // TODO(pcwalton): Subpixel positioning.
fn bounding_box_from_slot(&self, _: &FontInstanceKey, _: &GlyphKey, glyph_slot: FT_GlyphSlot) fn bounding_box_from_slot(&self, _: &FontInstance, _: &GlyphKey, glyph_slot: FT_GlyphSlot)
-> FT_BBox { -> FT_BBox {
let mut bounding_box: FT_BBox; let mut bounding_box: FT_BBox;
unsafe { unsafe {

View File

@ -12,11 +12,15 @@ extern crate app_units;
extern crate euclid; extern crate euclid;
extern crate libc; extern crate libc;
extern crate pathfinder_path_utils; extern crate pathfinder_path_utils;
extern crate serde;
#[allow(unused_imports)] #[allow(unused_imports)]
#[macro_use] #[macro_use]
extern crate log; extern crate log;
#[macro_use]
extern crate serde_derive;
#[cfg(test)] #[cfg(test)]
extern crate env_logger; extern crate env_logger;
@ -57,7 +61,9 @@ mod directwrite;
#[cfg(any(target_os = "linux", feature = "freetype"))] #[cfg(any(target_os = "linux", feature = "freetype"))]
mod freetype; mod freetype;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] const SUBPIXEL_GRANULARITY: u8 = 4;
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)]
pub struct FontKey { pub struct FontKey {
id: usize, id: usize,
} }
@ -71,39 +77,78 @@ impl FontKey {
} }
} }
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord)] #[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)]
pub struct FontInstanceKey { pub struct FontInstanceKey {
pub font_key: FontKey, id: usize,
pub size: Au,
} }
impl FontInstanceKey { impl FontInstanceKey {
#[inline] #[inline]
pub fn new(font_key: &FontKey, size: Au) -> FontInstanceKey { pub fn new() -> FontInstanceKey {
static NEXT_FONT_INSTANCE_KEY_ID: AtomicUsize = ATOMIC_USIZE_INIT;
FontInstanceKey { FontInstanceKey {
id: NEXT_FONT_INSTANCE_KEY_ID.fetch_add(1, Ordering::Relaxed),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd, Eq, Ord, Serialize, Deserialize)]
pub struct FontInstance {
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.
pub size: Au,
}
impl FontInstance {
#[inline]
pub fn new(font_key: &FontKey, size: Au) -> FontInstance {
FontInstance {
font_key: *font_key, font_key: *font_key,
size: size, size: size,
} }
} }
} }
// FIXME(pcwalton): Subpixel offsets? #[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Copy, PartialEq)] pub struct SubpixelOffset(pub u8);
impl Into<f32> for SubpixelOffset {
#[inline]
fn into(self) -> f32 {
self.0 as f32 / SUBPIXEL_GRANULARITY as f32
}
}
impl Into<f64> for SubpixelOffset {
#[inline]
fn into(self) -> f64 {
self.0 as f64 / SUBPIXEL_GRANULARITY as f64
}
}
#[derive(Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct GlyphKey { pub struct GlyphKey {
pub glyph_index: u32, pub glyph_index: u32,
pub subpixel_offset: SubpixelOffset,
} }
impl GlyphKey { impl GlyphKey {
#[inline] #[inline]
pub fn new(glyph_index: u32) -> GlyphKey { pub fn new(glyph_index: u32, subpixel_offset: SubpixelOffset) -> GlyphKey {
GlyphKey { GlyphKey {
glyph_index: glyph_index, glyph_index: glyph_index,
subpixel_offset: subpixel_offset,
} }
} }
} }
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Debug, PartialEq)] #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
pub struct GlyphDimensions { pub struct GlyphDimensions {
pub origin: Point2D<i32>, pub origin: Point2D<i32>,
pub size: Size2D<u32>, pub size: Size2D<u32>,