|
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 |
}
|