Add basic native rasterization for Core Graphics and match the WebRender
`FontContext` API more
This commit is contained in:
parent
f8c950d39a
commit
775ee2b526
|
@ -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)
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>,
|
||||||
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
Loading…
Reference in New Issue