mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Auto merge of #20021 - jonleighton:lazy-font-group, r=mbrubeck,glennw
Lazy load fonts in a FontGroup The first commit message explains this so I'll just copy it here: --- This is a step towards fixing #17267. To fix that, we need to be able to try various different fallback fonts in turn, which would become unweildy with the prior eager-loading strategy. Prior to this change, FontGroup loaded up all Font instances, including the fallback font, before any of them were checked for the presence of the glyphs we're trying to render. So for the following CSS: font-family: Helvetica, Arial; The FontGroup would contain a Font instance for Helvetica, and a Font instance for Arial, and a Font instance for the fallback font. It may be that Helvetica contains glyphs for every character in the document, and therefore Arial and the fallback font are not needed at all. This change makes the strategy lazy, so that we'll only create a Font for Arial if we cannot find a glyph within Helvetica. I've also substantially refactored the existing code in the process and added some documentation along the way. --- I've added some tests in the second commit, but it required quite a bit of gymnastics to make it possible to write such a test. I'm not sure if the added complexity to the production code is worth it? On the other hand, having this infrastructure in place may be useful for testing future changes in this area, and also possibly brings us a step closer to extracting a library as discussed in #4901. (What I mean by that is: it reduces coupling between `FontCacheThread` and `FontContext` -- the latter would have a place in such a library, the former wouldn't.) <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/20021) <!-- Reviewable:end -->
This commit is contained in:
commit
f48dce120d
65 changed files with 708 additions and 255 deletions
|
@ -27,10 +27,12 @@ use std::thread;
|
|||
use style::context::RegisteredSpeculativePainter;
|
||||
use style::context::SharedStyleContext;
|
||||
|
||||
thread_local!(static FONT_CONTEXT_KEY: RefCell<Option<FontContext>> = RefCell::new(None));
|
||||
pub type LayoutFontContext = FontContext<FontCacheThread>;
|
||||
|
||||
thread_local!(static FONT_CONTEXT_KEY: RefCell<Option<LayoutFontContext>> = RefCell::new(None));
|
||||
|
||||
pub fn with_thread_local_font_context<F, R>(layout_context: &LayoutContext, f: F) -> R
|
||||
where F: FnOnce(&mut FontContext) -> R
|
||||
where F: FnOnce(&mut LayoutFontContext) -> R
|
||||
{
|
||||
FONT_CONTEXT_KEY.with(|k| {
|
||||
let mut font_context = k.borrow_mut();
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
use ServoArc;
|
||||
use app_units::{Au, MIN_AU};
|
||||
use block::AbsoluteAssignBSizesTraversal;
|
||||
use context::LayoutContext;
|
||||
use context::{LayoutContext, LayoutFontContext};
|
||||
use display_list::{DisplayListBuildState, InlineFlowDisplayListBuilding};
|
||||
use display_list::StackingContextCollectionState;
|
||||
use euclid::{Point2D, Size2D};
|
||||
|
@ -20,7 +20,6 @@ use fragment::FragmentFlags;
|
|||
use fragment::SpecificFragmentInfo;
|
||||
use gfx::display_list::OpaqueNode;
|
||||
use gfx::font::FontMetrics;
|
||||
use gfx::font_context::FontContext;
|
||||
use gfx_traits::print_tree::PrintTree;
|
||||
use layout_debug;
|
||||
use model::IntrinsicISizesContribution;
|
||||
|
@ -1132,7 +1131,7 @@ impl InlineFlow {
|
|||
/// Computes the minimum metrics for each line. This is done during flow construction.
|
||||
///
|
||||
/// `style` is the style of the block.
|
||||
pub fn minimum_line_metrics(&self, font_context: &mut FontContext, style: &ComputedValues)
|
||||
pub fn minimum_line_metrics(&self, font_context: &mut LayoutFontContext, style: &ComputedValues)
|
||||
-> LineMetrics {
|
||||
InlineFlow::minimum_line_metrics_for_fragments(&self.fragments.fragments,
|
||||
font_context,
|
||||
|
@ -1144,7 +1143,7 @@ impl InlineFlow {
|
|||
///
|
||||
/// `style` is the style of the block that these fragments belong to.
|
||||
pub fn minimum_line_metrics_for_fragments(fragments: &[Fragment],
|
||||
font_context: &mut FontContext,
|
||||
font_context: &mut LayoutFontContext,
|
||||
style: &ComputedValues)
|
||||
-> LineMetrics {
|
||||
// As a special case, if this flow contains only hypothetical fragments, then the entire
|
||||
|
|
|
@ -7,10 +7,10 @@
|
|||
#![deny(unsafe_code)]
|
||||
|
||||
use app_units::Au;
|
||||
use context::LayoutFontContext;
|
||||
use fragment::{Fragment, ScannedTextFlags};
|
||||
use fragment::{ScannedTextFragmentInfo, SpecificFragmentInfo, UnscannedTextFragmentInfo};
|
||||
use gfx::font::{FontMetrics, RunMetrics, ShapingFlags, ShapingOptions};
|
||||
use gfx::font_context::FontContext;
|
||||
use gfx::font::{FontRef, FontMetrics, RunMetrics, ShapingFlags, ShapingOptions};
|
||||
use gfx::text::glyph::ByteIndex;
|
||||
use gfx::text::text_run::TextRun;
|
||||
use gfx::text::util::{self, CompressionMode};
|
||||
|
@ -18,6 +18,7 @@ use inline::{InlineFragmentNodeFlags, InlineFragments};
|
|||
use linked_list::split_off_head;
|
||||
use ordered_float::NotNaN;
|
||||
use range::Range;
|
||||
use servo_atoms::Atom;
|
||||
use std::borrow::ToOwned;
|
||||
use std::collections::LinkedList;
|
||||
use std::mem;
|
||||
|
@ -28,7 +29,7 @@ use style::computed_values::white_space::T as WhiteSpace;
|
|||
use style::computed_values::word_break::T as WordBreak;
|
||||
use style::logical_geometry::{LogicalSize, WritingMode};
|
||||
use style::properties::ComputedValues;
|
||||
use style::properties::style_structs;
|
||||
use style::properties::style_structs::Font as FontStyleStruct;
|
||||
use style::values::generics::text::LineHeight;
|
||||
use unicode_bidi as bidi;
|
||||
use unicode_script::{Script, get_script};
|
||||
|
@ -68,7 +69,7 @@ impl TextRunScanner {
|
|||
}
|
||||
|
||||
pub fn scan_for_runs(&mut self,
|
||||
font_context: &mut FontContext,
|
||||
font_context: &mut LayoutFontContext,
|
||||
mut fragments: LinkedList<Fragment>)
|
||||
-> InlineFragments {
|
||||
debug!("TextRunScanner: scanning {} fragments for text runs...", fragments.len());
|
||||
|
@ -136,7 +137,7 @@ impl TextRunScanner {
|
|||
/// for correct painting order. Since we compress several leaf fragments here, the mapping must
|
||||
/// be adjusted.
|
||||
fn flush_clump_to_list(&mut self,
|
||||
font_context: &mut FontContext,
|
||||
mut font_context: &mut LayoutFontContext,
|
||||
out_fragments: &mut Vec<Fragment>,
|
||||
paragraph_bytes_processed: &mut usize,
|
||||
bidi_levels: Option<&[bidi::Level]>,
|
||||
|
@ -159,7 +160,7 @@ impl TextRunScanner {
|
|||
// Concatenate all of the transformed strings together, saving the new character indices.
|
||||
let mut mappings: Vec<RunMapping> = Vec::new();
|
||||
let runs = {
|
||||
let fontgroup;
|
||||
let font_group;
|
||||
let compression;
|
||||
let text_transform;
|
||||
let letter_spacing;
|
||||
|
@ -170,7 +171,7 @@ impl TextRunScanner {
|
|||
let in_fragment = self.clump.front().unwrap();
|
||||
let font_style = in_fragment.style().clone_font();
|
||||
let inherited_text_style = in_fragment.style().get_inheritedtext();
|
||||
fontgroup = font_context.layout_font_group_for_style(font_style);
|
||||
font_group = font_context.font_group(font_style);
|
||||
compression = match in_fragment.white_space() {
|
||||
WhiteSpace::Normal |
|
||||
WhiteSpace::Nowrap => CompressionMode::CompressWhitespaceNewline,
|
||||
|
@ -214,14 +215,7 @@ impl TextRunScanner {
|
|||
|
||||
let (mut start_position, mut end_position) = (0, 0);
|
||||
for (byte_index, character) in text.char_indices() {
|
||||
// Search for the first font in this font group that contains a glyph for this
|
||||
// character.
|
||||
let font_index = fontgroup.fonts.iter().position(|font| {
|
||||
font.borrow().glyph_index(character).is_some()
|
||||
}).unwrap_or(0);
|
||||
|
||||
// The following code panics one way or another if this condition isn't met.
|
||||
assert!(fontgroup.fonts.len() > 0);
|
||||
let font = font_group.borrow_mut().find_by_codepoint(&mut font_context, character);
|
||||
|
||||
let bidi_level = match bidi_levels {
|
||||
Some(levels) => levels[*paragraph_bytes_processed],
|
||||
|
@ -245,7 +239,7 @@ impl TextRunScanner {
|
|||
};
|
||||
|
||||
// Now, if necessary, flush the mapping we were building up.
|
||||
let flush_run = run_info.font_index != font_index ||
|
||||
let flush_run = !run_info.has_font(&font) ||
|
||||
run_info.bidi_level != bidi_level ||
|
||||
!compatible_script;
|
||||
let new_mapping_needed = flush_run || mapping.selected != selected;
|
||||
|
@ -272,7 +266,7 @@ impl TextRunScanner {
|
|||
mapping = RunMapping::new(&run_info_list[..],
|
||||
fragment_index);
|
||||
}
|
||||
run_info.font_index = font_index;
|
||||
run_info.font = font;
|
||||
run_info.bidi_level = bidi_level;
|
||||
run_info.script = script;
|
||||
mapping.selected = selected;
|
||||
|
@ -328,9 +322,14 @@ impl TextRunScanner {
|
|||
if run_info.bidi_level.is_rtl() {
|
||||
options.flags.insert(ShapingFlags::RTL_FLAG);
|
||||
}
|
||||
let mut font = fontgroup.fonts.get(run_info.font_index).unwrap().borrow_mut();
|
||||
|
||||
let (run, break_at_zero) = TextRun::new(&mut *font,
|
||||
// If no font is found (including fallbacks), there's no way we can render.
|
||||
let font =
|
||||
run_info.font
|
||||
.or_else(|| font_group.borrow_mut().first(&mut font_context))
|
||||
.expect("No font found for text run!");
|
||||
|
||||
let (run, break_at_zero) = TextRun::new(&mut *font.borrow_mut(),
|
||||
run_info.text,
|
||||
&options,
|
||||
run_info.bidi_level,
|
||||
|
@ -456,15 +455,20 @@ fn bounding_box_for_run_metrics(metrics: &RunMetrics, writing_mode: WritingMode)
|
|||
metrics.bounding_box.size.height)
|
||||
}
|
||||
|
||||
/// Returns the metrics of the font represented by the given `style_structs::Font`, respectively.
|
||||
/// Returns the metrics of the font represented by the given `FontStyleStruct`.
|
||||
///
|
||||
/// `#[inline]` because often the caller only needs a few fields from the font metrics.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if no font can be found for the given font style.
|
||||
#[inline]
|
||||
pub fn font_metrics_for_style(font_context: &mut FontContext, font_style: ::ServoArc<style_structs::Font>)
|
||||
pub fn font_metrics_for_style(mut font_context: &mut LayoutFontContext, style: ::ServoArc<FontStyleStruct>)
|
||||
-> FontMetrics {
|
||||
let fontgroup = font_context.layout_font_group_for_style(font_style);
|
||||
// FIXME(https://github.com/rust-lang/rust/issues/23338)
|
||||
let font = fontgroup.fonts[0].borrow();
|
||||
let font_group = font_context.font_group(style);
|
||||
let font = font_group.borrow_mut().first(&mut font_context);
|
||||
let font = font.as_ref().unwrap().borrow();
|
||||
|
||||
font.metrics.clone()
|
||||
}
|
||||
|
||||
|
@ -546,8 +550,8 @@ struct RunInfo {
|
|||
text: String,
|
||||
/// The insertion point in this text run, if applicable.
|
||||
insertion_point: Option<ByteIndex>,
|
||||
/// The index of the applicable font in the font group.
|
||||
font_index: usize,
|
||||
/// The font that the text should be rendered with.
|
||||
font: Option<FontRef>,
|
||||
/// The bidirection embedding level of this text run.
|
||||
bidi_level: bidi::Level,
|
||||
/// The Unicode script property of this text run.
|
||||
|
@ -559,7 +563,7 @@ impl RunInfo {
|
|||
RunInfo {
|
||||
text: String::new(),
|
||||
insertion_point: None,
|
||||
font_index: 0,
|
||||
font: None,
|
||||
bidi_level: bidi::Level::ltr(),
|
||||
script: Script::Common,
|
||||
}
|
||||
|
@ -584,6 +588,14 @@ impl RunInfo {
|
|||
}
|
||||
list.push(self);
|
||||
}
|
||||
|
||||
fn has_font(&self, font: &Option<FontRef>) -> bool {
|
||||
fn identifier(font: &Option<FontRef>) -> Option<Atom> {
|
||||
font.as_ref().map(|f| f.borrow().identifier())
|
||||
}
|
||||
|
||||
identifier(&self.font) == identifier(font)
|
||||
}
|
||||
}
|
||||
|
||||
/// A mapping from a portion of an unscanned text fragment to the text run we're going to create
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue