зеркало из https://github.com/mozilla/gecko-dev.git
264 строки
10 KiB
Rust
264 строки
10 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* 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 app_units::Au;
|
|
use fnv::FnvHasher;
|
|
use font::{Font, FontGroup, FontHandleMethods};
|
|
use font_cache_thread::FontCacheThread;
|
|
use font_template::FontTemplateDescriptor;
|
|
use heapsize::HeapSizeOf;
|
|
use platform::font::FontHandle;
|
|
use platform::font_context::FontContextHandle;
|
|
use platform::font_template::FontTemplateData;
|
|
use smallvec::SmallVec;
|
|
use std::cell::RefCell;
|
|
use std::collections::HashMap;
|
|
use std::default::Default;
|
|
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
|
use std::rc::Rc;
|
|
use std::sync::Arc;
|
|
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};
|
|
use style::computed_values::{font_style, font_variant};
|
|
use style::properties::style_structs;
|
|
use webrender_traits;
|
|
|
|
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
|
|
|
|
#[derive(Debug)]
|
|
struct LayoutFontCacheEntry {
|
|
family: String,
|
|
font: Option<Rc<RefCell<Font>>>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct FallbackFontCacheEntry {
|
|
font: Rc<RefCell<Font>>,
|
|
}
|
|
|
|
/// An epoch for the font context cache. The cache is flushed if the current epoch does not match
|
|
/// this one.
|
|
static FONT_CACHE_EPOCH: AtomicUsize = ATOMIC_USIZE_INIT;
|
|
|
|
/// The FontContext represents the per-thread/thread state necessary for
|
|
/// working with fonts. It is the public API used by the layout and
|
|
/// paint code. It talks directly to the font cache thread where
|
|
/// required.
|
|
#[derive(Debug)]
|
|
pub struct FontContext {
|
|
platform_handle: FontContextHandle,
|
|
font_cache_thread: FontCacheThread,
|
|
|
|
/// TODO: See bug https://github.com/servo/servo/issues/3300.
|
|
layout_font_cache: Vec<LayoutFontCacheEntry>,
|
|
fallback_font_cache: Vec<FallbackFontCacheEntry>,
|
|
|
|
layout_font_group_cache:
|
|
HashMap<LayoutFontGroupCacheKey, Rc<FontGroup>, BuildHasherDefault<FnvHasher>>,
|
|
|
|
epoch: usize,
|
|
}
|
|
|
|
impl FontContext {
|
|
pub fn new(font_cache_thread: FontCacheThread) -> FontContext {
|
|
let handle = FontContextHandle::new();
|
|
FontContext {
|
|
platform_handle: handle,
|
|
font_cache_thread: font_cache_thread,
|
|
layout_font_cache: vec!(),
|
|
fallback_font_cache: vec!(),
|
|
layout_font_group_cache: HashMap::with_hasher(Default::default()),
|
|
epoch: 0,
|
|
}
|
|
}
|
|
|
|
/// Create a font for use in layout calculations.
|
|
fn create_layout_font(&self,
|
|
template: Arc<FontTemplateData>,
|
|
descriptor: FontTemplateDescriptor,
|
|
pt_size: Au,
|
|
variant: font_variant::T,
|
|
font_key: webrender_traits::FontKey) -> Result<Font, ()> {
|
|
// TODO: (Bug #3463): Currently we only support fake small-caps
|
|
// painting. We should also support true small-caps (where the
|
|
// font supports it) in the future.
|
|
let actual_pt_size = match variant {
|
|
font_variant::T::small_caps => pt_size.scale_by(SMALL_CAPS_SCALE_FACTOR),
|
|
font_variant::T::normal => pt_size,
|
|
};
|
|
|
|
let handle = try!(FontHandle::new_from_template(&self.platform_handle,
|
|
template,
|
|
Some(actual_pt_size)));
|
|
|
|
Ok(Font::new(handle, variant, descriptor, pt_size, actual_pt_size, font_key))
|
|
}
|
|
|
|
fn expire_font_caches_if_necessary(&mut self) {
|
|
let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst);
|
|
if current_epoch == self.epoch {
|
|
return
|
|
}
|
|
|
|
self.layout_font_cache.clear();
|
|
self.fallback_font_cache.clear();
|
|
self.layout_font_group_cache.clear();
|
|
self.epoch = current_epoch
|
|
}
|
|
|
|
/// 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 layout_font_group_for_style(&mut self, style: Arc<style_structs::Font>)
|
|
-> Rc<FontGroup> {
|
|
self.expire_font_caches_if_necessary();
|
|
|
|
let layout_font_group_cache_key = LayoutFontGroupCacheKey {
|
|
pointer: style.clone(),
|
|
size: style.font_size,
|
|
};
|
|
if let Some(ref cached_font_group) = self.layout_font_group_cache.get(
|
|
&layout_font_group_cache_key) {
|
|
return (*cached_font_group).clone()
|
|
}
|
|
|
|
// TODO: The font context holds a strong ref to the cached fonts
|
|
// so they will never be released. Find out a good time to drop them.
|
|
|
|
let desc = FontTemplateDescriptor::new(style.font_weight,
|
|
style.font_stretch,
|
|
style.font_style == font_style::T::italic ||
|
|
style.font_style == font_style::T::oblique);
|
|
|
|
let mut fonts: SmallVec<[Rc<RefCell<Font>>; 8]> = SmallVec::new();
|
|
|
|
for family in &style.font_family.0 {
|
|
// GWTODO: Check on real pages if this is faster as Vec() or HashMap().
|
|
let mut cache_hit = false;
|
|
for cached_font_entry in &self.layout_font_cache {
|
|
if cached_font_entry.family == family.name() {
|
|
match cached_font_entry.font {
|
|
None => {
|
|
cache_hit = true;
|
|
break;
|
|
}
|
|
Some(ref cached_font_ref) => {
|
|
let cached_font = (*cached_font_ref).borrow();
|
|
if cached_font.descriptor == desc &&
|
|
cached_font.requested_pt_size == style.font_size &&
|
|
cached_font.variant == style.font_variant {
|
|
fonts.push((*cached_font_ref).clone());
|
|
cache_hit = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if !cache_hit {
|
|
let template_info = self.font_cache_thread.find_font_template(family.clone(),
|
|
desc.clone());
|
|
match template_info {
|
|
Some(template_info) => {
|
|
let layout_font = self.create_layout_font(template_info.font_template,
|
|
desc.clone(),
|
|
style.font_size,
|
|
style.font_variant,
|
|
template_info.font_key
|
|
.expect("No font key present!"));
|
|
let font = match layout_font {
|
|
Ok(layout_font) => {
|
|
let layout_font = Rc::new(RefCell::new(layout_font));
|
|
fonts.push(layout_font.clone());
|
|
|
|
Some(layout_font)
|
|
}
|
|
Err(_) => None
|
|
};
|
|
|
|
self.layout_font_cache.push(LayoutFontCacheEntry {
|
|
family: family.name().to_owned(),
|
|
font: font
|
|
});
|
|
}
|
|
None => {
|
|
self.layout_font_cache.push(LayoutFontCacheEntry {
|
|
family: family.name().to_owned(),
|
|
font: None,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Add a last resort font as a fallback option.
|
|
let mut cache_hit = false;
|
|
for cached_font_entry in &self.fallback_font_cache {
|
|
let cached_font = cached_font_entry.font.borrow();
|
|
if cached_font.descriptor == desc &&
|
|
cached_font.requested_pt_size == style.font_size &&
|
|
cached_font.variant == style.font_variant {
|
|
fonts.push(cached_font_entry.font.clone());
|
|
cache_hit = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if !cache_hit {
|
|
let template_info = self.font_cache_thread.last_resort_font_template(desc.clone());
|
|
let layout_font = self.create_layout_font(template_info.font_template,
|
|
desc.clone(),
|
|
style.font_size,
|
|
style.font_variant,
|
|
template_info.font_key.expect("No font key present!"));
|
|
match layout_font {
|
|
Ok(layout_font) => {
|
|
let layout_font = Rc::new(RefCell::new(layout_font));
|
|
self.fallback_font_cache.push(FallbackFontCacheEntry {
|
|
font: layout_font.clone(),
|
|
});
|
|
fonts.push(layout_font);
|
|
}
|
|
Err(_) => debug!("Failed to create fallback layout font!")
|
|
}
|
|
}
|
|
|
|
let font_group = Rc::new(FontGroup::new(fonts));
|
|
self.layout_font_group_cache.insert(layout_font_group_cache_key, font_group.clone());
|
|
font_group
|
|
}
|
|
}
|
|
|
|
impl HeapSizeOf for FontContext {
|
|
fn heap_size_of_children(&self) -> usize {
|
|
// FIXME(njn): Measure other fields eventually.
|
|
self.platform_handle.heap_size_of_children()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct LayoutFontGroupCacheKey {
|
|
pointer: Arc<style_structs::Font>,
|
|
size: Au,
|
|
}
|
|
|
|
impl PartialEq for LayoutFontGroupCacheKey {
|
|
fn eq(&self, other: &LayoutFontGroupCacheKey) -> bool {
|
|
self.pointer == other.pointer && self.size == other.size
|
|
}
|
|
}
|
|
|
|
impl Eq for LayoutFontGroupCacheKey {}
|
|
|
|
impl Hash for LayoutFontGroupCacheKey {
|
|
fn hash<H>(&self, hasher: &mut H) where H: Hasher {
|
|
self.pointer.hash.hash(hasher)
|
|
}
|
|
}
|
|
|
|
#[inline]
|
|
pub fn invalidate_font_caches() {
|
|
FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);
|
|
}
|