Blame servo/components/gfx/font_context.rs

Packit f0b94e
/* This Source Code Form is subject to the terms of the Mozilla Public
Packit f0b94e
 * License, v. 2.0. If a copy of the MPL was not distributed with this
Packit f0b94e
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
Packit f0b94e
Packit f0b94e
use app_units::Au;
Packit f0b94e
use fnv::FnvHasher;
Packit f0b94e
use font::{Font, FontDescriptor, FontGroup, FontHandleMethods, FontRef};
Packit f0b94e
use font_cache_thread::FontTemplateInfo;
Packit f0b94e
use font_template::FontTemplateDescriptor;
Packit f0b94e
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
Packit f0b94e
use platform::font::FontHandle;
Packit f0b94e
pub use platform::font_context::FontContextHandle;
Packit f0b94e
use servo_arc::Arc;
Packit f0b94e
use servo_atoms::Atom;
Packit f0b94e
use std::cell::RefCell;
Packit f0b94e
use std::collections::HashMap;
Packit f0b94e
use std::default::Default;
Packit f0b94e
use std::hash::{BuildHasherDefault, Hash, Hasher};
Packit f0b94e
use std::rc::Rc;
Packit f0b94e
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
Packit f0b94e
use style::computed_values::font_variant_caps::T as FontVariantCaps;
Packit f0b94e
use style::properties::style_structs::Font as FontStyleStruct;
Packit f0b94e
use style::values::computed::font::SingleFontFamily;
Packit f0b94e
use webrender_api;
Packit f0b94e
Packit f0b94e
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8;      // Matches FireFox (see gfxFont.h)
Packit f0b94e
Packit f0b94e
#[derive(Debug)]
Packit f0b94e
struct FontCacheEntry {
Packit f0b94e
    family: Atom,
Packit f0b94e
    font: Option<FontRef>,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl FontCacheEntry {
Packit f0b94e
    fn matches(&self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> bool {
Packit f0b94e
        if self.family != *family.atom() {
Packit f0b94e
            return false
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        if let Some(ref font) = self.font {
Packit f0b94e
            (*font).borrow().descriptor == *descriptor
Packit f0b94e
        } else {
Packit f0b94e
            true
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
#[derive(Debug)]
Packit f0b94e
struct FallbackFontCacheEntry {
Packit f0b94e
    font: FontRef,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl FallbackFontCacheEntry {
Packit f0b94e
    fn matches(&self, descriptor: &FontDescriptor) -> bool {
Packit f0b94e
        self.font.borrow().descriptor == *descriptor
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// An epoch for the font context cache. The cache is flushed if the current epoch does not match
Packit f0b94e
/// this one.
Packit f0b94e
static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT;
Packit f0b94e
Packit f0b94e
pub trait FontSource {
Packit f0b94e
    fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey;
Packit f0b94e
Packit f0b94e
    fn find_font_template(
Packit f0b94e
        &mut self,
Packit f0b94e
        family: SingleFontFamily,
Packit f0b94e
        desc: FontTemplateDescriptor
Packit f0b94e
    ) -> Option<FontTemplateInfo>;
Packit f0b94e
Packit f0b94e
    fn last_resort_font_template(&mut self, desc: FontTemplateDescriptor) -> FontTemplateInfo;
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
/// The FontContext represents the per-thread/thread state necessary for
Packit f0b94e
/// working with fonts. It is the public API used by the layout and
Packit f0b94e
/// paint code. It talks directly to the font cache thread where
Packit f0b94e
/// required.
Packit f0b94e
#[derive(Debug)]
Packit f0b94e
pub struct FontContext<S: FontSource> {
Packit f0b94e
    platform_handle: FontContextHandle,
Packit f0b94e
    font_source: S,
Packit f0b94e
Packit f0b94e
    // TODO: The font context holds a strong ref to the cached fonts
Packit f0b94e
    // so they will never be released. Find out a good time to drop them.
Packit f0b94e
    // See bug https://github.com/servo/servo/issues/3300
Packit f0b94e
    //
Packit f0b94e
    // GWTODO: Check on real pages if this is faster as Vec() or HashMap().
Packit f0b94e
    font_cache: Vec<FontCacheEntry>,
Packit f0b94e
    fallback_font_cache: Vec<FallbackFontCacheEntry>,
Packit f0b94e
Packit f0b94e
    font_group_cache:
Packit f0b94e
        HashMap<FontGroupCacheKey, Rc<RefCell<FontGroup>>, BuildHasherDefault<FnvHasher>>,
Packit f0b94e
Packit f0b94e
    epoch: usize,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<S: FontSource> FontContext<S> {
Packit f0b94e
    pub fn new(font_source: S) -> FontContext<S> {
Packit f0b94e
        let handle = FontContextHandle::new();
Packit f0b94e
        FontContext {
Packit f0b94e
            platform_handle: handle,
Packit f0b94e
            font_source,
Packit f0b94e
            font_cache: vec!(),
Packit f0b94e
            fallback_font_cache: vec!(),
Packit f0b94e
            font_group_cache: HashMap::with_hasher(Default::default()),
Packit f0b94e
            epoch: 0,
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Create a `Font` for use in layout calculations, from a `FontTemplateInfo` returned by the
Packit f0b94e
    /// cache thread (which contains the underlying font data) and a `FontDescriptor` which
Packit f0b94e
    /// contains the styling parameters.
Packit f0b94e
    fn create_font(&mut self, info: FontTemplateInfo, descriptor: FontDescriptor) -> Result<Font, ()> {
Packit f0b94e
        // TODO: (Bug #3463): Currently we only support fake small-caps
Packit f0b94e
        // painting. We should also support true small-caps (where the
Packit f0b94e
        // font supports it) in the future.
Packit f0b94e
        let actual_pt_size = match descriptor.variant {
Packit f0b94e
            FontVariantCaps::SmallCaps => descriptor.pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR),
Packit f0b94e
            FontVariantCaps::Normal => descriptor.pt_size,
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        let handle = FontHandle::new_from_template(&self.platform_handle,
Packit f0b94e
                                                        info.font_template,
Packit f0b94e
                                                        Some(actual_pt_size))?;
Packit f0b94e
Packit f0b94e
        let font_instance_key = self.font_source.get_font_instance(info.font_key, actual_pt_size);
Packit f0b94e
        Ok(Font::new(handle, descriptor.to_owned(), actual_pt_size, font_instance_key))
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    fn expire_font_caches_if_necessary(&mut self) {
Packit f0b94e
        let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst);
Packit f0b94e
        if current_epoch == self.epoch {
Packit f0b94e
            return
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        self.font_cache.clear();
Packit f0b94e
        self.fallback_font_cache.clear();
Packit f0b94e
        self.font_group_cache.clear();
Packit f0b94e
        self.epoch = current_epoch
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`.
Packit f0b94e
    /// Font groups are cached, so subsequent calls with the same `style` will return a reference
Packit f0b94e
    /// to an existing `FontGroup`.
Packit f0b94e
    pub fn font_group(&mut self, style: Arc<FontStyleStruct>) -> Rc<RefCell<FontGroup>> {
Packit f0b94e
        self.expire_font_caches_if_necessary();
Packit f0b94e
Packit f0b94e
        let cache_key = FontGroupCacheKey {
Packit f0b94e
            size: style.font_size.size(),
Packit f0b94e
            style,
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        if let Some(ref font_group) = self.font_group_cache.get(&cache_key) {
Packit f0b94e
            return (*font_group).clone()
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let font_group = Rc::new(RefCell::new(FontGroup::new(&cache_key.style)));
Packit f0b94e
        self.font_group_cache.insert(cache_key, font_group.clone());
Packit f0b94e
        font_group
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Returns a reference to an existing font cache entry matching `descriptor` and `family`, if
Packit f0b94e
    /// there is one.
Packit f0b94e
    fn font_cache_entry(&self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> Option<&FontCacheEntry> {
Packit f0b94e
        self.font_cache.iter()
Packit f0b94e
            .find(|cache_entry| cache_entry.matches(&descriptor, &family))
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Creates a new font cache entry matching `descriptor` and `family`.
Packit f0b94e
    fn create_font_cache_entry(&mut self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> FontCacheEntry {
Packit f0b94e
        let font =
Packit f0b94e
            self.font_source.find_font_template(family.clone(), descriptor.template_descriptor.clone())
Packit f0b94e
                .and_then(|template_info|
Packit f0b94e
                    self.create_font(template_info, descriptor.to_owned()).ok()
Packit f0b94e
                )
Packit f0b94e
                .map(|font| Rc::new(RefCell::new(font)));
Packit f0b94e
Packit f0b94e
        FontCacheEntry { family: family.atom().to_owned(), font }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Returns a font from `family` matching the `descriptor`. Fonts are cached, so repeated calls
Packit f0b94e
    /// will return a reference to the same underlying `Font`.
Packit f0b94e
    pub fn font(&mut self, descriptor: &FontDescriptor, family: &SingleFontFamily) -> Option<FontRef> {
Packit f0b94e
        if let Some(entry) = self.font_cache_entry(descriptor, family) {
Packit f0b94e
            return entry.font.clone()
Packit f0b94e
        }
Packit f0b94e
Packit f0b94e
        let entry = self.create_font_cache_entry(descriptor, family);
Packit f0b94e
        let font = entry.font.clone();
Packit f0b94e
        self.font_cache.push(entry);
Packit f0b94e
        font
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Returns a reference to an existing fallback font cache entry matching `descriptor`, if
Packit f0b94e
    /// there is one.
Packit f0b94e
    fn fallback_font_cache_entry(&self, descriptor: &FontDescriptor) -> Option<&FallbackFontCacheEntry> {
Packit f0b94e
        self.fallback_font_cache.iter()
Packit f0b94e
            .find(|cache_entry| cache_entry.matches(descriptor))
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Creates a new fallback font cache entry matching `descriptor`.
Packit f0b94e
    fn create_fallback_font_cache_entry(&mut self, descriptor: &FontDescriptor) -> Option<FallbackFontCacheEntry> {
Packit f0b94e
        let template_info = self.font_source.last_resort_font_template(descriptor.template_descriptor.clone());
Packit f0b94e
Packit f0b94e
        match self.create_font(template_info, descriptor.to_owned()) {
Packit f0b94e
            Ok(font) =>
Packit f0b94e
                Some(FallbackFontCacheEntry {
Packit f0b94e
                    font: Rc::new(RefCell::new(font))
Packit f0b94e
                }),
Packit f0b94e
Packit f0b94e
            Err(_) => {
Packit f0b94e
                debug!("Failed to create fallback font!");
Packit f0b94e
                None
Packit f0b94e
            }
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
Packit f0b94e
    /// Returns a fallback font matching the `descriptor`. Fonts are cached, so repeated calls will
Packit f0b94e
    /// return a reference to the same underlying `Font`.
Packit f0b94e
    pub fn fallback_font(&mut self, descriptor: &FontDescriptor) -> Option<FontRef> {
Packit f0b94e
        if let Some(cached_entry) = self.fallback_font_cache_entry(descriptor) {
Packit f0b94e
            return Some(cached_entry.font.clone())
Packit f0b94e
        };
Packit f0b94e
Packit f0b94e
        if let Some(entry) = self.create_fallback_font_cache_entry(descriptor) {
Packit f0b94e
            let font = entry.font.clone();
Packit f0b94e
            self.fallback_font_cache.push(entry);
Packit f0b94e
            Some(font)
Packit f0b94e
        } else {
Packit f0b94e
            None
Packit f0b94e
        }
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl<S: FontSource> MallocSizeOf for FontContext<S> {
Packit f0b94e
    fn size_of(&self, ops: &mut MallocSizeOfOps) -> usize {
Packit f0b94e
        // FIXME(njn): Measure other fields eventually.
Packit f0b94e
        self.platform_handle.size_of(ops)
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
#[derive(Debug)]
Packit f0b94e
struct FontGroupCacheKey {
Packit f0b94e
    style: Arc<FontStyleStruct>,
Packit f0b94e
    size: Au,
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl PartialEq for FontGroupCacheKey {
Packit f0b94e
    fn eq(&self, other: &FontGroupCacheKey) -> bool {
Packit f0b94e
        self.style == other.style && self.size == other.size
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
impl Eq for FontGroupCacheKey {}
Packit f0b94e
Packit f0b94e
impl Hash for FontGroupCacheKey {
Packit f0b94e
    fn hash<H>(&self, hasher: &mut H) where H: Hasher {
Packit f0b94e
        self.style.hash.hash(hasher)
Packit f0b94e
    }
Packit f0b94e
}
Packit f0b94e
Packit f0b94e
#[inline]
Packit f0b94e
pub fn invalidate_font_caches() {
Packit f0b94e
    FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);
Packit f0b94e
}