From b70394170c0fa9cd4e4be8150121a698a426a069 Mon Sep 17 00:00:00 2001 From: Joe Wilm Date: Fri, 12 Aug 2016 13:38:41 -0500 Subject: Support bold/italic font rendering on macOS This patch adds support for rendering italic fonts and bold fonts. The `font` crate has a couple of new paradigms to support this: font keys and glyph keys. `FontKey` is a lightweight (4 byte) identifier for a font loaded out of the rasterizer. This replaces `FontDesc` for rasterizing glyphs from a loaded font. `FontDesc` had the problem that it contained two strings, and the glyph cache needs to store a copy of the font key for every loaded glyph. `GlyphKey` is now passed to the glyph rasterization method instead of a simple `char`. `GlyphKey` contains information including font, size, and the character. The rasterizer APIs do not define what happens when loading the same font from a `FontDesc` more than once. It is assumed that the application will track the resulting `FontKey` instead of asking the font to be loaded multiple times. --- font/src/darwin/mod.rs | 60 +++++++++++++++++++++++++++++++++++--------------- font/src/lib.rs | 52 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 93 insertions(+), 19 deletions(-) (limited to 'font') diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 31eb1292..4db8dad1 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -43,7 +43,7 @@ use euclid::point::Point2D; use euclid::rect::Rect; use euclid::size::Size2D; -use super::{FontDesc, RasterizedGlyph, Metrics}; +use super::{FontDesc, RasterizedGlyph, Metrics, FontKey, GlyphKey}; pub mod cg_color; use self::cg_color::{CGColorRef, CGColor}; @@ -52,6 +52,10 @@ pub mod byte_order; use self::byte_order::kCGBitmapByteOrder32Host; use self::byte_order::extract_rgb; +use super::Size; + +static FONT_LOAD_ERROR: &'static str = "font specified by FontKey has been loaded"; + /// Font descriptor /// /// The descriptor provides data about a font and supports creating a font. @@ -70,7 +74,7 @@ pub struct Descriptor { /// /// Given a fontdesc, can rasterize fonts. pub struct Rasterizer { - fonts: HashMap, + fonts: HashMap, device_pixel_ratio: f32, } @@ -83,22 +87,35 @@ impl Rasterizer { } } - pub fn metrics(&mut self, desc: &FontDesc, size: f32) -> Metrics { - let scaled_size = self.device_pixel_ratio * size; - self.get_font(desc, scaled_size).unwrap().metrics() + /// Get metrics for font specified by FontKey + /// + /// # Panics + /// + /// If FontKey was not generated by `load_font`, this method will panic. + pub fn metrics(&self, key: FontKey, _size: Size) -> Metrics { + // NOTE size is not needed here since the font loaded already contains + // it. It's part of the API due to platform differences. + let font = self.fonts.get(&key).expect(FONT_LOAD_ERROR); + font.metrics() } - fn get_font(&mut self, desc: &FontDesc, size: f32) -> Option { - if let Some(font) = self.fonts.get(desc) { - return Some(font.clone()); - } + pub fn load_font(&mut self, desc: &FontDesc, size: Size) -> Option { + self.get_font(desc, size) + .map(|font| { + let key = FontKey::next(); + self.fonts.insert(key, font); + key + }) + } + + fn get_font(&mut self, desc: &FontDesc, size: Size) -> Option { let descriptors = descriptors_for_family(&desc.name[..]); for descriptor in descriptors { if descriptor.style_name == desc.style { // Found the font we want - let font = descriptor.to_font(size as _); - self.fonts.insert(desc.to_owned(), font.clone()); + let scaled_size = size.as_f32_pts() as f64 * self.device_pixel_ratio as f64; + let font = descriptor.to_font(scaled_size); return Some(font); } } @@ -106,11 +123,18 @@ impl Rasterizer { None } - pub fn get_glyph(&mut self, desc: &FontDesc, size: f32, c: char) -> RasterizedGlyph { - let scaled_size = self.device_pixel_ratio * size; - let glyph = self.get_font(desc, scaled_size).unwrap().get_glyph(c, scaled_size as _); - - glyph + /// Get rasterized glyph for given glyph key + /// + /// # Panics + /// + /// Panics if the FontKey specified in GlyphKey was not generated from `load_font` + pub fn get_glyph(&mut self, glyph: &GlyphKey) -> RasterizedGlyph { + let scaled_size = self.device_pixel_ratio * glyph.size.as_f32_pts(); + + self.fonts + .get(&glyph.font_key) + .expect(FONT_LOAD_ERROR) + .get_glyph(glyph.c, scaled_size as _) } } @@ -181,8 +205,8 @@ pub fn descriptors_for_family(family: &str) -> Vec { impl Descriptor { /// Create a Font from this descriptor - pub fn to_font(&self, pt_size: f64) -> Font { - let ct_font = ct_new_from_descriptor(&self.ct_descriptor, pt_size); + pub fn to_font(&self, size: f64) -> Font { + let ct_font = ct_new_from_descriptor(&self.ct_descriptor, size); let cg_font = ct_font.copy_to_CGFont(); Font { ct_font: ct_font, diff --git a/font/src/lib.rs b/font/src/lib.rs index a42020d8..74563b1a 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -19,6 +19,7 @@ //! //! CoreText is used on Mac OS. //! FreeType is used on everything that's not Mac OS. +#![feature(integer_atomics)] #[cfg(not(target_os = "macos"))] extern crate fontconfig; @@ -38,6 +39,7 @@ extern crate euclid; extern crate libc; use std::fmt; +use std::sync::atomic::{AtomicU32, ATOMIC_U32_INIT, Ordering}; // If target isn't macos, reexport everything from ft #[cfg(not(target_os = "macos"))] @@ -51,7 +53,7 @@ mod darwin; #[cfg(target_os = "macos")] pub use darwin::*; -#[derive(Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct FontDesc { name: String, style: String, @@ -68,6 +70,54 @@ impl FontDesc { } } +/// Identifier for a Font for use in maps/etc +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub struct FontKey { + token: u32, +} + +impl FontKey { + /// Get next font key for given size + /// + /// The generated key will be globally unique + pub fn next() -> FontKey { + static TOKEN: AtomicU32 = ATOMIC_U32_INIT; + + FontKey { + token: TOKEN.fetch_add(1, Ordering::SeqCst), + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct GlyphKey { + pub c: char, + pub font_key: FontKey, + pub size: Size, +} + +/// Font size stored as integer +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct Size(i32); + +impl Size { + /// Scale factor between font "Size" type and point size + #[inline] + pub fn factor() -> f32 { + 2.0 + } + + /// Create a new `Size` from a f32 size in points + pub fn new(size: f32) -> Size { + Size((size * Size::factor()) as i32) + } + + /// Get the f32 size in points + pub fn as_f32_pts(self) -> f32 { + self.0 as f32 / Size::factor() + } +} + pub struct RasterizedGlyph { pub c: char, pub width: i32, -- cgit v1.2.3-54-g00ecf