diff options
author | Matt Brubeck <mbrubeck@limpet.net> | 2016-05-17 09:58:55 -0700 |
---|---|---|
committer | Matt Brubeck <mbrubeck@limpet.net> | 2016-05-20 16:47:01 -0700 |
commit | 5991afafa412cf1b36bf1d375fc8304328042496 (patch) | |
tree | 4cdfc90767127ed19299446ffe882e65fdd9e131 /components/gfx/platform/macos | |
parent | 477258f11ba757cdf48da8d8ed3755e9f2a47f9b (diff) | |
download | servo-5991afafa412cf1b36bf1d375fc8304328042496.tar.gz servo-5991afafa412cf1b36bf1d375fc8304328042496.zip |
Add a fast path for shaping ASCII text
Diffstat (limited to 'components/gfx/platform/macos')
-rw-r--r-- | components/gfx/platform/macos/font.rs | 135 |
1 files changed, 126 insertions, 9 deletions
diff --git a/components/gfx/platform/macos/font.rs b/components/gfx/platform/macos/font.rs index d9b0d60e9d4..4a12b50163a 100644 --- a/components/gfx/platform/macos/font.rs +++ b/components/gfx/platform/macos/font.rs @@ -9,6 +9,7 @@ extern crate core_graphics; extern crate core_text; use app_units::Au; +use byteorder::{BigEndian, ByteOrder}; use core_foundation::base::CFIndex; use core_foundation::data::CFData; use core_foundation::string::UniChar; @@ -17,16 +18,18 @@ use core_graphics::geometry::CGRect; use core_text::font::CTFont; use core_text::font_descriptor::{SymbolicTraitAccessors, TraitAccessors}; use core_text::font_descriptor::{kCTFontDefaultOrientation}; -use font::FontTableTag; -use font::FractionalPixel; -use font::{FontHandleMethods, FontMetrics, FontTableMethods}; +use font::{FontHandleMethods, FontMetrics, FontTableTag, FontTableMethods, FractionalPixel}; +use font::{GPOS, GSUB, KERN}; use platform::font_template::FontTemplateData; use platform::macos::font_context::FontContextHandle; -use std::ptr; +use std::ops::Range; use std::sync::Arc; +use std::{fmt, ptr}; use style::computed_values::{font_stretch, font_weight}; use text::glyph::GlyphId; +const KERN_PAIR_LEN: usize = 6; + pub struct FontTable { data: CFData, } @@ -61,8 +64,107 @@ impl FontTableMethods for FontTable { pub struct FontHandle { font_data: Arc<FontTemplateData>, ctfont: CTFont, + h_kern_subtable: Option<CachedKernTable>, + can_do_fast_shaping: bool, +} + +impl FontHandle { + /// Cache all the data needed for basic horizontal kerning. This is used only as a fallback or + /// fast path (when the GPOS table is missing or unnecessary) so it needn't handle every case. + fn find_h_kern_subtable(&self) -> Option<CachedKernTable> { + let font_table = match self.table_for_tag(KERN) { + Some(table) => table, + None => return None + }; + + let mut result = CachedKernTable { + font_table: font_table, + pair_data_range: 0..0, + px_per_font_unit: 0.0, + }; + + // Look for a subtable with horizontal kerning in format 0. + // https://www.microsoft.com/typography/otspec/kern.htm + const KERN_COVERAGE_HORIZONTAL_FORMAT_0: u16 = 1; + const SUBTABLE_HEADER_LEN: usize = 6; + const FORMAT_0_HEADER_LEN: usize = 8; + { + let table = result.font_table.buffer(); + let version = BigEndian::read_u16(table); + if version != 0 { + return None; + } + let num_subtables = BigEndian::read_u16(&table[2..]); + let mut start = 4; + for _ in 0..num_subtables { + // TODO: Check the subtable version number? + let len = BigEndian::read_u16(&table[start + 2..]) as usize; + let cov = BigEndian::read_u16(&table[start + 4..]); + let end = start + len; + if cov == KERN_COVERAGE_HORIZONTAL_FORMAT_0 { + // Found a matching subtable. + if result.pair_data_range.len() > 0 { + debug!("Found multiple horizontal kern tables. Disable fast path."); + return None; + } + // Read the subtable header. + let subtable_start = start + SUBTABLE_HEADER_LEN; + let n_pairs = BigEndian::read_u16(&table[subtable_start..]) as usize; + let pair_data_start = subtable_start + FORMAT_0_HEADER_LEN; + + result.pair_data_range = pair_data_start..end; + let pt_per_font_unit = self.ctfont.pt_size() as f64 / + self.ctfont.units_per_em() as f64; + result.px_per_font_unit = pt_to_px(pt_per_font_unit); + + debug_assert_eq!(n_pairs * KERN_PAIR_LEN, result.pair_data_range.len()); + } + start = end; + } + } + if result.pair_data_range.len() > 0 { + Some(result) + } else { + None + } + } } +struct CachedKernTable { + font_table: FontTable, + pair_data_range: Range<usize>, + px_per_font_unit: f64, +} + +impl CachedKernTable { + /// Search for a glyph pair in the kern table and return the corresponding value. + fn binary_search(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> Option<i16> { + let pairs = &self.font_table.buffer()[self.pair_data_range.clone()]; + + let query = first_glyph << 16 | second_glyph; + let (mut start, mut end) = (0, pairs.len() / KERN_PAIR_LEN); + while start < end { + let i = (start + end) / 2; + let key = BigEndian::read_u32(&pairs[i * KERN_PAIR_LEN..]); + if key > query { + end = i; + } else if key < query { + start = i + 1; + } else { + return Some(BigEndian::read_i16(&pairs[i * KERN_PAIR_LEN + 4..])); + } + } + None + } +} + +impl fmt::Debug for CachedKernTable { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "CachedKernTable") + } +} + + impl FontHandleMethods for FontHandle { fn new_from_template(_fctx: &FontContextHandle, template: Arc<FontTemplateData>, @@ -74,10 +176,18 @@ impl FontHandleMethods for FontHandle { }; match template.ctfont(size) { Some(ref ctfont) => { - Ok(FontHandle { + let mut handle = FontHandle { font_data: template.clone(), ctfont: ctfont.clone_with_font_size(size), - }) + h_kern_subtable: None, + can_do_fast_shaping: false, + }; + handle.h_kern_subtable = handle.find_h_kern_subtable(); + // TODO (#11310): Implement basic support for GPOS and GSUB. + handle.can_do_fast_shaping = handle.h_kern_subtable.is_some() && + handle.table_for_tag(GPOS).is_none() && + handle.table_for_tag(GSUB).is_none(); + Ok(handle) } None => { Err(()) @@ -155,12 +265,19 @@ impl FontHandleMethods for FontHandle { return Some(glyphs[0] as GlyphId); } - fn glyph_h_kerning(&self, _first_glyph: GlyphId, _second_glyph: GlyphId) - -> FractionalPixel { - // TODO: Implement on mac + fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel { + if let Some(ref table) = self.h_kern_subtable { + if let Some(font_units) = table.binary_search(first_glyph, second_glyph) { + return font_units as f64 * table.px_per_font_unit; + } + } 0.0 } + fn can_do_fast_shaping(&self) -> bool { + self.can_do_fast_shaping + } + fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> { let glyphs = [glyph as CGGlyph]; let advance = self.ctfont.get_advances_for_glyphs(kCTFontDefaultOrientation, |