mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
fonts: Make FontContext
thread-safe and share it per-Layout (#32205)
This allows sharing font templates, fonts, and platform fonts across layout threads. It's the first step toward storing web fonts in the layout versus the shared `FontCacheThread`. Now fonts and font groups have some locking (especially on FreeType), which will probably affect performance. On the other hand, we measured memory usage and this saves roughly 40 megabytes of memory when loading servo.org based on data from the memory profiler. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com>
This commit is contained in:
parent
8ec5344f70
commit
556bfb7dff
27 changed files with 437 additions and 500 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1952,6 +1952,7 @@ name = "gfx"
|
||||||
version = "0.0.1"
|
version = "0.0.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"app_units",
|
"app_units",
|
||||||
|
"atomic_refcell",
|
||||||
"bitflags 2.5.0",
|
"bitflags 2.5.0",
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
|
|
|
@ -115,6 +115,7 @@ to_shmem = { git = "https://github.com/servo/stylo", branch = "2024-04-16" }
|
||||||
tokio = "1"
|
tokio = "1"
|
||||||
tokio-rustls = "0.24"
|
tokio-rustls = "0.24"
|
||||||
tungstenite = "0.20"
|
tungstenite = "0.20"
|
||||||
|
uluru = "3.0"
|
||||||
unicode-bidi = "0.3.15"
|
unicode-bidi = "0.3.15"
|
||||||
unicode-script = "0.5"
|
unicode-script = "0.5"
|
||||||
unicode-segmentation = "1.1.0"
|
unicode-segmentation = "1.1.0"
|
||||||
|
|
|
@ -2,9 +2,8 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::Arc;
|
||||||
|
|
||||||
use canvas_traits::canvas::*;
|
use canvas_traits::canvas::*;
|
||||||
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
|
use euclid::default::{Point2D, Rect, Size2D, Transform2D, Vector2D};
|
||||||
|
@ -359,21 +358,6 @@ pub enum Filter {
|
||||||
Nearest,
|
Nearest,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) type CanvasFontContext = FontContext<FontCacheThread>;
|
|
||||||
|
|
||||||
thread_local!(static FONT_CONTEXT: RefCell<Option<CanvasFontContext>> = RefCell::new(None));
|
|
||||||
|
|
||||||
pub(crate) fn with_thread_local_font_context<F, R>(canvas_data: &CanvasData, f: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut CanvasFontContext) -> R,
|
|
||||||
{
|
|
||||||
FONT_CONTEXT.with(|font_context| {
|
|
||||||
f(font_context.borrow_mut().get_or_insert_with(|| {
|
|
||||||
FontContext::new(canvas_data.font_cache_thread.lock().unwrap().clone())
|
|
||||||
}))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct CanvasData<'a> {
|
pub struct CanvasData<'a> {
|
||||||
backend: Box<dyn Backend>,
|
backend: Box<dyn Backend>,
|
||||||
drawtarget: Box<dyn GenericDrawTarget>,
|
drawtarget: Box<dyn GenericDrawTarget>,
|
||||||
|
@ -386,7 +370,7 @@ pub struct CanvasData<'a> {
|
||||||
old_image_key: Option<ImageKey>,
|
old_image_key: Option<ImageKey>,
|
||||||
/// An old webrender image key that can be deleted when the current epoch ends.
|
/// An old webrender image key that can be deleted when the current epoch ends.
|
||||||
very_old_image_key: Option<ImageKey>,
|
very_old_image_key: Option<ImageKey>,
|
||||||
font_cache_thread: Mutex<FontCacheThread>,
|
font_context: Arc<FontContext<FontCacheThread>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_backend() -> Box<dyn Backend> {
|
fn create_backend() -> Box<dyn Backend> {
|
||||||
|
@ -398,7 +382,7 @@ impl<'a> CanvasData<'a> {
|
||||||
size: Size2D<u64>,
|
size: Size2D<u64>,
|
||||||
webrender_api: Box<dyn WebrenderApi>,
|
webrender_api: Box<dyn WebrenderApi>,
|
||||||
antialias: AntialiasMode,
|
antialias: AntialiasMode,
|
||||||
font_cache_thread: FontCacheThread,
|
font_context: Arc<FontContext<FontCacheThread>>,
|
||||||
) -> CanvasData<'a> {
|
) -> CanvasData<'a> {
|
||||||
let backend = create_backend();
|
let backend = create_backend();
|
||||||
let draw_target = backend.create_drawtarget(size);
|
let draw_target = backend.create_drawtarget(size);
|
||||||
|
@ -412,7 +396,7 @@ impl<'a> CanvasData<'a> {
|
||||||
image_key: None,
|
image_key: None,
|
||||||
old_image_key: None,
|
old_image_key: None,
|
||||||
very_old_image_key: None,
|
very_old_image_key: None,
|
||||||
font_cache_thread: Mutex::new(font_cache_thread),
|
font_context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,17 +478,14 @@ impl<'a> CanvasData<'a> {
|
||||||
let font = font_style.map_or_else(
|
let font = font_style.map_or_else(
|
||||||
|| load_system_font_from_style(None),
|
|| load_system_font_from_style(None),
|
||||||
|style| {
|
|style| {
|
||||||
with_thread_local_font_context(self, |font_context| {
|
let font_group = self.font_context.font_group(ServoArc::new(style.clone()));
|
||||||
let font_group = font_context.font_group(ServoArc::new(style.clone()));
|
let font = font_group
|
||||||
let font = font_group
|
.write()
|
||||||
.borrow_mut()
|
.first(&self.font_context)
|
||||||
.first(font_context)
|
.expect("couldn't find font");
|
||||||
.expect("couldn't find font");
|
Font::from_bytes(font.template.data(), 0)
|
||||||
let font = font.borrow_mut();
|
.ok()
|
||||||
Font::from_bytes(font.template.data(), 0)
|
.or_else(|| load_system_font_from_style(Some(style)))
|
||||||
.ok()
|
|
||||||
.or_else(|| load_system_font_from_style(Some(style)))
|
|
||||||
})
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
let font = match font {
|
let font = match font {
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::Arc;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
|
||||||
use canvas_traits::canvas::*;
|
use canvas_traits::canvas::*;
|
||||||
|
@ -11,6 +12,7 @@ use canvas_traits::ConstellationCanvasMsg;
|
||||||
use crossbeam_channel::{select, unbounded, Sender};
|
use crossbeam_channel::{select, unbounded, Sender};
|
||||||
use euclid::default::Size2D;
|
use euclid::default::Size2D;
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
|
use gfx::font_context::FontContext;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use log::warn;
|
use log::warn;
|
||||||
|
@ -40,7 +42,7 @@ pub struct CanvasPaintThread<'a> {
|
||||||
canvases: HashMap<CanvasId, CanvasData<'a>>,
|
canvases: HashMap<CanvasId, CanvasData<'a>>,
|
||||||
next_canvas_id: CanvasId,
|
next_canvas_id: CanvasId,
|
||||||
webrender_api: Box<dyn WebrenderApi>,
|
webrender_api: Box<dyn WebrenderApi>,
|
||||||
font_cache_thread: FontCacheThread,
|
font_context: Arc<FontContext<FontCacheThread>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CanvasPaintThread<'a> {
|
impl<'a> CanvasPaintThread<'a> {
|
||||||
|
@ -52,7 +54,7 @@ impl<'a> CanvasPaintThread<'a> {
|
||||||
canvases: HashMap::new(),
|
canvases: HashMap::new(),
|
||||||
next_canvas_id: CanvasId(0),
|
next_canvas_id: CanvasId(0),
|
||||||
webrender_api,
|
webrender_api,
|
||||||
font_cache_thread,
|
font_context: Arc::new(FontContext::new(font_cache_thread)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,8 +131,6 @@ impl<'a> CanvasPaintThread<'a> {
|
||||||
AntialiasMode::None
|
AntialiasMode::None
|
||||||
};
|
};
|
||||||
|
|
||||||
let font_cache_thread = self.font_cache_thread.clone();
|
|
||||||
|
|
||||||
let canvas_id = self.next_canvas_id;
|
let canvas_id = self.next_canvas_id;
|
||||||
self.next_canvas_id.0 += 1;
|
self.next_canvas_id.0 += 1;
|
||||||
|
|
||||||
|
@ -138,7 +138,7 @@ impl<'a> CanvasPaintThread<'a> {
|
||||||
size,
|
size,
|
||||||
self.webrender_api.clone(),
|
self.webrender_api.clone(),
|
||||||
antialias,
|
antialias,
|
||||||
font_cache_thread,
|
self.font_context.clone(),
|
||||||
);
|
);
|
||||||
self.canvases.insert(canvas_id, canvas_data);
|
self.canvases.insert(canvas_id, canvas_data);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,7 @@ doctest = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
app_units = { workspace = true }
|
app_units = { workspace = true }
|
||||||
|
atomic_refcell = { workspace = true }
|
||||||
bitflags = { workspace = true }
|
bitflags = { workspace = true }
|
||||||
cssparser = { workspace = true }
|
cssparser = { workspace = true }
|
||||||
euclid = { workspace = true }
|
euclid = { workspace = true }
|
||||||
|
|
|
@ -3,11 +3,9 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Arc;
|
use std::sync::{Arc, OnceLock};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use std::{iter, str};
|
use std::{iter, str};
|
||||||
|
|
||||||
|
@ -15,6 +13,7 @@ use app_units::Au;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use euclid::default::{Point2D, Rect, Size2D};
|
use euclid::default::{Point2D, Rect, Size2D};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
use parking_lot::RwLock;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use servo_atoms::{atom, Atom};
|
use servo_atoms::{atom, Atom};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -185,15 +184,21 @@ impl<'a> From<&'a FontStyleStruct> for FontDescriptor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
struct CachedShapeData {
|
||||||
|
glyph_advances: HashMap<GlyphId, FractionalPixel>,
|
||||||
|
glyph_indices: HashMap<char, Option<GlyphId>>,
|
||||||
|
shaped_text: HashMap<ShapeCacheEntry, Arc<GlyphStore>>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Font {
|
pub struct Font {
|
||||||
pub handle: PlatformFont,
|
pub handle: PlatformFont,
|
||||||
pub template: FontTemplateRef,
|
pub template: FontTemplateRef,
|
||||||
pub metrics: FontMetrics,
|
pub metrics: FontMetrics,
|
||||||
pub descriptor: FontDescriptor,
|
pub descriptor: FontDescriptor,
|
||||||
shaper: Option<Shaper>,
|
shaper: OnceLock<Shaper>,
|
||||||
shape_cache: RefCell<HashMap<ShapeCacheEntry, Arc<GlyphStore>>>,
|
cached_shape_data: RwLock<CachedShapeData>,
|
||||||
glyph_advance_cache: RefCell<HashMap<u32, FractionalPixel>>,
|
|
||||||
pub font_key: FontInstanceKey,
|
pub font_key: FontInstanceKey,
|
||||||
|
|
||||||
/// If this is a synthesized small caps font, then this font reference is for
|
/// If this is a synthesized small caps font, then this font reference is for
|
||||||
|
@ -214,11 +219,10 @@ impl Font {
|
||||||
Ok(Font {
|
Ok(Font {
|
||||||
handle,
|
handle,
|
||||||
template,
|
template,
|
||||||
shaper: None,
|
shaper: OnceLock::new(),
|
||||||
descriptor,
|
descriptor,
|
||||||
metrics,
|
metrics,
|
||||||
shape_cache: RefCell::new(HashMap::new()),
|
cached_shape_data: Default::default(),
|
||||||
glyph_advance_cache: RefCell::new(HashMap::new()),
|
|
||||||
font_key: FontInstanceKey::default(),
|
font_key: FontInstanceKey::default(),
|
||||||
synthesized_small_caps,
|
synthesized_small_caps,
|
||||||
})
|
})
|
||||||
|
@ -272,52 +276,49 @@ struct ShapeCacheEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Font {
|
impl Font {
|
||||||
pub fn shape_text(&mut self, text: &str, options: &ShapingOptions) -> Arc<GlyphStore> {
|
pub fn shape_text(&self, text: &str, options: &ShapingOptions) -> Arc<GlyphStore> {
|
||||||
let this = self as *const Font;
|
let this = self as *const Font;
|
||||||
let mut shaper = self.shaper.take();
|
|
||||||
|
|
||||||
let lookup_key = ShapeCacheEntry {
|
let lookup_key = ShapeCacheEntry {
|
||||||
text: text.to_owned(),
|
text: text.to_owned(),
|
||||||
options: *options,
|
options: *options,
|
||||||
};
|
};
|
||||||
let result = self
|
{
|
||||||
.shape_cache
|
let cache = self.cached_shape_data.read();
|
||||||
.borrow_mut()
|
if let Some(shaped_text) = cache.shaped_text.get(&lookup_key) {
|
||||||
.entry(lookup_key)
|
return shaped_text.clone();
|
||||||
.or_insert_with(|| {
|
}
|
||||||
let start_time = Instant::now();
|
}
|
||||||
let mut glyphs = GlyphStore::new(
|
|
||||||
text.len(),
|
|
||||||
options
|
|
||||||
.flags
|
|
||||||
.contains(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG),
|
|
||||||
options.flags.contains(ShapingFlags::RTL_FLAG),
|
|
||||||
);
|
|
||||||
|
|
||||||
if self.can_do_fast_shaping(text, options) {
|
let start_time = Instant::now();
|
||||||
debug!("shape_text: Using ASCII fast path.");
|
let mut glyphs = GlyphStore::new(
|
||||||
self.shape_text_fast(text, options, &mut glyphs);
|
text.len(),
|
||||||
} else {
|
options
|
||||||
debug!("shape_text: Using Harfbuzz.");
|
.flags
|
||||||
if shaper.is_none() {
|
.contains(ShapingFlags::IS_WHITESPACE_SHAPING_FLAG),
|
||||||
shaper = Some(Shaper::new(this));
|
options.flags.contains(ShapingFlags::RTL_FLAG),
|
||||||
}
|
);
|
||||||
shaper
|
|
||||||
.as_ref()
|
|
||||||
.unwrap()
|
|
||||||
.shape_text(text, options, &mut glyphs);
|
|
||||||
}
|
|
||||||
|
|
||||||
let end_time = Instant::now();
|
if self.can_do_fast_shaping(text, options) {
|
||||||
TEXT_SHAPING_PERFORMANCE_COUNTER.fetch_add(
|
debug!("shape_text: Using ASCII fast path.");
|
||||||
(end_time.duration_since(start_time).as_nanos()) as usize,
|
self.shape_text_fast(text, options, &mut glyphs);
|
||||||
Ordering::Relaxed,
|
} else {
|
||||||
);
|
debug!("shape_text: Using Harfbuzz.");
|
||||||
Arc::new(glyphs)
|
self.shaper
|
||||||
})
|
.get_or_init(|| Shaper::new(this))
|
||||||
.clone();
|
.shape_text(text, options, &mut glyphs);
|
||||||
self.shaper = shaper;
|
}
|
||||||
result
|
|
||||||
|
let shaped_text = Arc::new(glyphs);
|
||||||
|
let mut cache = self.cached_shape_data.write();
|
||||||
|
cache.shaped_text.insert(lookup_key, shaped_text.clone());
|
||||||
|
|
||||||
|
let end_time = Instant::now();
|
||||||
|
TEXT_SHAPING_PERFORMANCE_COUNTER.fetch_add(
|
||||||
|
(end_time.duration_since(start_time).as_nanos()) as usize,
|
||||||
|
Ordering::Relaxed,
|
||||||
|
);
|
||||||
|
|
||||||
|
shaped_text
|
||||||
}
|
}
|
||||||
|
|
||||||
fn can_do_fast_shaping(&self, text: &str, options: &ShapingOptions) -> bool {
|
fn can_do_fast_shaping(&self, text: &str, options: &ShapingOptions) -> bool {
|
||||||
|
@ -377,11 +378,21 @@ impl Font {
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
pub fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
||||||
|
{
|
||||||
|
let cache = self.cached_shape_data.read();
|
||||||
|
if let Some(glyph) = cache.glyph_indices.get(&codepoint) {
|
||||||
|
return *glyph;
|
||||||
|
}
|
||||||
|
}
|
||||||
let codepoint = match self.descriptor.variant {
|
let codepoint = match self.descriptor.variant {
|
||||||
font_variant_caps::T::SmallCaps => codepoint.to_ascii_uppercase(),
|
font_variant_caps::T::SmallCaps => codepoint.to_ascii_uppercase(),
|
||||||
font_variant_caps::T::Normal => codepoint,
|
font_variant_caps::T::Normal => codepoint,
|
||||||
};
|
};
|
||||||
self.handle.glyph_index(codepoint)
|
let glyph_index = self.handle.glyph_index(codepoint);
|
||||||
|
|
||||||
|
let mut cache = self.cached_shape_data.write();
|
||||||
|
cache.glyph_indices.insert(codepoint, glyph_index);
|
||||||
|
glyph_index
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_glyph_for(&self, codepoint: char) -> bool {
|
pub fn has_glyph_for(&self, codepoint: char) -> bool {
|
||||||
|
@ -392,21 +403,27 @@ impl Font {
|
||||||
self.handle.glyph_h_kerning(first_glyph, second_glyph)
|
self.handle.glyph_h_kerning(first_glyph, second_glyph)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn glyph_h_advance(&self, glyph: GlyphId) -> FractionalPixel {
|
pub fn glyph_h_advance(&self, glyph_id: GlyphId) -> FractionalPixel {
|
||||||
*self
|
{
|
||||||
.glyph_advance_cache
|
let cache = self.cached_shape_data.read();
|
||||||
.borrow_mut()
|
if let Some(width) = cache.glyph_advances.get(&glyph_id) {
|
||||||
.entry(glyph)
|
return *width;
|
||||||
.or_insert_with(|| {
|
}
|
||||||
match self.handle.glyph_h_advance(glyph) {
|
}
|
||||||
Some(adv) => adv,
|
|
||||||
None => LAST_RESORT_GLYPH_ADVANCE as FractionalPixel, // FIXME: Need fallback strategy
|
// TODO: Need a fallback strategy.
|
||||||
}
|
let new_width = match self.handle.glyph_h_advance(glyph_id) {
|
||||||
})
|
Some(adv) => adv,
|
||||||
|
None => LAST_RESORT_GLYPH_ADVANCE as FractionalPixel,
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cache = self.cached_shape_data.write();
|
||||||
|
cache.glyph_advances.insert(glyph_id, new_width);
|
||||||
|
new_width
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type FontRef = Rc<RefCell<Font>>;
|
pub type FontRef = Arc<Font>;
|
||||||
|
|
||||||
/// A `FontGroup` is a prioritised list of fonts for a given set of font styles. It is used by
|
/// 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
|
/// `TextRun` to decide which font to render a character with. If none of the fonts listed in the
|
||||||
|
@ -442,14 +459,13 @@ impl FontGroup {
|
||||||
/// found, returns None.
|
/// found, returns None.
|
||||||
pub fn find_by_codepoint<S: FontSource>(
|
pub fn find_by_codepoint<S: FontSource>(
|
||||||
&mut self,
|
&mut self,
|
||||||
font_context: &mut FontContext<S>,
|
font_context: &FontContext<S>,
|
||||||
codepoint: char,
|
codepoint: char,
|
||||||
) -> Option<FontRef> {
|
) -> Option<FontRef> {
|
||||||
let should_look_for_small_caps = self.descriptor.variant == font_variant_caps::T::SmallCaps &&
|
let should_look_for_small_caps = self.descriptor.variant == font_variant_caps::T::SmallCaps &&
|
||||||
codepoint.is_ascii_lowercase();
|
codepoint.is_ascii_lowercase();
|
||||||
let font_or_synthesized_small_caps = |font: FontRef| {
|
let font_or_synthesized_small_caps = |font: FontRef| {
|
||||||
if should_look_for_small_caps {
|
if should_look_for_small_caps {
|
||||||
let font = font.borrow();
|
|
||||||
if font.synthesized_small_caps.is_some() {
|
if font.synthesized_small_caps.is_some() {
|
||||||
return font.synthesized_small_caps.clone();
|
return font.synthesized_small_caps.clone();
|
||||||
}
|
}
|
||||||
|
@ -457,7 +473,7 @@ impl FontGroup {
|
||||||
Some(font)
|
Some(font)
|
||||||
};
|
};
|
||||||
|
|
||||||
let glyph_in_font = |font: &FontRef| font.borrow().has_glyph_for(codepoint);
|
let glyph_in_font = |font: &FontRef| font.has_glyph_for(codepoint);
|
||||||
let char_in_template =
|
let char_in_template =
|
||||||
|template: FontTemplateRef| template.char_in_unicode_range(codepoint);
|
|template: FontTemplateRef| template.char_in_unicode_range(codepoint);
|
||||||
|
|
||||||
|
@ -466,7 +482,7 @@ impl FontGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(ref last_matching_fallback) = self.last_matching_fallback {
|
if let Some(ref last_matching_fallback) = self.last_matching_fallback {
|
||||||
if char_in_template(last_matching_fallback.borrow().template.clone()) &&
|
if char_in_template(last_matching_fallback.template.clone()) &&
|
||||||
glyph_in_font(last_matching_fallback)
|
glyph_in_font(last_matching_fallback)
|
||||||
{
|
{
|
||||||
return font_or_synthesized_small_caps(last_matching_fallback.clone());
|
return font_or_synthesized_small_caps(last_matching_fallback.clone());
|
||||||
|
@ -487,7 +503,7 @@ impl FontGroup {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Find the first available font in the group, or the first available fallback font.
|
/// Find the first available font in the group, or the first available fallback font.
|
||||||
pub fn first<S: FontSource>(&mut self, font_context: &mut FontContext<S>) -> Option<FontRef> {
|
pub fn first<S: FontSource>(&mut self, font_context: &FontContext<S>) -> Option<FontRef> {
|
||||||
// From https://drafts.csswg.org/css-fonts/#first-available-font:
|
// From https://drafts.csswg.org/css-fonts/#first-available-font:
|
||||||
// > The first available font, used for example in the definition of font-relative lengths
|
// > The first available font, used for example in the definition of font-relative lengths
|
||||||
// > such as ex or in the definition of the line-height property, is defined to be the first
|
// > such as ex or in the definition of the line-height property, is defined to be the first
|
||||||
|
@ -506,7 +522,7 @@ impl FontGroup {
|
||||||
/// a suitable font.
|
/// a suitable font.
|
||||||
fn find<S, TemplatePredicate, FontPredicate>(
|
fn find<S, TemplatePredicate, FontPredicate>(
|
||||||
&mut self,
|
&mut self,
|
||||||
font_context: &mut FontContext<S>,
|
font_context: &FontContext<S>,
|
||||||
template_predicate: TemplatePredicate,
|
template_predicate: TemplatePredicate,
|
||||||
font_predicate: FontPredicate,
|
font_predicate: FontPredicate,
|
||||||
) -> Option<FontRef>
|
) -> Option<FontRef>
|
||||||
|
@ -535,7 +551,7 @@ impl FontGroup {
|
||||||
/// used to refine the list of family names which will be tried.
|
/// used to refine the list of family names which will be tried.
|
||||||
fn find_fallback<S, TemplatePredicate, FontPredicate>(
|
fn find_fallback<S, TemplatePredicate, FontPredicate>(
|
||||||
&mut self,
|
&mut self,
|
||||||
font_context: &mut FontContext<S>,
|
font_context: &FontContext<S>,
|
||||||
codepoint: Option<char>,
|
codepoint: Option<char>,
|
||||||
template_predicate: TemplatePredicate,
|
template_predicate: TemplatePredicate,
|
||||||
font_predicate: FontPredicate,
|
font_predicate: FontPredicate,
|
||||||
|
@ -602,7 +618,7 @@ impl FontGroupFamily {
|
||||||
fn find<S, TemplatePredicate, FontPredicate>(
|
fn find<S, TemplatePredicate, FontPredicate>(
|
||||||
&mut self,
|
&mut self,
|
||||||
font_descriptor: &FontDescriptor,
|
font_descriptor: &FontDescriptor,
|
||||||
font_context: &mut FontContext<S>,
|
font_context: &FontContext<S>,
|
||||||
template_predicate: &TemplatePredicate,
|
template_predicate: &TemplatePredicate,
|
||||||
font_predicate: &FontPredicate,
|
font_predicate: &FontPredicate,
|
||||||
) -> Option<FontRef>
|
) -> Option<FontRef>
|
||||||
|
@ -633,7 +649,7 @@ impl FontGroupFamily {
|
||||||
fn members<'a, S: FontSource>(
|
fn members<'a, S: FontSource>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
font_descriptor: &FontDescriptor,
|
font_descriptor: &FontDescriptor,
|
||||||
font_context: &mut FontContext<S>,
|
font_context: &FontContext<S>,
|
||||||
) -> impl Iterator<Item = &mut FontGroupFamilyMember> + 'a {
|
) -> impl Iterator<Item = &mut FontGroupFamilyMember> + 'a {
|
||||||
let family_descriptor = &self.family_descriptor;
|
let family_descriptor = &self.family_descriptor;
|
||||||
let members = self.members.get_or_insert_with(|| {
|
let members = self.members.get_or_insert_with(|| {
|
||||||
|
|
|
@ -3,14 +3,13 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::borrow::ToOwned;
|
use std::borrow::ToOwned;
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::ops::{Deref, RangeInclusive};
|
use std::ops::{Deref, RangeInclusive};
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::{f32, fmt, mem, thread};
|
use std::{f32, fmt, mem, thread};
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
|
use atomic_refcell::AtomicRefCell;
|
||||||
use gfx_traits::WebrenderApi;
|
use gfx_traits::WebrenderApi;
|
||||||
use ipc_channel::ipc::{self, IpcBytesSender, IpcReceiver, IpcSender};
|
use ipc_channel::ipc::{self, IpcBytesSender, IpcReceiver, IpcSender};
|
||||||
use log::{debug, trace};
|
use log::{debug, trace};
|
||||||
|
@ -125,7 +124,8 @@ impl FontTemplates {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.templates.push(Rc::new(RefCell::new(new_template)));
|
self.templates
|
||||||
|
.push(Arc::new(AtomicRefCell::new(new_template)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -730,7 +730,7 @@ impl FontSource for FontCacheThread {
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|serialized_font_template| {
|
.map(|serialized_font_template| {
|
||||||
let font_data = serialized_font_template.bytes_receiver.recv().ok();
|
let font_data = serialized_font_template.bytes_receiver.recv().ok();
|
||||||
Rc::new(RefCell::new(FontTemplate {
|
Arc::new(AtomicRefCell::new(FontTemplate {
|
||||||
identifier: serialized_font_template.identifier,
|
identifier: serialized_font_template.identifier,
|
||||||
descriptor: serialized_font_template.descriptor.clone(),
|
descriptor: serialized_font_template.descriptor.clone(),
|
||||||
data: font_data.map(Arc::new),
|
data: font_data.map(Arc::new),
|
||||||
|
|
|
@ -2,17 +2,16 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
use std::hash::{BuildHasherDefault, Hash, Hasher};
|
||||||
use std::rc::Rc;
|
use std::sync::Arc;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use servo_arc::Arc;
|
use parking_lot::{Mutex, RwLock};
|
||||||
|
use servo_arc::Arc as ServoArc;
|
||||||
use style::computed_values::font_variant_caps::T as FontVariantCaps;
|
use style::computed_values::font_variant_caps::T as FontVariantCaps;
|
||||||
use style::properties::style_structs::Font as FontStyleStruct;
|
use style::properties::style_structs::Font as FontStyleStruct;
|
||||||
use webrender_api::{FontInstanceFlags, FontInstanceKey};
|
use webrender_api::{FontInstanceFlags, FontInstanceKey};
|
||||||
|
@ -25,10 +24,6 @@ use crate::platform::core_text_font_cache::CoreTextFontCache;
|
||||||
|
|
||||||
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
|
static SMALL_CAPS_SCALE_FACTOR: f32 = 0.8; // Matches FireFox (see gfxFont.h)
|
||||||
|
|
||||||
/// 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 = AtomicUsize::new(0);
|
|
||||||
|
|
||||||
pub trait FontSource {
|
pub trait FontSource {
|
||||||
fn get_font_instance(
|
fn get_font_instance(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -49,48 +44,48 @@ pub trait FontSource {
|
||||||
/// required.
|
/// required.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct FontContext<S: FontSource> {
|
pub struct FontContext<S: FontSource> {
|
||||||
font_source: S,
|
font_source: Mutex<S>,
|
||||||
|
|
||||||
// TODO: The font context holds a strong ref to the cached fonts
|
// 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.
|
// so they will never be released. Find out a good time to drop them.
|
||||||
// See bug https://github.com/servo/servo/issues/3300
|
// See bug https://github.com/servo/servo/issues/3300
|
||||||
font_cache: HashMap<FontCacheKey, Option<FontRef>>,
|
font_cache: RwLock<HashMap<FontCacheKey, Option<FontRef>>>,
|
||||||
font_template_cache: HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>,
|
font_template_cache: RwLock<HashMap<FontTemplateCacheKey, Vec<FontTemplateRef>>>,
|
||||||
|
|
||||||
font_group_cache:
|
font_group_cache:
|
||||||
HashMap<FontGroupCacheKey, Rc<RefCell<FontGroup>>, BuildHasherDefault<FnvHasher>>,
|
RwLock<HashMap<FontGroupCacheKey, Arc<RwLock<FontGroup>>, BuildHasherDefault<FnvHasher>>>,
|
||||||
|
|
||||||
epoch: usize,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: FontSource> FontContext<S> {
|
impl<S: FontSource> FontContext<S> {
|
||||||
pub fn new(font_source: S) -> FontContext<S> {
|
pub fn new(font_source: S) -> FontContext<S> {
|
||||||
#[allow(clippy::default_constructed_unit_structs)]
|
#[allow(clippy::default_constructed_unit_structs)]
|
||||||
FontContext {
|
FontContext {
|
||||||
font_source,
|
font_source: Mutex::new(font_source),
|
||||||
font_cache: HashMap::new(),
|
font_cache: RwLock::default(),
|
||||||
font_template_cache: HashMap::new(),
|
font_template_cache: RwLock::default(),
|
||||||
font_group_cache: HashMap::with_hasher(Default::default()),
|
font_group_cache: RwLock::default(),
|
||||||
epoch: 0,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn expire_font_caches_if_necessary(&mut self) {
|
/// Invalidate all caches that this [`FontContext`] holds and any in-process platform-specific
|
||||||
let current_epoch = FONT_CACHE_EPOCH.load(Ordering::SeqCst);
|
/// caches.
|
||||||
if current_epoch == self.epoch {
|
///
|
||||||
return;
|
/// # Safety
|
||||||
}
|
///
|
||||||
|
/// This should never be called when more than one thread is using the [`FontContext`] or it
|
||||||
|
/// may leave the context in an inconsistent state.
|
||||||
|
pub fn invalidate_caches(&self) {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
CoreTextFontCache::clear_core_text_font_cache();
|
||||||
|
|
||||||
self.font_cache.clear();
|
self.font_cache.write().clear();
|
||||||
self.font_template_cache.clear();
|
self.font_template_cache.write().clear();
|
||||||
self.font_group_cache.clear();
|
self.font_group_cache.write().clear();
|
||||||
self.epoch = current_epoch
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`.
|
/// Returns a `FontGroup` representing fonts which can be used for layout, given the `style`.
|
||||||
/// Font groups are cached, so subsequent calls with the same `style` will return a reference
|
/// Font groups are cached, so subsequent calls with the same `style` will return a reference
|
||||||
/// to an existing `FontGroup`.
|
/// to an existing `FontGroup`.
|
||||||
pub fn font_group(&mut self, style: Arc<FontStyleStruct>) -> Rc<RefCell<FontGroup>> {
|
pub fn font_group(&self, style: ServoArc<FontStyleStruct>) -> Arc<RwLock<FontGroup>> {
|
||||||
let font_size = style.font_size.computed_size().into();
|
let font_size = style.font_size.computed_size().into();
|
||||||
self.font_group_with_size(style, font_size)
|
self.font_group_with_size(style, font_size)
|
||||||
}
|
}
|
||||||
|
@ -98,27 +93,27 @@ impl<S: FontSource> FontContext<S> {
|
||||||
/// Like [`Self::font_group`], but overriding the size found in the [`FontStyleStruct`] with the given size
|
/// Like [`Self::font_group`], but overriding the size found in the [`FontStyleStruct`] with the given size
|
||||||
/// in pixels.
|
/// in pixels.
|
||||||
pub fn font_group_with_size(
|
pub fn font_group_with_size(
|
||||||
&mut self,
|
&self,
|
||||||
style: Arc<FontStyleStruct>,
|
style: ServoArc<FontStyleStruct>,
|
||||||
size: Au,
|
size: Au,
|
||||||
) -> Rc<RefCell<FontGroup>> {
|
) -> Arc<RwLock<FontGroup>> {
|
||||||
self.expire_font_caches_if_necessary();
|
|
||||||
|
|
||||||
let cache_key = FontGroupCacheKey { size, style };
|
let cache_key = FontGroupCacheKey { size, style };
|
||||||
|
|
||||||
if let Some(font_group) = self.font_group_cache.get(&cache_key) {
|
if let Some(font_group) = self.font_group_cache.read().get(&cache_key) {
|
||||||
return font_group.clone();
|
return font_group.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
let font_group = Rc::new(RefCell::new(FontGroup::new(&cache_key.style)));
|
let font_group = Arc::new(RwLock::new(FontGroup::new(&cache_key.style)));
|
||||||
self.font_group_cache.insert(cache_key, font_group.clone());
|
self.font_group_cache
|
||||||
|
.write()
|
||||||
|
.insert(cache_key, font_group.clone());
|
||||||
font_group
|
font_group
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a
|
/// Returns a font matching the parameters. Fonts are cached, so repeated calls will return a
|
||||||
/// reference to the same underlying `Font`.
|
/// reference to the same underlying `Font`.
|
||||||
pub fn font(
|
pub fn font(
|
||||||
&mut self,
|
&self,
|
||||||
font_template: FontTemplateRef,
|
font_template: FontTemplateRef,
|
||||||
font_descriptor: &FontDescriptor,
|
font_descriptor: &FontDescriptor,
|
||||||
) -> Option<FontRef> {
|
) -> Option<FontRef> {
|
||||||
|
@ -130,7 +125,7 @@ impl<S: FontSource> FontContext<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_font_maybe_synthesizing_small_caps(
|
fn get_font_maybe_synthesizing_small_caps(
|
||||||
&mut self,
|
&self,
|
||||||
font_template: FontTemplateRef,
|
font_template: FontTemplateRef,
|
||||||
font_descriptor: &FontDescriptor,
|
font_descriptor: &FontDescriptor,
|
||||||
synthesize_small_caps: bool,
|
synthesize_small_caps: bool,
|
||||||
|
@ -157,27 +152,28 @@ impl<S: FontSource> FontContext<S> {
|
||||||
font_descriptor: font_descriptor.clone(),
|
font_descriptor: font_descriptor.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.font_cache.get(&cache_key).cloned().unwrap_or_else(|| {
|
if let Some(font) = self.font_cache.read().get(&cache_key).cloned() {
|
||||||
debug!(
|
return font;
|
||||||
"FontContext::font cache miss for font_template={:?} font_descriptor={:?}",
|
}
|
||||||
font_template, font_descriptor
|
|
||||||
);
|
|
||||||
|
|
||||||
let font = self
|
debug!(
|
||||||
.create_font(
|
"FontContext::font cache miss for font_template={:?} font_descriptor={:?}",
|
||||||
font_template,
|
font_template, font_descriptor
|
||||||
font_descriptor.to_owned(),
|
);
|
||||||
synthesized_small_caps_font,
|
|
||||||
)
|
|
||||||
.ok();
|
|
||||||
self.font_cache.insert(cache_key, font.clone());
|
|
||||||
|
|
||||||
font
|
let font = self
|
||||||
})
|
.create_font(
|
||||||
|
font_template,
|
||||||
|
font_descriptor.to_owned(),
|
||||||
|
synthesized_small_caps_font,
|
||||||
|
)
|
||||||
|
.ok();
|
||||||
|
self.font_cache.write().insert(cache_key, font.clone());
|
||||||
|
font
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn matching_templates(
|
pub fn matching_templates(
|
||||||
&mut self,
|
&self,
|
||||||
descriptor_to_match: &FontDescriptor,
|
descriptor_to_match: &FontDescriptor,
|
||||||
family_descriptor: &FontFamilyDescriptor,
|
family_descriptor: &FontFamilyDescriptor,
|
||||||
) -> Vec<FontTemplateRef> {
|
) -> Vec<FontTemplateRef> {
|
||||||
|
@ -186,27 +182,31 @@ impl<S: FontSource> FontContext<S> {
|
||||||
family_descriptor: family_descriptor.clone(),
|
family_descriptor: family_descriptor.clone(),
|
||||||
};
|
};
|
||||||
|
|
||||||
self.font_template_cache.get(&cache_key).cloned().unwrap_or_else(|| {
|
if let Some(templates) = self.font_template_cache.read().get(&cache_key).cloned() {
|
||||||
debug!(
|
return templates;
|
||||||
"FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}",
|
}
|
||||||
descriptor_to_match,
|
|
||||||
family_descriptor
|
|
||||||
);
|
|
||||||
|
|
||||||
let template_info = self.font_source.find_matching_font_templates(
|
debug!(
|
||||||
descriptor_to_match,
|
"FontContext::font_template cache miss for template_descriptor={:?} family_descriptor={:?}",
|
||||||
family_descriptor.clone(),
|
descriptor_to_match,
|
||||||
);
|
family_descriptor
|
||||||
|
);
|
||||||
|
|
||||||
self.font_template_cache.insert(cache_key, template_info.clone());
|
let templates = self
|
||||||
template_info
|
.font_source
|
||||||
})
|
.lock()
|
||||||
|
.find_matching_font_templates(descriptor_to_match, family_descriptor.clone());
|
||||||
|
|
||||||
|
self.font_template_cache
|
||||||
|
.write()
|
||||||
|
.insert(cache_key, templates.clone());
|
||||||
|
templates
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the
|
/// Create a `Font` for use in layout calculations, from a `FontTemplateData` returned by the
|
||||||
/// cache thread and a `FontDescriptor` which contains the styling parameters.
|
/// cache thread and a `FontDescriptor` which contains the styling parameters.
|
||||||
fn create_font(
|
fn create_font(
|
||||||
&mut self,
|
&self,
|
||||||
font_template: FontTemplateRef,
|
font_template: FontTemplateRef,
|
||||||
font_descriptor: FontDescriptor,
|
font_descriptor: FontDescriptor,
|
||||||
synthesized_small_caps: Option<FontRef>,
|
synthesized_small_caps: Option<FontRef>,
|
||||||
|
@ -216,13 +216,13 @@ impl<S: FontSource> FontContext<S> {
|
||||||
font_descriptor.clone(),
|
font_descriptor.clone(),
|
||||||
synthesized_small_caps,
|
synthesized_small_caps,
|
||||||
)?;
|
)?;
|
||||||
font.font_key = self.font_source.get_font_instance(
|
font.font_key = self.font_source.lock().get_font_instance(
|
||||||
font_template.identifier(),
|
font_template.identifier(),
|
||||||
font_descriptor.pt_size,
|
font_descriptor.pt_size,
|
||||||
font.webrender_font_instance_flags(),
|
font.webrender_font_instance_flags(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Ok(Rc::new(RefCell::new(font)))
|
Ok(Arc::new(font))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -240,7 +240,7 @@ struct FontTemplateCacheKey {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct FontGroupCacheKey {
|
struct FontGroupCacheKey {
|
||||||
style: Arc<FontStyleStruct>,
|
style: ServoArc<FontStyleStruct>,
|
||||||
size: Au,
|
size: Au,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,11 +260,3 @@ impl Hash for FontGroupCacheKey {
|
||||||
self.style.hash.hash(hasher)
|
self.style.hash.hash(hasher)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
pub fn invalidate_font_caches() {
|
|
||||||
FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst);
|
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
CoreTextFontCache::clear_core_text_font_cache();
|
|
||||||
}
|
|
||||||
|
|
|
@ -2,12 +2,11 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::fmt::{Debug, Error, Formatter};
|
use std::fmt::{Debug, Error, Formatter};
|
||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use atomic_refcell::AtomicRefCell;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use servo_url::ServoUrl;
|
use servo_url::ServoUrl;
|
||||||
use style::computed_values::font_stretch::T as FontStretch;
|
use style::computed_values::font_stretch::T as FontStretch;
|
||||||
|
@ -22,7 +21,7 @@ use crate::platform::font::PlatformFont;
|
||||||
use crate::platform::font_list::LocalFontIdentifier;
|
use crate::platform::font_list::LocalFontIdentifier;
|
||||||
|
|
||||||
/// A reference to a [`FontTemplate`] with shared ownership and mutability.
|
/// A reference to a [`FontTemplate`] with shared ownership and mutability.
|
||||||
pub type FontTemplateRef = Rc<RefCell<FontTemplate>>;
|
pub type FontTemplateRef = Arc<AtomicRefCell<FontTemplate>>;
|
||||||
|
|
||||||
/// Describes how to select a font from a given family. This is very basic at the moment and needs
|
/// Describes how to select a font from a given family. This is very basic at the moment and needs
|
||||||
/// to be expanded or refactored when we support more of the font styling parameters.
|
/// to be expanded or refactored when we support more of the font styling parameters.
|
||||||
|
|
|
@ -9,14 +9,15 @@ use std::{mem, ptr};
|
||||||
|
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use freetype::freetype::{
|
use freetype::freetype::{
|
||||||
FT_Done_Face, FT_F26Dot6, FT_Face, FT_FaceRec, FT_Get_Char_Index, FT_Get_Kerning,
|
FT_Done_Face, FT_F26Dot6, FT_Face, FT_Get_Char_Index, FT_Get_Kerning, FT_Get_Sfnt_Table,
|
||||||
FT_Get_Sfnt_Table, FT_GlyphSlot, FT_Int32, FT_Kerning_Mode, FT_Load_Glyph, FT_Load_Sfnt_Table,
|
FT_GlyphSlot, FT_Int32, FT_Kerning_Mode, FT_Load_Glyph, FT_Load_Sfnt_Table, FT_Long,
|
||||||
FT_Long, FT_New_Memory_Face, FT_Set_Char_Size, FT_Sfnt_Tag, FT_SizeRec, FT_Size_Metrics,
|
FT_New_Memory_Face, FT_Set_Char_Size, FT_Sfnt_Tag, FT_SizeRec, FT_Size_Metrics, FT_UInt,
|
||||||
FT_UInt, FT_ULong, FT_Vector, FT_STYLE_FLAG_ITALIC,
|
FT_ULong, FT_Vector, FT_STYLE_FLAG_ITALIC,
|
||||||
};
|
};
|
||||||
use freetype::succeeded;
|
use freetype::succeeded;
|
||||||
use freetype::tt_os2::TT_OS2;
|
use freetype::tt_os2::TT_OS2;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
use parking_lot::ReentrantMutex;
|
||||||
use style::computed_values::font_stretch::T as FontStretch;
|
use style::computed_values::font_stretch::T as FontStretch;
|
||||||
use style::computed_values::font_weight::T as FontWeight;
|
use style::computed_values::font_weight::T as FontWeight;
|
||||||
use style::values::computed::font::FontStyle;
|
use style::values::computed::font::FontStyle;
|
||||||
|
@ -75,19 +76,26 @@ pub struct PlatformFont {
|
||||||
/// The font data itself, which must stay valid for the lifetime of the
|
/// The font data itself, which must stay valid for the lifetime of the
|
||||||
/// platform [`FT_Face`].
|
/// platform [`FT_Face`].
|
||||||
font_data: Arc<Vec<u8>>,
|
font_data: Arc<Vec<u8>>,
|
||||||
face: FT_Face,
|
face: ReentrantMutex<FT_Face>,
|
||||||
can_do_fast_shaping: bool,
|
can_do_fast_shaping: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FT_Face can be used in multiple threads, but from only one thread at a time.
|
||||||
|
// It's protected with a ReentrantMutex for PlatformFont.
|
||||||
|
// See https://freetype.org/freetype2/docs/reference/ft2-face_creation.html#ft_face.
|
||||||
|
unsafe impl Sync for PlatformFont {}
|
||||||
|
unsafe impl Send for PlatformFont {}
|
||||||
|
|
||||||
impl Drop for PlatformFont {
|
impl Drop for PlatformFont {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
assert!(!self.face.is_null());
|
let face = self.face.lock();
|
||||||
|
assert!(!face.is_null());
|
||||||
unsafe {
|
unsafe {
|
||||||
// The FreeType documentation says that both `FT_New_Face` and `FT_Done_Face`
|
// The FreeType documentation says that both `FT_New_Face` and `FT_Done_Face`
|
||||||
// should be protected by a mutex.
|
// should be protected by a mutex.
|
||||||
// See https://freetype.org/freetype2/docs/reference/ft2-library_setup.html.
|
// See https://freetype.org/freetype2/docs/reference/ft2-library_setup.html.
|
||||||
let _guard = FreeTypeLibraryHandle::get().lock();
|
let _guard = FreeTypeLibraryHandle::get().lock();
|
||||||
if !succeeded(FT_Done_Face(self.face)) {
|
if !succeeded(FT_Done_Face(*face)) {
|
||||||
panic!("FT_Done_Face failed");
|
panic!("FT_Done_Face failed");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -134,7 +142,7 @@ impl PlatformFontMethods for PlatformFont {
|
||||||
) -> Result<PlatformFont, &'static str> {
|
) -> Result<PlatformFont, &'static str> {
|
||||||
let face = create_face(data.clone(), face_index, pt_size)?;
|
let face = create_face(data.clone(), face_index, pt_size)?;
|
||||||
let mut handle = PlatformFont {
|
let mut handle = PlatformFont {
|
||||||
face,
|
face: ReentrantMutex::new(face),
|
||||||
font_data: data,
|
font_data: data,
|
||||||
can_do_fast_shaping: false,
|
can_do_fast_shaping: false,
|
||||||
};
|
};
|
||||||
|
@ -147,7 +155,8 @@ impl PlatformFontMethods for PlatformFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn descriptor(&self) -> FontTemplateDescriptor {
|
fn descriptor(&self) -> FontTemplateDescriptor {
|
||||||
let style = if unsafe { (*self.face).style_flags & FT_STYLE_FLAG_ITALIC as c_long != 0 } {
|
let face = self.face.lock();
|
||||||
|
let style = if unsafe { (**face).style_flags & FT_STYLE_FLAG_ITALIC as c_long != 0 } {
|
||||||
FontStyle::ITALIC
|
FontStyle::ITALIC
|
||||||
} else {
|
} else {
|
||||||
FontStyle::NORMAL
|
FontStyle::NORMAL
|
||||||
|
@ -178,9 +187,11 @@ impl PlatformFontMethods for PlatformFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
fn glyph_index(&self, codepoint: char) -> Option<GlyphId> {
|
||||||
assert!(!self.face.is_null());
|
let face = self.face.lock();
|
||||||
|
assert!(!face.is_null());
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let idx = FT_Get_Char_Index(self.face, codepoint as FT_ULong);
|
let idx = FT_Get_Char_Index(*face, codepoint as FT_ULong);
|
||||||
if idx != 0 as FT_UInt {
|
if idx != 0 as FT_UInt {
|
||||||
Some(idx as GlyphId)
|
Some(idx as GlyphId)
|
||||||
} else {
|
} else {
|
||||||
|
@ -194,11 +205,13 @@ impl PlatformFontMethods for PlatformFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
|
fn glyph_h_kerning(&self, first_glyph: GlyphId, second_glyph: GlyphId) -> FractionalPixel {
|
||||||
assert!(!self.face.is_null());
|
let face = self.face.lock();
|
||||||
|
assert!(!face.is_null());
|
||||||
|
|
||||||
let mut delta = FT_Vector { x: 0, y: 0 };
|
let mut delta = FT_Vector { x: 0, y: 0 };
|
||||||
unsafe {
|
unsafe {
|
||||||
FT_Get_Kerning(
|
FT_Get_Kerning(
|
||||||
self.face,
|
*face,
|
||||||
first_glyph,
|
first_glyph,
|
||||||
second_glyph,
|
second_glyph,
|
||||||
FT_Kerning_Mode::FT_KERNING_DEFAULT as FT_UInt,
|
FT_Kerning_Mode::FT_KERNING_DEFAULT as FT_UInt,
|
||||||
|
@ -213,11 +226,13 @@ impl PlatformFontMethods for PlatformFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
|
fn glyph_h_advance(&self, glyph: GlyphId) -> Option<FractionalPixel> {
|
||||||
assert!(!self.face.is_null());
|
let face = self.face.lock();
|
||||||
|
assert!(!face.is_null());
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let res = FT_Load_Glyph(self.face, glyph as FT_UInt, GLYPH_LOAD_FLAGS);
|
let res = FT_Load_Glyph(*face, glyph as FT_UInt, GLYPH_LOAD_FLAGS);
|
||||||
if succeeded(res) {
|
if succeeded(res) {
|
||||||
let void_glyph = (*self.face).glyph;
|
let void_glyph = (**face).glyph;
|
||||||
let slot: FT_GlyphSlot = void_glyph;
|
let slot: FT_GlyphSlot = void_glyph;
|
||||||
assert!(!slot.is_null());
|
assert!(!slot.is_null());
|
||||||
let advance = (*slot).metrics.horiAdvance;
|
let advance = (*slot).metrics.horiAdvance;
|
||||||
|
@ -232,8 +247,8 @@ impl PlatformFontMethods for PlatformFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metrics(&self) -> FontMetrics {
|
fn metrics(&self) -> FontMetrics {
|
||||||
/* TODO(Issue #76): complete me */
|
let face = self.face.lock();
|
||||||
let face = self.face_rec_mut();
|
let face = unsafe { **face };
|
||||||
|
|
||||||
let underline_size = self.font_units_to_au(face.underline_thickness as f64);
|
let underline_size = self.font_units_to_au(face.underline_thickness as f64);
|
||||||
let underline_offset = self.font_units_to_au(face.underline_position as f64);
|
let underline_offset = self.font_units_to_au(face.underline_position as f64);
|
||||||
|
@ -294,24 +309,19 @@ impl PlatformFontMethods for PlatformFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
|
fn table_for_tag(&self, tag: FontTableTag) -> Option<FontTable> {
|
||||||
|
let face = self.face.lock();
|
||||||
let tag = tag as FT_ULong;
|
let tag = tag as FT_ULong;
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
// Get the length
|
// Get the length
|
||||||
let mut len = 0;
|
let mut len = 0;
|
||||||
if !succeeded(FT_Load_Sfnt_Table(
|
if !succeeded(FT_Load_Sfnt_Table(*face, tag, 0, ptr::null_mut(), &mut len)) {
|
||||||
self.face,
|
|
||||||
tag,
|
|
||||||
0,
|
|
||||||
ptr::null_mut(),
|
|
||||||
&mut len,
|
|
||||||
)) {
|
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
// Get the bytes
|
// Get the bytes
|
||||||
let mut buf = vec![0u8; len as usize];
|
let mut buf = vec![0u8; len as usize];
|
||||||
if !succeeded(FT_Load_Sfnt_Table(
|
if !succeeded(FT_Load_Sfnt_Table(
|
||||||
self.face,
|
*face,
|
||||||
tag,
|
tag,
|
||||||
0,
|
0,
|
||||||
buf.as_mut_ptr(),
|
buf.as_mut_ptr(),
|
||||||
|
@ -343,9 +353,10 @@ impl<'a> PlatformFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn has_table(&self, tag: FontTableTag) -> bool {
|
fn has_table(&self, tag: FontTableTag) -> bool {
|
||||||
|
let face = self.face.lock();
|
||||||
unsafe {
|
unsafe {
|
||||||
succeeded(FT_Load_Sfnt_Table(
|
succeeded(FT_Load_Sfnt_Table(
|
||||||
self.face,
|
*face,
|
||||||
tag as FT_ULong,
|
tag as FT_ULong,
|
||||||
0,
|
0,
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
|
@ -354,13 +365,9 @@ impl<'a> PlatformFont {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::mut_from_ref)] // Intended for this function
|
|
||||||
fn face_rec_mut(&'a self) -> &'a mut FT_FaceRec {
|
|
||||||
unsafe { &mut (*self.face) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn font_units_to_au(&self, value: f64) -> Au {
|
fn font_units_to_au(&self, value: f64) -> Au {
|
||||||
let face = self.face_rec_mut();
|
let face = self.face.lock();
|
||||||
|
let face = unsafe { **face };
|
||||||
|
|
||||||
// face.size is a *c_void in the bindings, presumably to avoid
|
// face.size is a *c_void in the bindings, presumably to avoid
|
||||||
// recursive structural types
|
// recursive structural types
|
||||||
|
@ -377,9 +384,10 @@ impl<'a> PlatformFont {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn os2_table(&self) -> Option<OS2Table> {
|
fn os2_table(&self) -> Option<OS2Table> {
|
||||||
|
let face = self.face.lock();
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let os2 =
|
let os2 = FT_Get_Sfnt_Table(*face, FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
|
||||||
FT_Get_Sfnt_Table(self.face_rec_mut(), FT_Sfnt_Tag::FT_SFNT_OS2) as *mut TT_OS2;
|
|
||||||
let valid = !os2.is_null() && (*os2).version != 0xffff;
|
let valid = !os2.is_null() && (*os2).version != 0xffff;
|
||||||
|
|
||||||
if !valid {
|
if !valid {
|
||||||
|
|
|
@ -63,6 +63,17 @@ pub struct PlatformFont {
|
||||||
can_do_fast_shaping: bool,
|
can_do_fast_shaping: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// From https://developer.apple.com/documentation/coretext:
|
||||||
|
// > All individual functions in Core Text are thread-safe. Font objects (CTFont,
|
||||||
|
// > CTFontDescriptor, and associated objects) can be used simultaneously by multiple
|
||||||
|
// > operations, work queues, or threads. However, the layout objects (CTTypesetter,
|
||||||
|
// > CTFramesetter, CTRun, CTLine, CTFrame, and associated objects) should be used in a
|
||||||
|
// > single operation, work queue, or thread.
|
||||||
|
//
|
||||||
|
// The other element is a read-only CachedKernTable which is stored in a CFData.
|
||||||
|
unsafe impl Sync for PlatformFont {}
|
||||||
|
unsafe impl Send for PlatformFont {}
|
||||||
|
|
||||||
impl PlatformFont {
|
impl PlatformFont {
|
||||||
/// Cache all the data needed for basic horizontal kerning. This is used only as a fallback or
|
/// Cache all the data needed for basic horizontal kerning. This is used only as a fallback or
|
||||||
/// fast path (when the GPOS table is missing or unnecessary) so it needn't handle every case.
|
/// fast path (when the GPOS table is missing or unnecessary) so it needn't handle every case.
|
||||||
|
|
|
@ -82,6 +82,13 @@ pub struct PlatformFont {
|
||||||
scaled_du_to_px: f32,
|
scaled_du_to_px: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Based on information from the Skia codebase, it seems that DirectWrite APIs from
|
||||||
|
// Windows 10 and beyond are thread safe. If problems arise from this, we can protect the
|
||||||
|
// platform font with a Mutex.
|
||||||
|
// See https://source.chromium.org/chromium/chromium/src/+/main:third_party/skia/src/ports/SkScalerContext_win_dw.cpp;l=56;bpv=0;bpt=1.
|
||||||
|
unsafe impl Sync for PlatformFont {}
|
||||||
|
unsafe impl Send for PlatformFont {}
|
||||||
|
|
||||||
struct Nondebug<T>(T);
|
struct Nondebug<T>(T);
|
||||||
|
|
||||||
impl<T> fmt::Debug for Nondebug<T> {
|
impl<T> fmt::Debug for Nondebug<T> {
|
||||||
|
|
|
@ -150,22 +150,26 @@ fn font_family(names: Vec<&str>) -> FontFamily {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_font_group_is_cached_by_style() {
|
fn test_font_group_is_cached_by_style() {
|
||||||
let source = TestFontSource::new();
|
let source = TestFontSource::new();
|
||||||
let mut context = FontContext::new(source);
|
let context = FontContext::new(source);
|
||||||
|
|
||||||
let style1 = style();
|
let style1 = style();
|
||||||
|
|
||||||
let mut style2 = style();
|
let mut style2 = style();
|
||||||
style2.set_font_style(FontStyle::ITALIC);
|
style2.set_font_style(FontStyle::ITALIC);
|
||||||
|
|
||||||
assert_eq!(
|
assert!(
|
||||||
context.font_group(Arc::new(style1.clone())).as_ptr(),
|
std::ptr::eq(
|
||||||
context.font_group(Arc::new(style1.clone())).as_ptr(),
|
&*context.font_group(Arc::new(style1.clone())).read(),
|
||||||
|
&*context.font_group(Arc::new(style1.clone())).read()
|
||||||
|
),
|
||||||
"the same font group should be returned for two styles with the same hash"
|
"the same font group should be returned for two styles with the same hash"
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_ne!(
|
assert!(
|
||||||
context.font_group(Arc::new(style1.clone())).as_ptr(),
|
!std::ptr::eq(
|
||||||
context.font_group(Arc::new(style2.clone())).as_ptr(),
|
&*context.font_group(Arc::new(style1.clone())).read(),
|
||||||
|
&*context.font_group(Arc::new(style2.clone())).read()
|
||||||
|
),
|
||||||
"different font groups should be returned for two styles with different hashes"
|
"different font groups should be returned for two styles with different hashes"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -181,12 +185,9 @@ fn test_font_group_find_by_codepoint() {
|
||||||
|
|
||||||
let group = context.font_group(Arc::new(style));
|
let group = context.font_group(Arc::new(style));
|
||||||
|
|
||||||
let font = group
|
let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
|
||||||
.borrow_mut()
|
|
||||||
.find_by_codepoint(&mut context, 'a')
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
font.borrow().identifier(),
|
font.identifier(),
|
||||||
TestFontSource::identifier_for_font_name("csstest-ascii")
|
TestFontSource::identifier_for_font_name("csstest-ascii")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -195,12 +196,9 @@ fn test_font_group_find_by_codepoint() {
|
||||||
"only the first font in the list should have been loaded"
|
"only the first font in the list should have been loaded"
|
||||||
);
|
);
|
||||||
|
|
||||||
let font = group
|
let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
|
||||||
.borrow_mut()
|
|
||||||
.find_by_codepoint(&mut context, 'a')
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
font.borrow().identifier(),
|
font.identifier(),
|
||||||
TestFontSource::identifier_for_font_name("csstest-ascii")
|
TestFontSource::identifier_for_font_name("csstest-ascii")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
|
@ -209,12 +207,9 @@ fn test_font_group_find_by_codepoint() {
|
||||||
"we shouldn't load the same font a second time"
|
"we shouldn't load the same font a second time"
|
||||||
);
|
);
|
||||||
|
|
||||||
let font = group
|
let font = group.write().find_by_codepoint(&mut context, 'á').unwrap();
|
||||||
.borrow_mut()
|
|
||||||
.find_by_codepoint(&mut context, 'á')
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
font.borrow().identifier(),
|
font.identifier(),
|
||||||
TestFontSource::identifier_for_font_name("csstest-basic-regular")
|
TestFontSource::identifier_for_font_name("csstest-basic-regular")
|
||||||
);
|
);
|
||||||
assert_eq!(count.get(), 2, "both fonts should now have been loaded");
|
assert_eq!(count.get(), 2, "both fonts should now have been loaded");
|
||||||
|
@ -230,22 +225,16 @@ fn test_font_fallback() {
|
||||||
|
|
||||||
let group = context.font_group(Arc::new(style));
|
let group = context.font_group(Arc::new(style));
|
||||||
|
|
||||||
let font = group
|
let font = group.write().find_by_codepoint(&mut context, 'a').unwrap();
|
||||||
.borrow_mut()
|
|
||||||
.find_by_codepoint(&mut context, 'a')
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
font.borrow().identifier(),
|
font.identifier(),
|
||||||
TestFontSource::identifier_for_font_name("csstest-ascii"),
|
TestFontSource::identifier_for_font_name("csstest-ascii"),
|
||||||
"a family in the group should be used if there is a matching glyph"
|
"a family in the group should be used if there is a matching glyph"
|
||||||
);
|
);
|
||||||
|
|
||||||
let font = group
|
let font = group.write().find_by_codepoint(&mut context, 'á').unwrap();
|
||||||
.borrow_mut()
|
|
||||||
.find_by_codepoint(&mut context, 'á')
|
|
||||||
.unwrap();
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
font.borrow().identifier(),
|
font.identifier(),
|
||||||
TestFontSource::identifier_for_font_name("csstest-basic-regular"),
|
TestFontSource::identifier_for_font_name("csstest-basic-regular"),
|
||||||
"a fallback font should be used if there is no matching glyph in the group"
|
"a fallback font should be used if there is no matching glyph in the group"
|
||||||
);
|
);
|
||||||
|
@ -255,7 +244,7 @@ fn test_font_fallback() {
|
||||||
fn test_font_template_is_cached() {
|
fn test_font_template_is_cached() {
|
||||||
let source = TestFontSource::new();
|
let source = TestFontSource::new();
|
||||||
let count = source.find_font_count.clone();
|
let count = source.find_font_count.clone();
|
||||||
let mut context = FontContext::new(source);
|
let context = FontContext::new(source);
|
||||||
|
|
||||||
let mut font_descriptor = FontDescriptor {
|
let mut font_descriptor = FontDescriptor {
|
||||||
weight: FontWeight::normal(),
|
weight: FontWeight::normal(),
|
||||||
|
@ -280,8 +269,7 @@ fn test_font_template_is_cached() {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
assert_ne!(
|
assert_ne!(
|
||||||
font1.borrow().descriptor.pt_size,
|
font1.descriptor.pt_size, font2.descriptor.pt_size,
|
||||||
font2.borrow().descriptor.pt_size,
|
|
||||||
"the same font should not have been returned"
|
"the same font should not have been returned"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -130,6 +130,12 @@ pub struct Shaper {
|
||||||
font: *const Font,
|
font: *const Font,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The HarfBuzz API is thread safe as well as our `Font`, so we can make the data
|
||||||
|
// structures here as thread-safe as well. This doesn't seem to be documented,
|
||||||
|
// but was expressed as one of the original goals of the HarfBuzz API.
|
||||||
|
unsafe impl Sync for Shaper {}
|
||||||
|
unsafe impl Send for Shaper {}
|
||||||
|
|
||||||
impl Drop for Shaper {
|
impl Drop for Shaper {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
|
|
@ -16,7 +16,7 @@ use unicode_bidi as bidi;
|
||||||
use webrender_api::FontInstanceKey;
|
use webrender_api::FontInstanceKey;
|
||||||
use xi_unicode::LineBreakLeafIter;
|
use xi_unicode::LineBreakLeafIter;
|
||||||
|
|
||||||
use crate::font::{Font, FontMetrics, RunMetrics, ShapingFlags, ShapingOptions};
|
use crate::font::{FontMetrics, FontRef, RunMetrics, ShapingFlags, ShapingOptions};
|
||||||
use crate::text::glyph::{ByteIndex, GlyphStore};
|
use crate::text::glyph::{ByteIndex, GlyphStore};
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
|
@ -180,13 +180,14 @@ impl<'a> Iterator for CharacterSliceIterator<'a> {
|
||||||
impl<'a> TextRun {
|
impl<'a> TextRun {
|
||||||
/// Constructs a new text run. Also returns if there is a line break at the beginning
|
/// Constructs a new text run. Also returns if there is a line break at the beginning
|
||||||
pub fn new(
|
pub fn new(
|
||||||
font: &mut Font,
|
font: FontRef,
|
||||||
text: String,
|
text: String,
|
||||||
options: &ShapingOptions,
|
options: &ShapingOptions,
|
||||||
bidi_level: bidi::Level,
|
bidi_level: bidi::Level,
|
||||||
breaker: &mut Option<LineBreakLeafIter>,
|
breaker: &mut Option<LineBreakLeafIter>,
|
||||||
) -> (TextRun, bool) {
|
) -> (TextRun, bool) {
|
||||||
let (glyphs, break_at_zero) = TextRun::break_and_shape(font, &text, options, breaker);
|
let (glyphs, break_at_zero) =
|
||||||
|
TextRun::break_and_shape(font.clone(), &text, options, breaker);
|
||||||
(
|
(
|
||||||
TextRun {
|
TextRun {
|
||||||
text: Arc::new(text),
|
text: Arc::new(text),
|
||||||
|
@ -202,7 +203,7 @@ impl<'a> TextRun {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn break_and_shape(
|
pub fn break_and_shape(
|
||||||
font: &mut Font,
|
font: FontRef,
|
||||||
text: &str,
|
text: &str,
|
||||||
options: &ShapingOptions,
|
options: &ShapingOptions,
|
||||||
breaker: &mut Option<LineBreakLeafIter>,
|
breaker: &mut Option<LineBreakLeafIter>,
|
||||||
|
|
|
@ -42,7 +42,7 @@ use style::values::generics::counters::ContentItem;
|
||||||
use style::LocalName;
|
use style::LocalName;
|
||||||
|
|
||||||
use crate::block::BlockFlow;
|
use crate::block::BlockFlow;
|
||||||
use crate::context::{with_thread_local_font_context, LayoutContext};
|
use crate::context::LayoutContext;
|
||||||
use crate::data::{InnerLayoutData, LayoutDataFlags};
|
use crate::data::{InnerLayoutData, LayoutDataFlags};
|
||||||
use crate::display_list::items::OpaqueNode;
|
use crate::display_list::items::OpaqueNode;
|
||||||
use crate::flex::FlexFlow;
|
use crate::flex::FlexFlow;
|
||||||
|
@ -517,11 +517,10 @@ where
|
||||||
// We must scan for runs before computing minimum ascent and descent because scanning
|
// We must scan for runs before computing minimum ascent and descent because scanning
|
||||||
// for runs might collapse so much whitespace away that only hypothetical fragments
|
// for runs might collapse so much whitespace away that only hypothetical fragments
|
||||||
// remain. In that case the inline flow will compute its ascent and descent to be zero.
|
// remain. In that case the inline flow will compute its ascent and descent to be zero.
|
||||||
let scanned_fragments =
|
let scanned_fragments = TextRunScanner::new().scan_for_runs(
|
||||||
with_thread_local_font_context(self.layout_context, |font_context| {
|
&self.layout_context.font_context,
|
||||||
TextRunScanner::new()
|
mem::take(&mut fragments.fragments),
|
||||||
.scan_for_runs(font_context, mem::take(&mut fragments.fragments))
|
);
|
||||||
});
|
|
||||||
let mut inline_flow_ref = FlowRef::new(Arc::new(InlineFlow::from_fragments(
|
let mut inline_flow_ref = FlowRef::new(Arc::new(InlineFlow::from_fragments(
|
||||||
scanned_fragments,
|
scanned_fragments,
|
||||||
node.style(self.style_context()).writing_mode,
|
node.style(self.style_context()).writing_mode,
|
||||||
|
@ -550,11 +549,10 @@ where
|
||||||
{
|
{
|
||||||
// FIXME(#6503): Use Arc::get_mut().unwrap() here.
|
// FIXME(#6503): Use Arc::get_mut().unwrap() here.
|
||||||
let inline_flow = FlowRef::deref_mut(&mut inline_flow_ref).as_mut_inline();
|
let inline_flow = FlowRef::deref_mut(&mut inline_flow_ref).as_mut_inline();
|
||||||
inline_flow.minimum_line_metrics =
|
inline_flow.minimum_line_metrics = inline_flow.minimum_line_metrics(
|
||||||
with_thread_local_font_context(self.layout_context, |font_context| {
|
&self.layout_context.font_context,
|
||||||
inline_flow
|
&node.style(self.style_context()),
|
||||||
.minimum_line_metrics(font_context, &node.style(self.style_context()))
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline_flow_ref.finish();
|
inline_flow_ref.finish();
|
||||||
|
@ -1545,11 +1543,10 @@ where
|
||||||
)),
|
)),
|
||||||
self.layout_context,
|
self.layout_context,
|
||||||
));
|
));
|
||||||
let marker_fragments =
|
let marker_fragments = TextRunScanner::new().scan_for_runs(
|
||||||
with_thread_local_font_context(self.layout_context, |font_context| {
|
&self.layout_context.font_context,
|
||||||
TextRunScanner::new()
|
unscanned_marker_fragments,
|
||||||
.scan_for_runs(font_context, unscanned_marker_fragments)
|
);
|
||||||
});
|
|
||||||
marker_fragments.fragments
|
marker_fragments.fragments
|
||||||
},
|
},
|
||||||
ListStyleTypeContent::GeneratedContent(info) => vec![Fragment::new(
|
ListStyleTypeContent::GeneratedContent(info) => vec![Fragment::new(
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
|
|
||||||
//! Data needed by layout.
|
//! Data needed by layout.
|
||||||
|
|
||||||
use std::cell::{RefCell, RefMut};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::hash::BuildHasherDefault;
|
use std::hash::BuildHasherDefault;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
@ -13,7 +12,6 @@ use std::thread;
|
||||||
use fnv::FnvHasher;
|
use fnv::FnvHasher;
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use gfx::font_context::FontContext;
|
use gfx::font_context::FontContext;
|
||||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
|
||||||
use msg::constellation_msg::PipelineId;
|
use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::image_cache::{
|
use net_traits::image_cache::{
|
||||||
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
|
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
|
||||||
|
@ -29,32 +27,6 @@ use crate::display_list::items::{OpaqueNode, WebRenderImageInfo};
|
||||||
|
|
||||||
pub type LayoutFontContext = FontContext<FontCacheThread>;
|
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 LayoutFontContext) -> R,
|
|
||||||
{
|
|
||||||
FONT_CONTEXT_KEY.with(|k| {
|
|
||||||
let mut font_context = k.borrow_mut();
|
|
||||||
if font_context.is_none() {
|
|
||||||
let font_cache_thread = layout_context.font_cache_thread.lock().unwrap().clone();
|
|
||||||
*font_context = Some(FontContext::new(font_cache_thread));
|
|
||||||
}
|
|
||||||
f(&mut RefMut::map(font_context, |x| x.as_mut().unwrap()))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn malloc_size_of_persistent_local_context(ops: &mut MallocSizeOfOps) -> usize {
|
|
||||||
FONT_CONTEXT_KEY.with(|r| {
|
|
||||||
if let Some(ref context) = *r.borrow() {
|
|
||||||
context.size_of(ops)
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
type WebrenderImageCache =
|
type WebrenderImageCache =
|
||||||
HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault<FnvHasher>>;
|
HashMap<(ServoUrl, UsePlaceholder), WebRenderImageInfo, BuildHasherDefault<FnvHasher>>;
|
||||||
|
|
||||||
|
@ -72,8 +44,8 @@ pub struct LayoutContext<'a> {
|
||||||
/// Reference to the script thread image cache.
|
/// Reference to the script thread image cache.
|
||||||
pub image_cache: Arc<dyn ImageCache>,
|
pub image_cache: Arc<dyn ImageCache>,
|
||||||
|
|
||||||
/// Interface to the font cache thread.
|
/// A FontContext to be used during layout.
|
||||||
pub font_cache_thread: Mutex<FontCacheThread>,
|
pub font_context: Arc<FontContext<FontCacheThread>>,
|
||||||
|
|
||||||
/// A cache of WebRender image info.
|
/// A cache of WebRender image info.
|
||||||
pub webrender_image_cache: Arc<RwLock<WebrenderImageCache>>,
|
pub webrender_image_cache: Arc<RwLock<WebrenderImageCache>>,
|
||||||
|
|
|
@ -57,7 +57,7 @@ use style::values::generics::transform;
|
||||||
use webrender_api::units::LayoutTransform;
|
use webrender_api::units::LayoutTransform;
|
||||||
use webrender_api::{self, ImageKey};
|
use webrender_api::{self, ImageKey};
|
||||||
|
|
||||||
use crate::context::{with_thread_local_font_context, LayoutContext};
|
use crate::context::LayoutContext;
|
||||||
use crate::display_list::items::{ClipScrollNodeIndex, OpaqueNode, BLUR_INFLATION_FACTOR};
|
use crate::display_list::items::{ClipScrollNodeIndex, OpaqueNode, BLUR_INFLATION_FACTOR};
|
||||||
use crate::display_list::ToLayout;
|
use crate::display_list::ToLayout;
|
||||||
use crate::floats::ClearType;
|
use crate::floats::ClearType;
|
||||||
|
@ -852,9 +852,8 @@ impl Fragment {
|
||||||
))),
|
))),
|
||||||
);
|
);
|
||||||
unscanned_ellipsis_fragments.push_back(ellipsis_fragment);
|
unscanned_ellipsis_fragments.push_back(ellipsis_fragment);
|
||||||
let ellipsis_fragments = with_thread_local_font_context(layout_context, |font_context| {
|
let ellipsis_fragments = TextRunScanner::new()
|
||||||
TextRunScanner::new().scan_for_runs(font_context, unscanned_ellipsis_fragments)
|
.scan_for_runs(&layout_context.font_context, unscanned_ellipsis_fragments);
|
||||||
});
|
|
||||||
debug_assert_eq!(ellipsis_fragments.len(), 1);
|
debug_assert_eq!(ellipsis_fragments.len(), 1);
|
||||||
ellipsis_fragment = ellipsis_fragments.fragments.into_iter().next().unwrap();
|
ellipsis_fragment = ellipsis_fragments.fragments.into_iter().next().unwrap();
|
||||||
ellipsis_fragment.flags |= FragmentFlags::IS_ELLIPSIS;
|
ellipsis_fragment.flags |= FragmentFlags::IS_ELLIPSIS;
|
||||||
|
@ -2346,9 +2345,10 @@ impl Fragment {
|
||||||
return InlineMetrics::new(Au(0), Au(0), Au(0));
|
return InlineMetrics::new(Au(0), Au(0), Au(0));
|
||||||
}
|
}
|
||||||
// See CSS 2.1 § 10.8.1.
|
// See CSS 2.1 § 10.8.1.
|
||||||
let font_metrics = with_thread_local_font_context(layout_context, |font_context| {
|
let font_metrics = text::font_metrics_for_style(
|
||||||
text::font_metrics_for_style(font_context, self_.style.clone_font())
|
&layout_context.font_context,
|
||||||
});
|
self_.style.clone_font(),
|
||||||
|
);
|
||||||
let line_height = text::line_height_from_style(&self_.style, &font_metrics);
|
let line_height = text::line_height_from_style(&self_.style, &font_metrics);
|
||||||
InlineMetrics::from_font_metrics(&info.run.font_metrics, line_height)
|
InlineMetrics::from_font_metrics(&info.run.font_metrics, line_height)
|
||||||
}
|
}
|
||||||
|
@ -2426,10 +2426,10 @@ impl Fragment {
|
||||||
VerticalAlign::Keyword(kw) => match kw {
|
VerticalAlign::Keyword(kw) => match kw {
|
||||||
VerticalAlignKeyword::Baseline => {},
|
VerticalAlignKeyword::Baseline => {},
|
||||||
VerticalAlignKeyword::Middle => {
|
VerticalAlignKeyword::Middle => {
|
||||||
let font_metrics =
|
let font_metrics = text::font_metrics_for_style(
|
||||||
with_thread_local_font_context(layout_context, |font_context| {
|
&layout_context.font_context,
|
||||||
text::font_metrics_for_style(font_context, self.style.clone_font())
|
self.style.clone_font(),
|
||||||
});
|
);
|
||||||
offset += (content_inline_metrics.ascent -
|
offset += (content_inline_metrics.ascent -
|
||||||
content_inline_metrics.space_below_baseline -
|
content_inline_metrics.space_below_baseline -
|
||||||
font_metrics.x_height)
|
font_metrics.x_height)
|
||||||
|
|
|
@ -20,7 +20,7 @@ use style::servo::restyle_damage::ServoRestyleDamage;
|
||||||
use style::values::generics::counters::ContentItem;
|
use style::values::generics::counters::ContentItem;
|
||||||
use style::values::specified::list::{QuotePair, Quotes};
|
use style::values::specified::list::{QuotePair, Quotes};
|
||||||
|
|
||||||
use crate::context::{with_thread_local_font_context, LayoutContext};
|
use crate::context::LayoutContext;
|
||||||
use crate::display_list::items::OpaqueNode;
|
use crate::display_list::items::OpaqueNode;
|
||||||
use crate::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils};
|
use crate::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils};
|
||||||
use crate::fragment::{
|
use crate::fragment::{
|
||||||
|
@ -493,9 +493,7 @@ fn render_text(
|
||||||
));
|
));
|
||||||
// FIXME(pcwalton): This should properly handle multiple marker fragments. This could happen
|
// FIXME(pcwalton): This should properly handle multiple marker fragments. This could happen
|
||||||
// due to text run splitting.
|
// due to text run splitting.
|
||||||
let fragments = with_thread_local_font_context(layout_context, |font_context| {
|
let fragments = TextRunScanner::new().scan_for_runs(&layout_context.font_context, fragments);
|
||||||
TextRunScanner::new().scan_for_runs(font_context, fragments)
|
|
||||||
});
|
|
||||||
if fragments.is_empty() {
|
if fragments.is_empty() {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1239,7 +1239,7 @@ impl InlineFlow {
|
||||||
/// `style` is the style of the block.
|
/// `style` is the style of the block.
|
||||||
pub fn minimum_line_metrics(
|
pub fn minimum_line_metrics(
|
||||||
&self,
|
&self,
|
||||||
font_context: &mut LayoutFontContext,
|
font_context: &LayoutFontContext,
|
||||||
style: &ComputedValues,
|
style: &ComputedValues,
|
||||||
) -> LineMetrics {
|
) -> LineMetrics {
|
||||||
InlineFlow::minimum_line_metrics_for_fragments(
|
InlineFlow::minimum_line_metrics_for_fragments(
|
||||||
|
@ -1255,7 +1255,7 @@ impl InlineFlow {
|
||||||
/// `style` is the style of the block that these fragments belong to.
|
/// `style` is the style of the block that these fragments belong to.
|
||||||
pub fn minimum_line_metrics_for_fragments(
|
pub fn minimum_line_metrics_for_fragments(
|
||||||
fragments: &[Fragment],
|
fragments: &[Fragment],
|
||||||
font_context: &mut LayoutFontContext,
|
font_context: &LayoutFontContext,
|
||||||
style: &ComputedValues,
|
style: &ComputedValues,
|
||||||
) -> LineMetrics {
|
) -> LineMetrics {
|
||||||
// As a special case, if this flow contains only hypothetical fragments, then the entire
|
// As a special case, if this flow contains only hypothetical fragments, then the entire
|
||||||
|
|
|
@ -14,7 +14,7 @@ use style::properties::ComputedValues;
|
||||||
use style::servo::restyle_damage::ServoRestyleDamage;
|
use style::servo::restyle_damage::ServoRestyleDamage;
|
||||||
|
|
||||||
use crate::block::BlockFlow;
|
use crate::block::BlockFlow;
|
||||||
use crate::context::{with_thread_local_font_context, LayoutContext};
|
use crate::context::LayoutContext;
|
||||||
use crate::display_list::items::DisplayListSection;
|
use crate::display_list::items::DisplayListSection;
|
||||||
use crate::display_list::{
|
use crate::display_list::{
|
||||||
BorderPaintingMode, DisplayListBuildState, StackingContextCollectionState,
|
BorderPaintingMode, DisplayListBuildState, StackingContextCollectionState,
|
||||||
|
@ -114,13 +114,11 @@ impl ListItemFlow {
|
||||||
|
|
||||||
fn assign_marker_block_sizes(&mut self, layout_context: &LayoutContext) {
|
fn assign_marker_block_sizes(&mut self, layout_context: &LayoutContext) {
|
||||||
// FIXME(pcwalton): Do this during flow construction, like `InlineFlow` does?
|
// FIXME(pcwalton): Do this during flow construction, like `InlineFlow` does?
|
||||||
let marker_line_metrics = with_thread_local_font_context(layout_context, |font_context| {
|
let marker_line_metrics = InlineFlow::minimum_line_metrics_for_fragments(
|
||||||
InlineFlow::minimum_line_metrics_for_fragments(
|
&self.marker_fragments,
|
||||||
&self.marker_fragments,
|
&layout_context.font_context,
|
||||||
font_context,
|
&self.block_flow.fragment.style,
|
||||||
&self.block_flow.fragment.style,
|
);
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
for marker in &mut self.marker_fragments {
|
for marker in &mut self.marker_fragments {
|
||||||
marker.assign_replaced_block_size_if_necessary();
|
marker.assign_replaced_block_size_if_necessary();
|
||||||
|
|
|
@ -70,7 +70,7 @@ impl TextRunScanner {
|
||||||
|
|
||||||
pub fn scan_for_runs(
|
pub fn scan_for_runs(
|
||||||
&mut self,
|
&mut self,
|
||||||
font_context: &mut LayoutFontContext,
|
font_context: &LayoutFontContext,
|
||||||
mut fragments: LinkedList<Fragment>,
|
mut fragments: LinkedList<Fragment>,
|
||||||
) -> InlineFragments {
|
) -> InlineFragments {
|
||||||
debug!(
|
debug!(
|
||||||
|
@ -150,7 +150,7 @@ impl TextRunScanner {
|
||||||
/// be adjusted.
|
/// be adjusted.
|
||||||
fn flush_clump_to_list(
|
fn flush_clump_to_list(
|
||||||
&mut self,
|
&mut self,
|
||||||
font_context: &mut LayoutFontContext,
|
font_context: &LayoutFontContext,
|
||||||
out_fragments: &mut Vec<Fragment>,
|
out_fragments: &mut Vec<Fragment>,
|
||||||
paragraph_bytes_processed: &mut usize,
|
paragraph_bytes_processed: &mut usize,
|
||||||
bidi_levels: Option<&[bidi::Level]>,
|
bidi_levels: Option<&[bidi::Level]>,
|
||||||
|
@ -203,10 +203,9 @@ impl TextRunScanner {
|
||||||
.map(|l| l.into())
|
.map(|l| l.into())
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
let space_width = font_group
|
let space_width = font_group
|
||||||
.borrow_mut()
|
.write()
|
||||||
.find_by_codepoint(font_context, ' ')
|
.find_by_codepoint(font_context, ' ')
|
||||||
.and_then(|font| {
|
.and_then(|font| {
|
||||||
let font = font.borrow();
|
|
||||||
font.glyph_index(' ')
|
font.glyph_index(' ')
|
||||||
.map(|glyph_id| font.glyph_h_advance(glyph_id))
|
.map(|glyph_id| font.glyph_h_advance(glyph_id))
|
||||||
})
|
})
|
||||||
|
@ -248,7 +247,7 @@ impl TextRunScanner {
|
||||||
for (byte_index, character) in text.char_indices() {
|
for (byte_index, character) in text.char_indices() {
|
||||||
if !character.is_control() {
|
if !character.is_control() {
|
||||||
let font = font_group
|
let font = font_group
|
||||||
.borrow_mut()
|
.write()
|
||||||
.find_by_codepoint(font_context, character);
|
.find_by_codepoint(font_context, character);
|
||||||
|
|
||||||
let bidi_level = match bidi_levels {
|
let bidi_level = match bidi_levels {
|
||||||
|
@ -367,7 +366,7 @@ impl TextRunScanner {
|
||||||
// If no font is found (including fallbacks), there's no way we can render.
|
// If no font is found (including fallbacks), there's no way we can render.
|
||||||
let font = match run_info
|
let font = match run_info
|
||||||
.font
|
.font
|
||||||
.or_else(|| font_group.borrow_mut().first(font_context))
|
.or_else(|| font_group.write().first(font_context))
|
||||||
{
|
{
|
||||||
Some(font) => font,
|
Some(font) => font,
|
||||||
None => {
|
None => {
|
||||||
|
@ -377,7 +376,7 @@ impl TextRunScanner {
|
||||||
};
|
};
|
||||||
|
|
||||||
let (run, break_at_zero) = TextRun::new(
|
let (run, break_at_zero) = TextRun::new(
|
||||||
&mut font.borrow_mut(),
|
font,
|
||||||
run_info.text,
|
run_info.text,
|
||||||
&options,
|
&options,
|
||||||
run_info.bidi_level,
|
run_info.bidi_level,
|
||||||
|
@ -535,14 +534,12 @@ fn bounding_box_for_run_metrics(
|
||||||
/// Panics if no font can be found for the given font style.
|
/// Panics if no font can be found for the given font style.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn font_metrics_for_style(
|
pub fn font_metrics_for_style(
|
||||||
font_context: &mut LayoutFontContext,
|
font_context: &LayoutFontContext,
|
||||||
style: crate::ServoArc<FontStyleStruct>,
|
style: crate::ServoArc<FontStyleStruct>,
|
||||||
) -> FontMetrics {
|
) -> FontMetrics {
|
||||||
let font_group = font_context.font_group(style);
|
let font_group = font_context.font_group(style);
|
||||||
let font = font_group.borrow_mut().first(font_context);
|
let font = font_group.write().first(font_context);
|
||||||
let font = font.as_ref().unwrap().borrow();
|
font.as_ref().unwrap().metrics.clone()
|
||||||
|
|
||||||
font.metrics.clone()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the line block-size needed by the given computed style and font size.
|
/// Returns the line block-size needed by the given computed style and font size.
|
||||||
|
@ -664,10 +661,8 @@ impl RunInfo {
|
||||||
|
|
||||||
fn has_font(&self, font: &Option<FontRef>) -> bool {
|
fn has_font(&self, font: &Option<FontRef>) -> bool {
|
||||||
fn identifier_and_pt_size(font: &Option<FontRef>) -> Option<(FontIdentifier, Au)> {
|
fn identifier_and_pt_size(font: &Option<FontRef>) -> Option<(FontIdentifier, Au)> {
|
||||||
font.as_ref().map(|font| {
|
font.as_ref()
|
||||||
let font = font.borrow();
|
.map(|font| (font.identifier().clone(), font.descriptor.pt_size))
|
||||||
(font.identifier().clone(), font.descriptor.pt_size)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
identifier_and_pt_size(&self.font) == identifier_and_pt_size(font)
|
identifier_and_pt_size(&self.font) == identifier_and_pt_size(font)
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
|
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
|
@ -12,7 +11,7 @@ use msg::constellation_msg::PipelineId;
|
||||||
use net_traits::image_cache::{
|
use net_traits::image_cache::{
|
||||||
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
|
ImageCache, ImageCacheResult, ImageOrMetadataAvailable, UsePlaceholder,
|
||||||
};
|
};
|
||||||
use parking_lot::{ReentrantMutex, RwLock};
|
use parking_lot::RwLock;
|
||||||
use script_layout_interface::{PendingImage, PendingImageState};
|
use script_layout_interface::{PendingImage, PendingImageState};
|
||||||
use servo_url::{ImmutableOrigin, ServoUrl};
|
use servo_url::{ImmutableOrigin, ServoUrl};
|
||||||
use style::context::SharedStyleContext;
|
use style::context::SharedStyleContext;
|
||||||
|
@ -20,8 +19,6 @@ use style::dom::OpaqueNode;
|
||||||
|
|
||||||
use crate::display_list::WebRenderImageInfo;
|
use crate::display_list::WebRenderImageInfo;
|
||||||
|
|
||||||
thread_local!(static FONT_CONTEXT: RefCell<Option<FontContext<FontCacheThread>>> = RefCell::new(None));
|
|
||||||
|
|
||||||
pub struct LayoutContext<'a> {
|
pub struct LayoutContext<'a> {
|
||||||
pub id: PipelineId,
|
pub id: PipelineId,
|
||||||
pub use_rayon: bool,
|
pub use_rayon: bool,
|
||||||
|
@ -31,7 +28,7 @@ pub struct LayoutContext<'a> {
|
||||||
pub style_context: SharedStyleContext<'a>,
|
pub style_context: SharedStyleContext<'a>,
|
||||||
|
|
||||||
/// A FontContext to be used during layout.
|
/// A FontContext to be used during layout.
|
||||||
pub font_cache_thread: Arc<ReentrantMutex<FontCacheThread>>,
|
pub font_context: Arc<FontContext<FontCacheThread>>,
|
||||||
|
|
||||||
/// Reference to the script thread image cache.
|
/// Reference to the script thread image cache.
|
||||||
pub image_cache: Arc<dyn ImageCache>,
|
pub image_cache: Arc<dyn ImageCache>,
|
||||||
|
@ -133,27 +130,4 @@ impl<'a> LayoutContext<'a> {
|
||||||
None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None,
|
None | Some(ImageOrMetadataAvailable::MetadataAvailable(_)) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_font_context<F, R>(&self, callback: F) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut FontContext<FontCacheThread>) -> R,
|
|
||||||
{
|
|
||||||
with_thread_local_font_context(&self.font_cache_thread, callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn with_thread_local_font_context<F, R>(
|
|
||||||
font_cache_thread: &ReentrantMutex<FontCacheThread>,
|
|
||||||
callback: F,
|
|
||||||
) -> R
|
|
||||||
where
|
|
||||||
F: FnOnce(&mut FontContext<FontCacheThread>) -> R,
|
|
||||||
{
|
|
||||||
FONT_CONTEXT.with(|font_context| {
|
|
||||||
callback(
|
|
||||||
font_context
|
|
||||||
.borrow_mut()
|
|
||||||
.get_or_insert_with(|| FontContext::new(font_cache_thread.lock().clone())),
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1638,10 +1638,9 @@ impl InlineFormattingContext {
|
||||||
|
|
||||||
// It's unfortunate that it isn't possible to get this during IFC text processing, but in
|
// It's unfortunate that it isn't possible to get this during IFC text processing, but in
|
||||||
// that situation the style of the containing block is unknown.
|
// that situation the style of the containing block is unknown.
|
||||||
let default_font_metrics = layout_context.with_font_context(|font_context| {
|
let default_font_metrics =
|
||||||
get_font_for_first_font_for_style(style, font_context)
|
get_font_for_first_font_for_style(style, &layout_context.font_context)
|
||||||
.map(|font| font.borrow().metrics.clone())
|
.map(|font| font.metrics.clone());
|
||||||
});
|
|
||||||
|
|
||||||
let style_text = containing_block.style.get_inherited_text();
|
let style_text = containing_block.style.get_inherited_text();
|
||||||
let mut ifc = InlineFormattingContextState {
|
let mut ifc = InlineFormattingContextState {
|
||||||
|
@ -1768,34 +1767,30 @@ impl InlineFormattingContext {
|
||||||
// For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
|
// For the purposes of `text-transform: capitalize` the start of the IFC is a word boundary.
|
||||||
let mut on_word_boundary = true;
|
let mut on_word_boundary = true;
|
||||||
|
|
||||||
layout_context.with_font_context(|font_context| {
|
let mut linebreaker = None;
|
||||||
let mut linebreaker = None;
|
self.foreach(|iter_item| match iter_item {
|
||||||
self.foreach(|iter_item| match iter_item {
|
InlineFormattingContextIterItem::Item(InlineLevelBox::TextRun(ref mut text_run)) => {
|
||||||
InlineFormattingContextIterItem::Item(InlineLevelBox::TextRun(
|
text_run.break_and_shape(
|
||||||
ref mut text_run,
|
&layout_context.font_context,
|
||||||
)) => {
|
&mut linebreaker,
|
||||||
text_run.break_and_shape(
|
&mut ifc_fonts,
|
||||||
font_context,
|
&mut last_inline_box_ended_with_white_space,
|
||||||
&mut linebreaker,
|
&mut on_word_boundary,
|
||||||
&mut ifc_fonts,
|
);
|
||||||
&mut last_inline_box_ended_with_white_space,
|
},
|
||||||
&mut on_word_boundary,
|
InlineFormattingContextIterItem::Item(InlineLevelBox::InlineBox(inline_box)) => {
|
||||||
);
|
if let Some(font) = get_font_for_first_font_for_style(
|
||||||
},
|
&inline_box.style,
|
||||||
InlineFormattingContextIterItem::Item(InlineLevelBox::InlineBox(inline_box)) => {
|
&layout_context.font_context,
|
||||||
if let Some(font) =
|
) {
|
||||||
get_font_for_first_font_for_style(&inline_box.style, font_context)
|
inline_box.default_font_index = Some(add_or_get_font(&font, &mut ifc_fonts));
|
||||||
{
|
}
|
||||||
inline_box.default_font_index =
|
},
|
||||||
Some(add_or_get_font(&font, &mut ifc_fonts));
|
InlineFormattingContextIterItem::Item(InlineLevelBox::Atomic(_)) => {
|
||||||
}
|
last_inline_box_ended_with_white_space = false;
|
||||||
},
|
on_word_boundary = true;
|
||||||
InlineFormattingContextIterItem::Item(InlineLevelBox::Atomic(_)) => {
|
},
|
||||||
last_inline_box_ended_with_white_space = false;
|
_ => {},
|
||||||
on_word_boundary = true;
|
|
||||||
},
|
|
||||||
_ => {},
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
self.font_metrics = ifc_fonts;
|
self.font_metrics = ifc_fonts;
|
||||||
|
|
|
@ -111,7 +111,7 @@ impl TextRunSegment {
|
||||||
/// compatible with this segment or false otherwise.
|
/// compatible with this segment or false otherwise.
|
||||||
fn update_if_compatible(
|
fn update_if_compatible(
|
||||||
&mut self,
|
&mut self,
|
||||||
font: &FontRef,
|
new_font: &FontRef,
|
||||||
script: Script,
|
script: Script,
|
||||||
fonts: &[FontKeyAndMetrics],
|
fonts: &[FontKeyAndMetrics],
|
||||||
) -> bool {
|
) -> bool {
|
||||||
|
@ -120,7 +120,6 @@ impl TextRunSegment {
|
||||||
}
|
}
|
||||||
|
|
||||||
let current_font_key_and_metrics = &fonts[self.font_index];
|
let current_font_key_and_metrics = &fonts[self.font_index];
|
||||||
let new_font = font.borrow();
|
|
||||||
if new_font.font_key != current_font_key_and_metrics.key ||
|
if new_font.font_key != current_font_key_and_metrics.key ||
|
||||||
new_font.descriptor.pt_size != current_font_key_and_metrics.pt_size
|
new_font.descriptor.pt_size != current_font_key_and_metrics.pt_size
|
||||||
{
|
{
|
||||||
|
@ -208,7 +207,7 @@ impl TextRun {
|
||||||
|
|
||||||
pub(super) fn break_and_shape(
|
pub(super) fn break_and_shape(
|
||||||
&mut self,
|
&mut self,
|
||||||
font_context: &mut FontContext<FontCacheThread>,
|
font_context: &FontContext<FontCacheThread>,
|
||||||
linebreaker: &mut Option<LineBreakLeafIter>,
|
linebreaker: &mut Option<LineBreakLeafIter>,
|
||||||
font_cache: &mut Vec<FontKeyAndMetrics>,
|
font_cache: &mut Vec<FontKeyAndMetrics>,
|
||||||
last_inline_box_ended_with_collapsible_white_space: &mut bool,
|
last_inline_box_ended_with_collapsible_white_space: &mut bool,
|
||||||
|
@ -244,7 +243,6 @@ impl TextRun {
|
||||||
let segments = segment_results
|
let segments = segment_results
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(mut segment, font)| {
|
.map(|(mut segment, font)| {
|
||||||
let mut font = font.borrow_mut();
|
|
||||||
let word_spacing = style_word_spacing.unwrap_or_else(|| {
|
let word_spacing = style_word_spacing.unwrap_or_else(|| {
|
||||||
let space_width = font
|
let space_width = font
|
||||||
.glyph_index(' ')
|
.glyph_index(' ')
|
||||||
|
@ -260,7 +258,7 @@ impl TextRun {
|
||||||
};
|
};
|
||||||
(segment.runs, segment.break_at_start) =
|
(segment.runs, segment.break_at_start) =
|
||||||
gfx::text::text_run::TextRun::break_and_shape(
|
gfx::text::text_run::TextRun::break_and_shape(
|
||||||
&mut font,
|
font,
|
||||||
&self.text
|
&self.text
|
||||||
[segment.range.begin().0 as usize..segment.range.end().0 as usize],
|
[segment.range.begin().0 as usize..segment.range.end().0 as usize],
|
||||||
&shaping_options,
|
&shaping_options,
|
||||||
|
@ -280,7 +278,7 @@ impl TextRun {
|
||||||
/// [`super::InlineFormattingContext`].
|
/// [`super::InlineFormattingContext`].
|
||||||
fn segment_text(
|
fn segment_text(
|
||||||
&mut self,
|
&mut self,
|
||||||
font_context: &mut FontContext<FontCacheThread>,
|
font_context: &FontContext<FontCacheThread>,
|
||||||
font_cache: &mut Vec<FontKeyAndMetrics>,
|
font_cache: &mut Vec<FontKeyAndMetrics>,
|
||||||
last_inline_box_ended_with_collapsible_white_space: &mut bool,
|
last_inline_box_ended_with_collapsible_white_space: &mut bool,
|
||||||
on_word_boundary: &mut bool,
|
on_word_boundary: &mut bool,
|
||||||
|
@ -341,7 +339,7 @@ impl TextRun {
|
||||||
}
|
}
|
||||||
|
|
||||||
let font = match font_group
|
let font = match font_group
|
||||||
.borrow_mut()
|
.write()
|
||||||
.find_by_codepoint(font_context, character)
|
.find_by_codepoint(font_context, character)
|
||||||
{
|
{
|
||||||
Some(font) => font,
|
Some(font) => font,
|
||||||
|
@ -383,7 +381,7 @@ impl TextRun {
|
||||||
// Either we have a current segment or we only had control character and whitespace. In both
|
// Either we have a current segment or we only had control character and whitespace. In both
|
||||||
// of those cases, just use the first font.
|
// of those cases, just use the first font.
|
||||||
if current.is_none() {
|
if current.is_none() {
|
||||||
current = font_group.borrow_mut().first(font_context).map(|font| {
|
current = font_group.write().first(font_context).map(|font| {
|
||||||
let font_index = add_or_get_font(&font, font_cache);
|
let font_index = add_or_get_font(&font, font_cache);
|
||||||
(
|
(
|
||||||
TextRunSegment::new(font_index, Script::Common, ByteIndex(0)),
|
TextRunSegment::new(font_index, Script::Common, ByteIndex(0)),
|
||||||
|
@ -489,7 +487,6 @@ fn char_does_not_change_font(character: char) -> bool {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetrics>) -> usize {
|
pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetrics>) -> usize {
|
||||||
let font = font.borrow();
|
|
||||||
for (index, ifc_font_info) in ifc_fonts.iter().enumerate() {
|
for (index, ifc_font_info) in ifc_fonts.iter().enumerate() {
|
||||||
if ifc_font_info.key == font.font_key && ifc_font_info.pt_size == font.descriptor.pt_size {
|
if ifc_font_info.key == font.font_key && ifc_font_info.pt_size == font.descriptor.pt_size {
|
||||||
return index;
|
return index;
|
||||||
|
@ -505,11 +502,11 @@ pub(super) fn add_or_get_font(font: &FontRef, ifc_fonts: &mut Vec<FontKeyAndMetr
|
||||||
|
|
||||||
pub(super) fn get_font_for_first_font_for_style(
|
pub(super) fn get_font_for_first_font_for_style(
|
||||||
style: &ComputedValues,
|
style: &ComputedValues,
|
||||||
font_context: &mut FontContext<FontCacheThread>,
|
font_context: &FontContext<FontCacheThread>,
|
||||||
) -> Option<FontRef> {
|
) -> Option<FontRef> {
|
||||||
let font = font_context
|
let font = font_context
|
||||||
.font_group(style.clone_font())
|
.font_group(style.clone_font())
|
||||||
.borrow_mut()
|
.write()
|
||||||
.first(font_context);
|
.first(font_context);
|
||||||
if font.is_none() {
|
if font.is_none() {
|
||||||
warn!("Could not find font for style: {:?}", style.clone_font());
|
warn!("Could not find font for style: {:?}", style.clone_font());
|
||||||
|
|
|
@ -22,16 +22,15 @@ use euclid::default::{Point2D as UntypedPoint2D, Rect as UntypedRect, Size2D as
|
||||||
use euclid::{Point2D, Rect, Scale, Size2D};
|
use euclid::{Point2D, Rect, Scale, Size2D};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use fxhash::{FxHashMap, FxHashSet};
|
use fxhash::{FxHashMap, FxHashSet};
|
||||||
|
use gfx::font;
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use gfx::{font, font_context};
|
use gfx::font_context::FontContext;
|
||||||
use gfx_traits::{node_id_from_scroll_id, Epoch};
|
use gfx_traits::{node_id_from_scroll_id, Epoch};
|
||||||
use histogram::Histogram;
|
use histogram::Histogram;
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use layout::construct::ConstructionResult;
|
use layout::construct::ConstructionResult;
|
||||||
use layout::context::{
|
use layout::context::{LayoutContext, RegisteredPainter, RegisteredPainters};
|
||||||
malloc_size_of_persistent_local_context, LayoutContext, RegisteredPainter, RegisteredPainters,
|
|
||||||
};
|
|
||||||
use layout::display_list::items::{DisplayList, ScrollOffsetMap, WebRenderImageInfo};
|
use layout::display_list::items::{DisplayList, ScrollOffsetMap, WebRenderImageInfo};
|
||||||
use layout::display_list::{IndexableText, ToLayout};
|
use layout::display_list::{IndexableText, ToLayout};
|
||||||
use layout::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils};
|
use layout::flow::{Flow, FlowFlags, GetBaseFlow, ImmutableFlowUtils, MutableOwnedFlowUtils};
|
||||||
|
@ -136,6 +135,9 @@ pub struct LayoutThread {
|
||||||
/// Public interface to the font cache thread.
|
/// Public interface to the font cache thread.
|
||||||
font_cache_thread: FontCacheThread,
|
font_cache_thread: FontCacheThread,
|
||||||
|
|
||||||
|
/// A FontContext to be used during layout.
|
||||||
|
font_context: Arc<FontContext<FontCacheThread>>,
|
||||||
|
|
||||||
/// Is this the first reflow in this LayoutThread?
|
/// Is this the first reflow in this LayoutThread?
|
||||||
first_reflow: Cell<bool>,
|
first_reflow: Cell<bool>,
|
||||||
|
|
||||||
|
@ -513,7 +515,7 @@ impl Layout for LayoutThread {
|
||||||
// malloc_enclosing_size_of function.
|
// malloc_enclosing_size_of function.
|
||||||
let mut ops = MallocSizeOfOps::new(servo_allocator::usable_size, None, None);
|
let mut ops = MallocSizeOfOps::new(servo_allocator::usable_size, None, None);
|
||||||
|
|
||||||
// FIXME(njn): Just measuring the display tree for now.
|
// TODO: Measure more than just display list, stylist, and font context.
|
||||||
let display_list = self.display_list.borrow();
|
let display_list = self.display_list.borrow();
|
||||||
let display_list_ref = display_list.as_ref();
|
let display_list_ref = display_list.as_ref();
|
||||||
let formatted_url = &format!("url({})", self.url);
|
let formatted_url = &format!("url({})", self.url);
|
||||||
|
@ -528,13 +530,6 @@ impl Layout for LayoutThread {
|
||||||
kind: ReportKind::ExplicitJemallocHeapSize,
|
kind: ReportKind::ExplicitJemallocHeapSize,
|
||||||
size: self.stylist.size_of(&mut ops),
|
size: self.stylist.size_of(&mut ops),
|
||||||
});
|
});
|
||||||
|
|
||||||
// The LayoutThread has data in Persistent TLS...
|
|
||||||
reports.push(Report {
|
|
||||||
path: path![formatted_url, "layout-thread", "local-context"],
|
|
||||||
kind: ReportKind::ExplicitJemallocHeapSize,
|
|
||||||
size: malloc_size_of_persistent_local_context(&mut ops),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reflow(&mut self, script_reflow: script_layout_interface::ScriptReflow) {
|
fn reflow(&mut self, script_reflow: script_layout_interface::ScriptReflow) {
|
||||||
|
@ -573,6 +568,7 @@ impl LayoutThread {
|
||||||
// Let webrender know about this pipeline by sending an empty display list.
|
// Let webrender know about this pipeline by sending an empty display list.
|
||||||
webrender_api.send_initial_transaction(id.into());
|
webrender_api.send_initial_transaction(id.into());
|
||||||
|
|
||||||
|
let font_context = Arc::new(FontContext::new(font_cache_thread.clone()));
|
||||||
let device = Device::new(
|
let device = Device::new(
|
||||||
MediaType::screen(),
|
MediaType::screen(),
|
||||||
QuirksMode::NoQuirks,
|
QuirksMode::NoQuirks,
|
||||||
|
@ -602,6 +598,7 @@ impl LayoutThread {
|
||||||
registered_painters: RegisteredPaintersImpl(Default::default()),
|
registered_painters: RegisteredPaintersImpl(Default::default()),
|
||||||
image_cache,
|
image_cache,
|
||||||
font_cache_thread,
|
font_cache_thread,
|
||||||
|
font_context,
|
||||||
first_reflow: Cell::new(true),
|
first_reflow: Cell::new(true),
|
||||||
font_cache_sender: ipc_font_cache_sender,
|
font_cache_sender: ipc_font_cache_sender,
|
||||||
parallel_flag: true,
|
parallel_flag: true,
|
||||||
|
@ -675,7 +672,7 @@ impl LayoutThread {
|
||||||
traversal_flags,
|
traversal_flags,
|
||||||
),
|
),
|
||||||
image_cache: self.image_cache.clone(),
|
image_cache: self.image_cache.clone(),
|
||||||
font_cache_thread: Mutex::new(self.font_cache_thread.clone()),
|
font_context: self.font_context.clone(),
|
||||||
webrender_image_cache: self.webrender_image_cache.clone(),
|
webrender_image_cache: self.webrender_image_cache.clone(),
|
||||||
pending_images: Mutex::new(vec![]),
|
pending_images: Mutex::new(vec![]),
|
||||||
registered_painters: &self.registered_painters,
|
registered_painters: &self.registered_painters,
|
||||||
|
@ -726,7 +723,7 @@ impl LayoutThread {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_web_font_loaded(&self) {
|
fn handle_web_font_loaded(&self) {
|
||||||
font_context::invalidate_font_caches();
|
self.font_context.invalidate_caches();
|
||||||
self.script_chan
|
self.script_chan
|
||||||
.send(ConstellationControlMsg::WebFontLoaded(self.id))
|
.send(ConstellationControlMsg::WebFontLoaded(self.id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
|
@ -22,7 +22,7 @@ use euclid::{Point2D, Scale, Size2D, Vector2D};
|
||||||
use fnv::FnvHashMap;
|
use fnv::FnvHashMap;
|
||||||
use fxhash::FxHashMap;
|
use fxhash::FxHashMap;
|
||||||
use gfx::font_cache_thread::FontCacheThread;
|
use gfx::font_cache_thread::FontCacheThread;
|
||||||
use gfx::font_context;
|
use gfx::font_context::FontContext;
|
||||||
use gfx_traits::{node_id_from_scroll_id, Epoch};
|
use gfx_traits::{node_id_from_scroll_id, Epoch};
|
||||||
use ipc_channel::ipc::{self, IpcSender};
|
use ipc_channel::ipc::{self, IpcSender};
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
|
@ -41,7 +41,7 @@ use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||||
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
|
use metrics::{PaintTimeMetrics, ProfilerMetadataFactory};
|
||||||
use msg::constellation_msg::{BrowsingContextId, PipelineId};
|
use msg::constellation_msg::{BrowsingContextId, PipelineId};
|
||||||
use net_traits::image_cache::{ImageCache, UsePlaceholder};
|
use net_traits::image_cache::{ImageCache, UsePlaceholder};
|
||||||
use parking_lot::{ReentrantMutex, RwLock};
|
use parking_lot::RwLock;
|
||||||
use profile_traits::mem::{Report, ReportKind};
|
use profile_traits::mem::{Report, ReportKind};
|
||||||
use profile_traits::path;
|
use profile_traits::path;
|
||||||
use profile_traits::time::{
|
use profile_traits::time::{
|
||||||
|
@ -121,7 +121,10 @@ pub struct LayoutThread {
|
||||||
|
|
||||||
/// Public interface to the font cache thread. This needs to be behind a [`ReentrantMutex`],
|
/// Public interface to the font cache thread. This needs to be behind a [`ReentrantMutex`],
|
||||||
/// because some font cache operations can trigger others.
|
/// because some font cache operations can trigger others.
|
||||||
font_cache_thread: Arc<ReentrantMutex<FontCacheThread>>,
|
font_cache_thread: FontCacheThread,
|
||||||
|
|
||||||
|
/// A FontContext to be used during layout.
|
||||||
|
font_context: Arc<FontContext<FontCacheThread>>,
|
||||||
|
|
||||||
/// Is this the first reflow in this LayoutThread?
|
/// Is this the first reflow in this LayoutThread?
|
||||||
first_reflow: Cell<bool>,
|
first_reflow: Cell<bool>,
|
||||||
|
@ -484,13 +487,13 @@ impl LayoutThread {
|
||||||
|
|
||||||
// The device pixel ratio is incorrect (it does not have the hidpi value),
|
// The device pixel ratio is incorrect (it does not have the hidpi value),
|
||||||
// but it will be set correctly when the initial reflow takes place.
|
// but it will be set correctly when the initial reflow takes place.
|
||||||
let font_cache_thread = Arc::new(ReentrantMutex::new(font_cache_thread));
|
let font_context = Arc::new(FontContext::new(font_cache_thread.clone()));
|
||||||
let device = Device::new(
|
let device = Device::new(
|
||||||
MediaType::screen(),
|
MediaType::screen(),
|
||||||
QuirksMode::NoQuirks,
|
QuirksMode::NoQuirks,
|
||||||
window_size.initial_viewport,
|
window_size.initial_viewport,
|
||||||
window_size.device_pixel_ratio,
|
window_size.device_pixel_ratio,
|
||||||
Box::new(LayoutFontMetricsProvider(font_cache_thread.clone())),
|
Box::new(LayoutFontMetricsProvider(font_context.clone())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Ask the router to proxy IPC messages from the font cache thread to layout.
|
// Ask the router to proxy IPC messages from the font cache thread to layout.
|
||||||
|
@ -514,6 +517,7 @@ impl LayoutThread {
|
||||||
registered_painters: RegisteredPaintersImpl(Default::default()),
|
registered_painters: RegisteredPaintersImpl(Default::default()),
|
||||||
image_cache,
|
image_cache,
|
||||||
font_cache_thread,
|
font_cache_thread,
|
||||||
|
font_context,
|
||||||
first_reflow: Cell::new(true),
|
first_reflow: Cell::new(true),
|
||||||
font_cache_sender: ipc_font_cache_sender,
|
font_cache_sender: ipc_font_cache_sender,
|
||||||
generation: Cell::new(0),
|
generation: Cell::new(0),
|
||||||
|
@ -584,7 +588,7 @@ impl LayoutThread {
|
||||||
traversal_flags,
|
traversal_flags,
|
||||||
),
|
),
|
||||||
image_cache: self.image_cache.clone(),
|
image_cache: self.image_cache.clone(),
|
||||||
font_cache_thread: self.font_cache_thread.clone(),
|
font_context: self.font_context.clone(),
|
||||||
webrender_image_cache: self.webrender_image_cache.clone(),
|
webrender_image_cache: self.webrender_image_cache.clone(),
|
||||||
pending_images: Mutex::new(vec![]),
|
pending_images: Mutex::new(vec![]),
|
||||||
use_rayon,
|
use_rayon,
|
||||||
|
@ -616,10 +620,8 @@ impl LayoutThread {
|
||||||
// Find all font-face rules and notify the font cache of them.
|
// Find all font-face rules and notify the font cache of them.
|
||||||
// GWTODO: Need to handle unloading web fonts.
|
// GWTODO: Need to handle unloading web fonts.
|
||||||
if stylesheet.is_effective_for_device(self.stylist.device(), guard) {
|
if stylesheet.is_effective_for_device(self.stylist.device(), guard) {
|
||||||
let newly_loading_font_count = self
|
let newly_loading_font_count =
|
||||||
.font_cache_thread
|
self.font_cache_thread.add_all_web_fonts_from_stylesheet(
|
||||||
.lock()
|
|
||||||
.add_all_web_fonts_from_stylesheet(
|
|
||||||
stylesheet,
|
stylesheet,
|
||||||
guard,
|
guard,
|
||||||
self.stylist.device(),
|
self.stylist.device(),
|
||||||
|
@ -637,7 +639,7 @@ impl LayoutThread {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_web_font_loaded(&self) {
|
fn handle_web_font_loaded(&self) {
|
||||||
font_context::invalidate_font_caches();
|
self.font_context.invalidate_caches();
|
||||||
self.script_chan
|
self.script_chan
|
||||||
.send(ConstellationControlMsg::WebFontLoaded(self.id))
|
.send(ConstellationControlMsg::WebFontLoaded(self.id))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
@ -1076,7 +1078,7 @@ impl LayoutThread {
|
||||||
self.stylist.quirks_mode(),
|
self.stylist.quirks_mode(),
|
||||||
window_size_data.initial_viewport,
|
window_size_data.initial_viewport,
|
||||||
window_size_data.device_pixel_ratio,
|
window_size_data.device_pixel_ratio,
|
||||||
Box::new(LayoutFontMetricsProvider(self.font_cache_thread.clone())),
|
Box::new(LayoutFontMetricsProvider(self.font_context.clone())),
|
||||||
);
|
);
|
||||||
|
|
||||||
// Preserve any previously computed root font size.
|
// Preserve any previously computed root font size.
|
||||||
|
@ -1235,7 +1237,7 @@ impl RegisteredSpeculativePainters for RegisteredPaintersImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct LayoutFontMetricsProvider(Arc<ReentrantMutex<FontCacheThread>>);
|
struct LayoutFontMetricsProvider(Arc<FontContext<FontCacheThread>>);
|
||||||
|
|
||||||
impl FontMetricsProvider for LayoutFontMetricsProvider {
|
impl FontMetricsProvider for LayoutFontMetricsProvider {
|
||||||
fn query_font_metrics(
|
fn query_font_metrics(
|
||||||
|
@ -1246,55 +1248,55 @@ impl FontMetricsProvider for LayoutFontMetricsProvider {
|
||||||
_in_media_query: bool,
|
_in_media_query: bool,
|
||||||
_retrieve_math_scales: bool,
|
_retrieve_math_scales: bool,
|
||||||
) -> FontMetrics {
|
) -> FontMetrics {
|
||||||
layout::context::with_thread_local_font_context(&self.0, move |font_context| {
|
let font_context = &self.0;
|
||||||
let font_group =
|
let font_group = self
|
||||||
font_context.font_group_with_size(ServoArc::new(font.clone()), base_size.into());
|
.0
|
||||||
let Some(first_font_metrics) = font_group
|
.font_group_with_size(ServoArc::new(font.clone()), base_size.into());
|
||||||
.borrow_mut()
|
|
||||||
.first(font_context)
|
|
||||||
.map(|font| font.borrow().metrics.clone())
|
|
||||||
else {
|
|
||||||
return Default::default();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only use the x-height of this font if it is non-zero. Some fonts return
|
let Some(first_font_metrics) = font_group
|
||||||
// inaccurate metrics, which shouldn't be used.
|
.write()
|
||||||
let x_height = Some(first_font_metrics.x_height)
|
.first(font_context)
|
||||||
.filter(|x_height| !x_height.is_zero())
|
.map(|font| font.metrics.clone())
|
||||||
.map(CSSPixelLength::from);
|
else {
|
||||||
|
return Default::default();
|
||||||
|
};
|
||||||
|
|
||||||
let zero_advance_measure = first_font_metrics
|
// Only use the x-height of this font if it is non-zero. Some fonts return
|
||||||
.zero_horizontal_advance
|
// inaccurate metrics, which shouldn't be used.
|
||||||
.or_else(|| {
|
let x_height = Some(first_font_metrics.x_height)
|
||||||
font_group
|
.filter(|x_height| !x_height.is_zero())
|
||||||
.borrow_mut()
|
.map(CSSPixelLength::from);
|
||||||
.find_by_codepoint(font_context, '0')?
|
|
||||||
.borrow()
|
|
||||||
.metrics
|
|
||||||
.zero_horizontal_advance
|
|
||||||
})
|
|
||||||
.map(CSSPixelLength::from);
|
|
||||||
let ic_width = first_font_metrics
|
|
||||||
.ic_horizontal_advance
|
|
||||||
.or_else(|| {
|
|
||||||
font_group
|
|
||||||
.borrow_mut()
|
|
||||||
.find_by_codepoint(font_context, '\u{6C34}')?
|
|
||||||
.borrow()
|
|
||||||
.metrics
|
|
||||||
.ic_horizontal_advance
|
|
||||||
})
|
|
||||||
.map(CSSPixelLength::from);
|
|
||||||
|
|
||||||
FontMetrics {
|
let zero_advance_measure = first_font_metrics
|
||||||
x_height,
|
.zero_horizontal_advance
|
||||||
zero_advance_measure,
|
.or_else(|| {
|
||||||
cap_height: None,
|
font_group
|
||||||
ic_width,
|
.write()
|
||||||
ascent: first_font_metrics.ascent.into(),
|
.find_by_codepoint(font_context, '0')?
|
||||||
script_percent_scale_down: None,
|
.metrics
|
||||||
script_script_percent_scale_down: None,
|
.zero_horizontal_advance
|
||||||
}
|
})
|
||||||
})
|
.map(CSSPixelLength::from);
|
||||||
|
|
||||||
|
let ic_width = first_font_metrics
|
||||||
|
.ic_horizontal_advance
|
||||||
|
.or_else(|| {
|
||||||
|
font_group
|
||||||
|
.write()
|
||||||
|
.find_by_codepoint(font_context, '\u{6C34}')?
|
||||||
|
.metrics
|
||||||
|
.ic_horizontal_advance
|
||||||
|
})
|
||||||
|
.map(CSSPixelLength::from);
|
||||||
|
|
||||||
|
FontMetrics {
|
||||||
|
x_height,
|
||||||
|
zero_advance_measure,
|
||||||
|
cap_height: None,
|
||||||
|
ic_width,
|
||||||
|
ascent: first_font_metrics.ascent.into(),
|
||||||
|
script_percent_scale_down: None,
|
||||||
|
script_script_percent_scale_down: None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue