diff options
Diffstat (limited to 'src/components/gfx/font_context.rs')
-rw-r--r-- | src/components/gfx/font_context.rs | 286 |
1 files changed, 105 insertions, 181 deletions
diff --git a/src/components/gfx/font_context.rs b/src/components/gfx/font_context.rs index 9437e44299f..a9cccae61fb 100644 --- a/src/components/gfx/font_context.rs +++ b/src/components/gfx/font_context.rs @@ -2,223 +2,147 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, SelectorPlatformIdentifier}; -use font::{SpecifiedFontStyle, UsedFontStyle}; -use font_list::FontList; -use platform::font::FontHandle; +use font::{Font, FontGroup}; +use font::SpecifiedFontStyle; use platform::font_context::FontContextHandle; +use style::computed_values::font_style; -use azure::azure_hl::BackendType; -use std::collections::hashmap::HashMap; -use servo_util::cache::{Cache, LRUCache}; -use servo_util::time::TimeProfilerChan; +use font_cache_task::FontCacheTask; +use font_template::FontTemplateDescriptor; +use platform::font_template::FontTemplateData; +use font::FontHandleMethods; +use platform::font::FontHandle; +use servo_util::cache::HashCache; -use std::rc::Rc; +use std::rc::{Rc, Weak}; use std::cell::RefCell; +use sync::Arc; -/// Information needed to create a font context. -#[deriving(Clone)] -pub struct FontContextInfo { - /// The painting backend we're using. - pub backend: BackendType, +use azure::AzFloat; +use azure::azure_hl::BackendType; +use azure::scaled_font::ScaledFont; + +#[cfg(target_os="linux")] +#[cfg(target_os="android")] +use azure::scaled_font::FontData; - /// Whether we need a font list. - pub needs_font_list: bool, +#[cfg(target_os="linux")] +#[cfg(target_os="android")] +fn create_scaled_font(backend: BackendType, template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont { + ScaledFont::new(backend, FontData(&template.bytes), pt_size as AzFloat) +} - /// A channel up to the time profiler. - pub time_profiler_chan: TimeProfilerChan, +#[cfg(target_os="macos")] +fn create_scaled_font(backend: BackendType, template: &Arc<FontTemplateData>, pt_size: f64) -> ScaledFont { + let cgfont = template.ctfont.copy_to_CGFont(); + ScaledFont::new(backend, &cgfont, pt_size as AzFloat) } -pub trait FontContextHandleMethods { - fn create_font_from_identifier(&self, &str, Option<&UsedFontStyle>) -> Result<FontHandle, ()>; +/// A cached azure font (per render task) that +/// can be shared by multiple text runs. +struct RenderFontCacheEntry { + pt_size: f64, + identifier: String, + font: Rc<RefCell<ScaledFont>>, } +/// The FontContext represents the per-thread/task state necessary for +/// working with fonts. It is the public API used by the layout and +/// render code. It talks directly to the font cache task where +/// required. pub struct FontContext { - pub instance_cache: LRUCache<FontDescriptor, Rc<RefCell<Font>>>, - pub font_list: Option<FontList>, // only needed by layout - pub group_cache: LRUCache<SpecifiedFontStyle, Rc<RefCell<FontGroup>>>, - pub handle: FontContextHandle, - pub backend: BackendType, - pub generic_fonts: HashMap<String,String>, - pub time_profiler_chan: TimeProfilerChan, + platform_handle: FontContextHandle, + font_cache_task: FontCacheTask, + + /// Weak reference as the layout FontContext is persistent. + layout_font_cache: Vec<Weak<RefCell<Font>>>, + + /// Strong reference as the render FontContext is (for now) recycled + /// per frame. TODO: Make this weak when incremental redraw is done. + render_font_cache: Vec<RenderFontCacheEntry>, } impl FontContext { - pub fn new(info: FontContextInfo) -> FontContext { + pub fn new(font_cache_task: FontCacheTask) -> FontContext { let handle = FontContextHandle::new(); - let font_list = if info.needs_font_list { - Some(FontList::new(&handle, info.time_profiler_chan.clone())) - } else { - None - }; - - // TODO: Allow users to specify these. - let mut generic_fonts = HashMap::with_capacity(5); - generic_fonts.insert("serif".to_string(), "Times New Roman".to_string()); - generic_fonts.insert("sans-serif".to_string(), "Arial".to_string()); - generic_fonts.insert("cursive".to_string(), "Apple Chancery".to_string()); - generic_fonts.insert("fantasy".to_string(), "Papyrus".to_string()); - generic_fonts.insert("monospace".to_string(), "Menlo".to_string()); - FontContext { - instance_cache: LRUCache::new(10), - font_list: font_list, - group_cache: LRUCache::new(10), - handle: handle, - backend: info.backend, - generic_fonts: generic_fonts, - time_profiler_chan: info.time_profiler_chan.clone(), + platform_handle: handle, + font_cache_task: font_cache_task, + layout_font_cache: vec!(), + render_font_cache: vec!(), } } - pub fn get_resolved_font_for_style(&mut self, style: &SpecifiedFontStyle) - -> Rc<RefCell<FontGroup>> { - match self.group_cache.find(style) { - Some(fg) => { - debug!("font group cache hit"); - fg - }, - None => { - debug!("font group cache miss"); - let fg = self.create_font_group(style); - self.group_cache.insert(style.clone(), fg.clone()); - fg - } - } - } + /// Create a font for use in layout calculations. + fn create_layout_font(&self, template: Arc<FontTemplateData>, + descriptor: FontTemplateDescriptor, pt_size: f64) -> Font { - pub fn get_font_by_descriptor(&mut self, desc: &FontDescriptor) - -> Result<Rc<RefCell<Font>>, ()> { - match self.instance_cache.find(desc) { - Some(f) => { - debug!("font cache hit"); - Ok(f) - }, - None => { - debug!("font cache miss"); - let result = self.create_font_instance(desc); - match result.clone() { - Ok(ref font) => { - self.instance_cache.insert(desc.clone(), font.clone()); - }, _ => {} - }; - result - } - } - } + let handle: FontHandle = FontHandleMethods::new_from_template(&self.platform_handle, template, Some(pt_size)).unwrap(); + let metrics = handle.get_metrics(); - fn transform_family(&self, family: &String) -> String { - debug!("(transform family) searching for `{:s}`", family.as_slice()); - match self.generic_fonts.find(family) { - None => family.to_string(), - Some(mapped_family) => (*mapped_family).clone() + Font { + handle: handle, + shaper: None, + descriptor: descriptor, + pt_size: pt_size, + metrics: metrics, + shape_cache: HashCache::new(), + glyph_advance_cache: HashCache::new(), } } - fn create_font_group(&mut self, style: &SpecifiedFontStyle) -> Rc<RefCell<FontGroup>> { - let mut fonts = vec!(); + /// Create a group of fonts for use in layout calculations. May return + /// a cached font if this font instance has already been used by + /// this context. + pub fn get_layout_font_group_for_style(&mut self, style: &SpecifiedFontStyle) -> FontGroup { + // Remove all weak pointers that have been dropped. + self.layout_font_cache.retain(|maybe_font| { + maybe_font.upgrade().is_some() + }); - debug!("(create font group) --- starting ---"); + let mut fonts: Vec<Rc<RefCell<Font>>> = vec!(); - // TODO(Issue #193): make iteration over 'font-family' more robust. for family in style.families.iter() { - let transformed_family_name = self.transform_family(family); - debug!("(create font group) transformed family is `{:s}`", transformed_family_name); - let mut found = false; - - let result = match self.font_list { - Some(ref mut fl) => { - let font_in_family = fl.find_font_in_family(&self.handle, &transformed_family_name, style); - match font_in_family { - Some(font_entry) => { - let font_id = - SelectorPlatformIdentifier(font_entry.handle.face_identifier()); - let font_desc = FontDescriptor::new((*style).clone(), font_id); - Some(font_desc) - }, - None => { - None - } - } + let desc = FontTemplateDescriptor::new(style.weight, style.style == font_style::italic); + + // GWTODO: Check on real pages if this is faster as Vec() or HashMap(). + let mut cache_hit = false; + for maybe_cached_font in self.layout_font_cache.iter() { + let cached_font = maybe_cached_font.upgrade().unwrap(); + if cached_font.borrow().descriptor == desc { + fonts.push(cached_font.clone()); + cache_hit = true; + break; } - None => None, - }; - - match result { - Some(ref result) => { - found = true; - let instance = self.get_font_by_descriptor(result); - let _ = instance.map(|font| fonts.push(font.clone())); - }, - _ => {} } - if !found { - debug!("(create font group) didn't find `{:s}`", transformed_family_name); + if !cache_hit { + let font_template = self.font_cache_task.get_font_template(family.clone(), desc.clone()); + let layout_font = Rc::new(RefCell::new(self.create_layout_font(font_template, desc.clone(), style.pt_size))); + self.layout_font_cache.push(layout_font.downgrade()); + fonts.push(layout_font); } } - if fonts.len() == 0 { - let last_resort = FontList::get_last_resort_font_families(); - for family in last_resort.iter() { - let font_desc = match self.font_list { - Some(ref mut font_list) => { - let font_desc = { - let font_entry = font_list.find_font_in_family(&self.handle, family, style); - match font_entry { - Some(v) => { - let font_id = - SelectorPlatformIdentifier(v.handle.face_identifier()); - Some(FontDescriptor::new((*style).clone(), font_id)) - }, - None => { - None - } - } - }; - font_desc - }, - None => { - None - } - }; - - match font_desc { - Some(ref fd) => { - let instance = self.get_font_by_descriptor(fd); - let _ = instance.map(|font| fonts.push(font.clone())); - }, - None => { } - }; - } - } - assert!(fonts.len() > 0, "No matching font(s), are the appropriate fonts installed?"); - // TODO(Issue #179): Split FontStyle into specified and used styles - let used_style = (*style).clone(); - - debug!("(create font group) --- finished ---"); - - Rc::new( - RefCell::new( - FontGroup::new(style.families.clone(), &used_style, fonts))) + FontGroup::new(fonts) } - fn create_font_instance(&self, desc: &FontDescriptor) -> Result<Rc<RefCell<Font>>, ()> { - return match &desc.selector { - // TODO(Issue #174): implement by-platform-name font selectors. - &SelectorPlatformIdentifier(ref identifier) => { - let result_handle = self.handle.create_font_from_identifier(identifier.as_slice(), - Some(&desc.style)); - result_handle.and_then(|handle| { - Ok( - Rc::new( - RefCell::new( - Font::new_from_adopted_handle(self, - handle, - &desc.style, - self.backend)))) - }) + /// Create a render font for use with azure. May return a cached + /// reference if already used by this font context. + pub fn get_render_font_from_template(&mut self, template: &Arc<FontTemplateData>, pt_size: f64, backend: BackendType) -> Rc<RefCell<ScaledFont>> { + for cached_font in self.render_font_cache.iter() { + if cached_font.pt_size == pt_size && + cached_font.identifier == template.identifier { + return cached_font.font.clone(); } - }; + } + + let render_font = Rc::new(RefCell::new(create_scaled_font(backend, template, pt_size))); + self.render_font_cache.push(RenderFontCacheEntry{ + font: render_font.clone(), + pt_size: pt_size, + identifier: template.identifier.clone(), + }); + render_font } } |