Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lazy load fonts in a FontGroup #20021

Merged
merged 2 commits into from Feb 22, 2018
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -4,11 +4,13 @@

use app_units::Au;
use euclid::{Point2D, Rect, Size2D};
use font_context::{FontContext, FontSource};
use font_template::FontTemplateDescriptor;
use ordered_float::NotNaN;
use platform::font::{FontHandle, FontTable};
use platform::font_context::FontContextHandle;
use platform::font_template::FontTemplateData;
use servo_atoms::Atom;
use smallvec::SmallVec;
use std::borrow::ToOwned;
use std::cell::RefCell;
@@ -18,6 +20,8 @@ use std::str;
use std::sync::Arc;
use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize, Ordering};
use style::computed_values::{font_stretch, font_variant_caps, font_weight};
use style::properties::style_structs::Font as FontStyleStruct;
use style::values::computed::font::SingleFontFamily;
use text::Shaper;
use text::glyph::{ByteIndex, GlyphData, GlyphId, GlyphStore};
use text::shaping::ShaperMethods;
@@ -59,6 +63,9 @@ pub trait FontHandleMethods: Sized {
fn can_do_fast_shaping(&self) -> bool;
fn metrics(&self) -> FontMetrics;
fn table_for_tag(&self, FontTableTag) -> Option<FontTable>;

/// A unique identifier for the font, allowing comparison.
fn identifier(&self) -> Atom;
}

// Used to abstract over the shaper's choice of fixed int representation.
@@ -100,13 +107,32 @@ pub struct FontMetrics {
pub line_gap: Au,
}

/// `FontDescriptor` describes the parameters of a `Font`. It represents rendering a given font
/// template at a particular size, with a particular font-variant-caps applied, etc. This contrasts
/// with `FontTemplateDescriptor` in that the latter represents only the parameters inherent in the
/// font data (weight, stretch, etc.).
#[derive(Clone, Debug, PartialEq)]
pub struct FontDescriptor {
pub template_descriptor: FontTemplateDescriptor,
pub variant: font_variant_caps::T,
pub pt_size: Au,
}

impl<'a> From<&'a FontStyleStruct> for FontDescriptor {
fn from(style: &'a FontStyleStruct) -> Self {
FontDescriptor {
template_descriptor: FontTemplateDescriptor::from(style),
variant: style.font_variant_caps,
pt_size: style.font_size.size(),
}
}
}

#[derive(Debug)]
pub struct Font {
pub handle: FontHandle,
pub metrics: FontMetrics,
pub variant: font_variant_caps::T,
pub descriptor: FontTemplateDescriptor,
pub requested_pt_size: Au,
pub descriptor: FontDescriptor,
pub actual_pt_size: Au,
shaper: Option<Shaper>,
shape_cache: RefCell<HashMap<ShapeCacheEntry, Arc<GlyphStore>>>,
@@ -116,25 +142,27 @@ pub struct Font {

impl Font {
pub fn new(handle: FontHandle,
variant: font_variant_caps::T,
descriptor: FontTemplateDescriptor,
requested_pt_size: Au,
descriptor: FontDescriptor,
actual_pt_size: Au,
font_key: webrender_api::FontInstanceKey) -> Font {
let metrics = handle.metrics();

Font {
handle: handle,
shaper: None,
variant: variant,
descriptor: descriptor,
requested_pt_size: requested_pt_size,
actual_pt_size: actual_pt_size,
metrics: metrics,
descriptor,
actual_pt_size,
metrics,
shape_cache: RefCell::new(HashMap::new()),
glyph_advance_cache: RefCell::new(HashMap::new()),
font_key: font_key,
font_key,
}
}

/// A unique identifier for the font, allowing comparison.
pub fn identifier(&self) -> Atom {
self.handle.identifier()
}
}

bitflags! {
@@ -260,13 +288,17 @@ impl Font {

#[inline]
pub fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
let codepoint = match self.variant {
let codepoint = match self.descriptor.variant {
font_variant_caps::T::SmallCaps => codepoint.to_uppercase().next().unwrap(), //FIXME: #5938
font_variant_caps::T::Normal => codepoint,
};
self.handle.glyph_index(codepoint)
}

pub fn has_glyph_for(&self, codepoint: char) -> bool {
self.glyph_index(codepoint).is_some()
}

pub fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId)
-> FractionalPixel {
self.handle.glyph_h_kerning(first_glyph, second_glyph)
@@ -282,17 +314,102 @@ impl Font {
}
}

pub type FontRef = Rc<RefCell<Font>>;

/// A `FontGroup` is a prioritised list of fonts for a given set of font styles. It is used by
/// `TextRun` to decide which font to render a character with. If none of the fonts listed in the
/// styles are suitable, a fallback font may be used.
#[derive(Debug)]
pub struct FontGroup {
pub fonts: SmallVec<[Rc<RefCell<Font>>; 8]>,
descriptor: FontDescriptor,
families: SmallVec<[FontGroupFamily; 8]>,
}

impl FontGroup {
pub fn new(fonts: SmallVec<[Rc<RefCell<Font>>; 8]>) -> FontGroup {
FontGroup {
fonts: fonts,
pub fn new(style: &FontStyleStruct) -> FontGroup {
let descriptor = FontDescriptor::from(style);

let families =
style.font_family.0.iter()
.map(|family| FontGroupFamily::new(descriptor.clone(), family.clone()))
.collect();

FontGroup { descriptor, families }
}

/// Finds the first font, or else the first fallback font, which contains a glyph for
/// `codepoint`. If no such font is found, returns the first available font or fallback font
/// (which will cause a "glyph not found" character to be rendered). If no font at all can be
/// found, returns None.
pub fn find_by_codepoint<S: FontSource>(
&mut self,
mut font_context: &mut FontContext<S>,
codepoint: char
) -> Option<FontRef> {
self.find(&mut font_context, |font| font.borrow().has_glyph_for(codepoint))
.or_else(|| self.first(&mut font_context))
}

pub fn first<S: FontSource>(
&mut self,
mut font_context: &mut FontContext<S>
) -> Option<FontRef> {
self.find(&mut font_context, |_| true)
}

/// Find a font which returns true for `predicate`. This method mutates because we may need to
/// load new font data in the process of finding a suitable font.
fn find<S, P>(
&mut self,
mut font_context: &mut FontContext<S>,
mut predicate: P
) -> Option<FontRef>
where
S: FontSource,
P: FnMut(&FontRef) -> bool
{
self.families.iter_mut()
.filter_map(|family| family.font(&mut font_context))
.find(|f| predicate(f))
.or_else(|| {
font_context.fallback_font(&self.descriptor)
.into_iter().find(predicate)
})
}
}

/// A `FontGroupFamily` is a single font family in a `FontGroup`. It corresponds to one of the
/// families listed in the `font-family` CSS property. The corresponding font data is lazy-loaded,
/// only if actually needed.
#[derive(Debug)]
struct FontGroupFamily {
descriptor: FontDescriptor,
family: SingleFontFamily,
loaded: bool,
font: Option<FontRef>,
}

impl FontGroupFamily {
fn new(descriptor: FontDescriptor, family: SingleFontFamily) -> FontGroupFamily {
FontGroupFamily {
descriptor,
family,
loaded: false,
font: None,
}
}

/// Returns the font within this family which matches the style. We'll fetch the data from the
/// `FontContext` the first time this method is called, and return a cached reference on
/// subsequent calls.
fn font<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> {
if !self.loaded {
self.font = font_context.font(&self.descriptor, &self.family);
self.loaded = true;
}

self.font.clone()
}
}

pub struct RunMetrics {
@@ -3,6 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use app_units::Au;
use font_context::FontSource;
use font_template::{FontTemplate, FontTemplateDescriptor};
use fontsan;
use ipc_channel::ipc::{self, IpcReceiver, IpcSender};
@@ -30,7 +31,7 @@ use style::values::computed::font::{SingleFontFamily, FamilyName};
use webrender_api;

/// A list of font templates that make up a given font family.
struct FontTemplates {
pub struct FontTemplates {
templates: Vec<FontTemplate>,
}

@@ -41,14 +42,14 @@ pub struct FontTemplateInfo {
}

impl FontTemplates {
fn new() -> FontTemplates {
pub fn new() -> FontTemplates {
FontTemplates {
templates: vec!(),
}
}

/// Find a font in this family that matches a given descriptor.
fn find_font_for_style(&mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle)
pub fn find_font_for_style(&mut self, desc: &FontTemplateDescriptor, fctx: &FontContextHandle)
-> Option<Arc<FontTemplateData>> {
// TODO(Issue #189): optimize lookup for
// regular/bold/italic/bolditalic with fixed offsets and a
@@ -89,7 +90,7 @@ impl FontTemplates {
None
}

fn add_template(&mut self, identifier: Atom, maybe_data: Option<Vec<u8>>) {
pub fn add_template(&mut self, identifier: Atom, maybe_data: Option<Vec<u8>>) {
for template in &self.templates {
if *template.identifier() == identifier {
return;
@@ -414,8 +415,8 @@ impl FontCache {
}
}

/// The public interface to the font cache thread, used exclusively by
/// the per-thread/thread FontContext structures.
/// The public interface to the font cache thread, used by per-thread `FontContext` instances (via
/// the `FontSource` trait), and also by layout.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct FontCacheThread {
chan: IpcSender<Command>,
@@ -453,7 +454,31 @@ impl FontCacheThread {
}
}

pub fn find_font_template(&self, family: SingleFontFamily, desc: FontTemplateDescriptor)
pub fn add_web_font(&self, family: FamilyName, sources: EffectiveSources, sender: IpcSender<()>) {
self.chan.send(Command::AddWebFont(LowercaseString::new(&family.name), sources, sender)).unwrap();
}

pub fn exit(&self) {
let (response_chan, response_port) = ipc::channel().unwrap();
self.chan.send(Command::Exit(response_chan)).expect("Couldn't send FontCacheThread exit message");
response_port.recv().expect("Couldn't receive FontCacheThread reply");
}
}

impl FontSource for FontCacheThread {
fn get_font_instance(&mut self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
self.chan.send(Command::GetFontInstance(key, size, response_chan))
.expect("failed to send message to font cache thread");

let instance_key = response_port.recv()
.expect("failed to receive response to font request");

instance_key
}

fn find_font_template(&mut self, family: SingleFontFamily, desc: FontTemplateDescriptor)
-> Option<FontTemplateInfo> {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
@@ -470,7 +495,7 @@ impl FontCacheThread {
}
}

pub fn last_resort_font_template(&self, desc: FontTemplateDescriptor)
fn last_resort_font_template(&mut self, desc: FontTemplateDescriptor)
-> FontTemplateInfo {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
@@ -486,31 +511,8 @@ impl FontCacheThread {
}
}
}

pub fn add_web_font(&self, family: FamilyName, sources: EffectiveSources, sender: IpcSender<()>) {
self.chan.send(Command::AddWebFont(LowercaseString::new(&family.name), sources, sender)).unwrap();
}

pub fn get_font_instance(&self, key: webrender_api::FontKey, size: Au) -> webrender_api::FontInstanceKey {
let (response_chan, response_port) =
ipc::channel().expect("failed to create IPC channel");
self.chan.send(Command::GetFontInstance(key, size, response_chan))
.expect("failed to send message to font cache thread");

let instance_key = response_port.recv()
.expect("failed to receive response to font request");

instance_key
}

pub fn exit(&self) {
let (response_chan, response_port) = ipc::channel().unwrap();
self.chan.send(Command::Exit(response_chan)).expect("Couldn't send FontCacheThread exit message");
response_port.recv().expect("Couldn't receive FontCacheThread reply");
}
}


#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct LowercaseString {
inner: String,
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.