diff options
author | Martin Robinson <mrobinson@igalia.com> | 2024-04-16 19:50:50 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-04-16 17:50:50 +0000 |
commit | 6b2fa91357ea289d03e206018389c43ffd836047 (patch) | |
tree | 164367ab35aa156bd944f58a1e6e9f9fa9a967b0 /components | |
parent | 689c14471430bb331ff0d46d5be7e16b81a1de54 (diff) | |
download | servo-6b2fa91357ea289d03e206018389c43ffd836047.tar.gz servo-6b2fa91357ea289d03e206018389c43ffd836047.zip |
gfx: Remove `FontTemplateData` (#32034)
Now that `FontTemplateData` is more or less the same on all platforms,
it can be removed. This is a preparatory change for a full refactor of
the font system on Servo. The major changes here are:
- Remove `FontTemplateData` and move its members into `FontTemplate`
- Make `FontTemplate` have full interior mutability instead of only
the `FontTemplateData` member. This is preparation for having these
data types `Send` and `Sync` with locking.
- Remove the strong/weak reference concept for font data. In practice,
all font data references were strong, so this was never fully
complete. Instead of using this approach, the new font system will
use a central font data cache with references associated to layouts.
- The `CTFont` cache is now a global cache, so `CTFont`s can be shared
between threads. The cache is cleared when clearing font caches.
A benefit of this change (apart from `CTFont` sharing) is that font data
loading is platform-independent now.
Diffstat (limited to 'components')
-rw-r--r-- | components/canvas/canvas_data.rs | 3 | ||||
-rw-r--r-- | components/gfx/font.rs | 14 | ||||
-rw-r--r-- | components/gfx/font_cache_thread.rs | 145 | ||||
-rw-r--r-- | components/gfx/font_context.rs | 5 | ||||
-rw-r--r-- | components/gfx/font_template.rs | 187 | ||||
-rw-r--r-- | components/gfx/platform/freetype/android/font_list.rs | 23 | ||||
-rw-r--r-- | components/gfx/platform/freetype/font.rs | 30 | ||||
-rw-r--r-- | components/gfx/platform/freetype/font_list.rs | 23 | ||||
-rw-r--r-- | components/gfx/platform/freetype/font_template.rs | 93 | ||||
-rw-r--r-- | components/gfx/platform/macos/font.rs | 50 | ||||
-rw-r--r-- | components/gfx/platform/macos/font_list.rs | 23 | ||||
-rw-r--r-- | components/gfx/platform/macos/font_template.rs | 205 | ||||
-rw-r--r-- | components/gfx/platform/mod.rs | 6 | ||||
-rw-r--r-- | components/gfx/platform/windows/font.rs | 26 | ||||
-rw-r--r-- | components/gfx/platform/windows/font_list.rs | 29 | ||||
-rw-r--r-- | components/gfx/platform/windows/font_template.rs | 101 | ||||
-rw-r--r-- | components/gfx/tests/font_context.rs | 13 | ||||
-rw-r--r-- | components/gfx/tests/font_template.rs | 7 | ||||
-rw-r--r-- | components/gfx/text/text_run.rs | 7 |
19 files changed, 392 insertions, 598 deletions
diff --git a/components/canvas/canvas_data.rs b/components/canvas/canvas_data.rs index 85e25d8dfb3..8b62a183733 100644 --- a/components/canvas/canvas_data.rs +++ b/components/canvas/canvas_data.rs @@ -17,6 +17,7 @@ use font_kit::source::SystemSource; use gfx::font::FontHandleMethods; use gfx::font_cache_thread::FontCacheThread; use gfx::font_context::FontContext; +use gfx::font_template::FontTemplateRefMethods; use ipc_channel::ipc::{IpcSender, IpcSharedMemory}; use log::{debug, error, warn}; use num_traits::ToPrimitive; @@ -501,7 +502,7 @@ impl<'a> CanvasData<'a> { .first(font_context) .expect("couldn't find font"); let font = font.borrow_mut(); - Font::from_bytes(font.handle.template().bytes(), 0) + Font::from_bytes(font.handle.template().data(), 0) .ok() .or_else(|| load_system_font_from_style(Some(style))) }) diff --git a/components/gfx/font.rs b/components/gfx/font.rs index f01f916d4ba..ad1ef24e313 100644 --- a/components/gfx/font.rs +++ b/components/gfx/font.rs @@ -26,10 +26,9 @@ use webrender_api::FontInstanceKey; use crate::font_cache_thread::FontIdentifier; use crate::font_context::{FontContext, FontSource}; -use crate::font_template::FontTemplateDescriptor; +use crate::font_template::{FontTemplateDescriptor, FontTemplateRef}; use crate::platform::font::{FontHandle, FontTable}; pub use crate::platform::font_list::fallback_font_families; -use crate::platform::font_template::FontTemplateData; use crate::text::glyph::{ByteIndex, GlyphData, GlyphId, GlyphStore}; use crate::text::shaping::ShaperMethods; use crate::text::Shaper; @@ -55,11 +54,11 @@ static TEXT_SHAPING_PERFORMANCE_COUNTER: AtomicUsize = AtomicUsize::new(0); pub trait FontHandleMethods: Sized { fn new_from_template( - template: Arc<FontTemplateData>, + template: FontTemplateRef, pt_size: Option<Au>, ) -> Result<Self, &'static str>; - fn template(&self) -> Arc<FontTemplateData>; + fn template(&self) -> FontTemplateRef; fn family_name(&self) -> Option<String>; fn face_name(&self) -> Option<String>; @@ -75,9 +74,6 @@ pub trait FontHandleMethods: Sized { fn can_do_fast_shaping(&self) -> bool; fn metrics(&self) -> FontMetrics; fn table_for_tag(&self, _: FontTableTag) -> Option<FontTable>; - - /// A unique identifier for the font, allowing comparison. - fn identifier(&self) -> &FontIdentifier; } // Used to abstract over the shaper's choice of fixed int representation. @@ -201,8 +197,8 @@ impl Font { } /// A unique identifier for the font, allowing comparison. - pub fn identifier(&self) -> &FontIdentifier { - self.handle.identifier() + pub fn identifier(&self) -> FontIdentifier { + self.handle.template().borrow().identifier.clone() } } diff --git a/components/gfx/font_cache_thread.rs b/components/gfx/font_cache_thread.rs index a5c6a2b0974..797a12317d6 100644 --- a/components/gfx/font_cache_thread.rs +++ b/components/gfx/font_cache_thread.rs @@ -3,8 +3,10 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::borrow::ToOwned; +use std::cell::RefCell; use std::collections::HashMap; use std::ops::Deref; +use std::rc::Rc; use std::sync::{Arc, Mutex}; use std::{f32, fmt, mem, thread}; @@ -25,22 +27,23 @@ use webrender_api::{FontInstanceKey, FontKey}; use crate::font::{FontFamilyDescriptor, FontFamilyName, FontSearchScope}; use crate::font_context::FontSource; -use crate::font_template::{FontTemplate, FontTemplateDescriptor}; +use crate::font_template::{ + FontTemplate, FontTemplateDescriptor, FontTemplateRef, FontTemplateRefMethods, +}; use crate::platform::font_list::{ for_each_available_family, for_each_variation, system_default_family, LocalFontIdentifier, SANS_SERIF_FONT_FAMILY, }; -use crate::platform::font_template::FontTemplateData; /// A list of font templates that make up a given font family. #[derive(Default)] pub struct FontTemplates { - templates: Vec<FontTemplate>, + templates: Vec<FontTemplateRef>, } -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug)] pub struct FontTemplateInfo { - pub font_template: Arc<FontTemplateData>, + pub font_template: FontTemplateRef, pub font_key: FontKey, } @@ -59,13 +62,19 @@ pub enum FontIdentifier { #[derive(Debug, Deserialize, Serialize)] pub struct SerializedFontTemplate { identifier: FontIdentifier, + descriptor: Option<FontTemplateDescriptor>, bytes_receiver: ipc_channel::ipc::IpcBytesReceiver, } impl SerializedFontTemplate { - pub fn to_font_template_data(&self) -> FontTemplateData { + pub fn to_font_template(&self) -> FontTemplate { let font_data = self.bytes_receiver.recv().ok(); - FontTemplateData::new(self.identifier.clone(), font_data).unwrap() + FontTemplate { + identifier: self.identifier.clone(), + descriptor: self.descriptor.clone(), + data: font_data.map(Arc::new), + is_valid: true, + } } } @@ -74,40 +83,37 @@ impl FontTemplates { pub fn find_font_for_style( &mut self, desc: &FontTemplateDescriptor, - ) -> Option<Arc<FontTemplateData>> { + ) -> Option<FontTemplateRef> { // TODO(Issue #189): optimize lookup for // regular/bold/italic/bolditalic with fixed offsets and a // static decision table for fallback between these values. for template in &mut self.templates { - let maybe_template = template.data_for_descriptor(desc); - if maybe_template.is_some() { - return maybe_template; + if template.descriptor_matches(desc) { + return Some(template.clone()); } } // We didn't find an exact match. Do more expensive fuzzy matching. // TODO(#190): Do a better job. - let (mut best_template_data, mut best_distance) = (None, f32::MAX); - for template in &mut self.templates { - if let Some((template_data, distance)) = template.data_for_approximate_descriptor(desc) - { + let (mut best_template, mut best_distance) = (None, f32::MAX); + for template in self.templates.iter() { + if let Some(distance) = template.descriptor_distance(desc) { if distance < best_distance { - best_template_data = Some(template_data); + best_template = Some(template); best_distance = distance } } } - if best_template_data.is_some() { - return best_template_data; + if best_template.is_some() { + return best_template.cloned(); } // If a request is made for a font family that exists, // pick the first valid font in the family if we failed // to find an exact match for the descriptor. - for template in &mut self.templates { - let maybe_template = template.get(); - if maybe_template.is_some() { - return maybe_template; + for template in &mut self.templates.iter() { + if template.is_valid() { + return Some(template.clone()); } } @@ -116,13 +122,13 @@ impl FontTemplates { pub fn add_template(&mut self, identifier: FontIdentifier, maybe_data: Option<Vec<u8>>) { for template in &self.templates { - if *template.identifier() == identifier { + if *template.borrow().identifier() == identifier { return; } } if let Ok(template) = FontTemplate::new(identifier, maybe_data) { - self.templates.push(template); + self.templates.push(Rc::new(RefCell::new(template))); } } } @@ -196,29 +202,30 @@ impl FontCache { match msg { Command::GetFontTemplate(template_descriptor, family_descriptor, result) => { - let maybe_font_template = - self.find_font_template(&template_descriptor, &family_descriptor); - match maybe_font_template { - None => { - let _ = result.send(Reply::GetFontTemplateReply(None)); - }, - Some(font_template_info) => { - let (bytes_sender, bytes_receiver) = - ipc::bytes_channel().expect("failed to create IPC channel"); - let serialized_font_template = SerializedFontTemplate { - identifier: font_template_info.font_template.identifier.clone(), - bytes_receiver, - }; + let Some(font_template_info) = + self.find_font_template(&template_descriptor, &family_descriptor) + else { + let _ = result.send(Reply::GetFontTemplateReply(None)); + continue; + }; - let _ = result.send(Reply::GetFontTemplateReply(Some( - SerializedFontTemplateInfo { - serialized_font_template, - font_key: font_template_info.font_key, - }, - ))); - let _ = bytes_sender.send(&font_template_info.font_template.bytes()); - }, + let (bytes_sender, bytes_receiver) = + ipc::bytes_channel().expect("failed to create IPC channel"); + let serialized_font_template = SerializedFontTemplate { + identifier: font_template_info.font_template.borrow().identifier.clone(), + descriptor: font_template_info.font_template.borrow().descriptor.clone(), + bytes_receiver, }; + + let _ = result.send(Reply::GetFontTemplateReply(Some( + SerializedFontTemplateInfo { + serialized_font_template, + font_key: font_template_info.font_key, + }, + ))); + + // NB: This will load the font into memory if it hasn't been loaded already. + let _ = bytes_sender.send(&font_template_info.font_template.data()); }, Command::GetFontInstance(font_key, size, result) => { let webrender_api = &self.webrender_api; @@ -382,7 +389,7 @@ impl FontCache { &mut self, template_descriptor: &FontTemplateDescriptor, family_name: &FontFamilyName, - ) -> Option<Arc<FontTemplateData>> { + ) -> Option<FontTemplateRef> { let family_name = self.transform_family(family_name); // TODO(Issue #188): look up localized font family names if canonical name not found @@ -414,7 +421,7 @@ impl FontCache { &mut self, template_descriptor: &FontTemplateDescriptor, family_name: &FontFamilyName, - ) -> Option<Arc<FontTemplateData>> { + ) -> Option<FontTemplateRef> { let family_name = LowercaseString::from(family_name); if self.web_families.contains_key(&family_name) { @@ -425,25 +432,20 @@ impl FontCache { } } - fn get_font_template_info(&mut self, template: Arc<FontTemplateData>) -> FontTemplateInfo { + fn get_font_key_for_template(&mut self, template: &FontTemplateRef) -> FontKey { let webrender_api = &self.webrender_api; let webrender_fonts = &mut self.webrender_fonts; - - let font_key = *webrender_fonts - .entry(template.identifier.clone()) + *webrender_fonts + .entry(template.borrow().identifier.clone()) .or_insert_with(|| { - let font = match (template.bytes_if_in_memory(), template.native_font()) { + let template = template.borrow(); + let font = match (template.data_if_in_memory(), template.native_font_handle()) { (Some(bytes), _) => FontData::Raw((*bytes).clone()), (None, Some(native_font)) => FontData::Native(native_font), - (None, None) => FontData::Raw((*template.bytes()).clone()), + (None, None) => unreachable!("Font should either be local or a web font."), }; webrender_api.add_font(font) - }); - - FontTemplateInfo { - font_template: template, - font_key, - } + }) } fn find_font_template( @@ -462,7 +464,10 @@ impl FontCache { self.find_font_in_local_family(template_descriptor, &family_descriptor.name) }, } - .map(|t| self.get_font_template_info(t)) + .map(|font_template| FontTemplateInfo { + font_key: self.get_font_key_for_template(&font_template), + font_template, + }) } } @@ -618,17 +623,17 @@ impl FontSource for FontCacheThread { match reply.unwrap() { Reply::GetFontTemplateReply(maybe_serialized_font_template_info) => { - match maybe_serialized_font_template_info { - None => None, - Some(serialized_font_template_info) => Some(FontTemplateInfo { - font_template: Arc::new( - serialized_font_template_info - .serialized_font_template - .to_font_template_data(), - ), + maybe_serialized_font_template_info.map(|serialized_font_template_info| { + let font_template = Rc::new(RefCell::new( + serialized_font_template_info + .serialized_font_template + .to_font_template(), + )); + FontTemplateInfo { + font_template, font_key: serialized_font_template_info.font_key, - }), - } + } + }) }, } } diff --git a/components/gfx/font_context.rs b/components/gfx/font_context.rs index d316ceb50c1..f37c1cd91fd 100644 --- a/components/gfx/font_context.rs +++ b/components/gfx/font_context.rs @@ -21,6 +21,8 @@ use crate::font::{ Font, FontDescriptor, FontFamilyDescriptor, FontGroup, FontHandleMethods, FontRef, }; use crate::font_cache_thread::FontTemplateInfo; +#[cfg(target_os = "macos")] +use crate::font_template::FontTemplate; use crate::font_template::FontTemplateDescriptor; use crate::platform::font::FontHandle; @@ -264,4 +266,7 @@ impl Hash for FontGroupCacheKey { #[inline] pub fn invalidate_font_caches() { FONT_CACHE_EPOCH.fetch_add(1, Ordering::SeqCst); + + #[cfg(target_os = "macos")] + FontTemplate::clear_core_text_font_cache(); } diff --git a/components/gfx/font_template.rs b/components/gfx/font_template.rs index 66db059db13..226017efa79 100644 --- a/components/gfx/font_template.rs +++ b/components/gfx/font_template.rs @@ -2,20 +2,26 @@ * 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/. */ +use std::cell::RefCell; use std::fmt::{Debug, Error, Formatter}; use std::io::Error as IoError; -use std::sync::{Arc, Weak}; +use std::rc::Rc; +use std::sync::Arc; +use log::warn; use serde::{Deserialize, Serialize}; use style::computed_values::font_stretch::T as FontStretch; use style::computed_values::font_style::T as FontStyle; use style::properties::style_structs::Font as FontStyleStruct; use style::values::computed::font::FontWeight; +use webrender_api::NativeFontHandle; use crate::font::FontHandleMethods; use crate::font_cache_thread::FontIdentifier; use crate::platform::font::FontHandle; -use crate::platform::font_template::FontTemplateData; + +/// A reference to a [`FontTemplate`] with shared ownership and mutability. +pub(crate) type FontTemplateRef = Rc<RefCell<FontTemplate>>; /// 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. @@ -84,12 +90,14 @@ impl<'a> From<&'a FontStyleStruct> for FontTemplateDescriptor { /// font instance handles. It contains a unique /// FontTemplateData structure that is platform specific. pub struct FontTemplate { - identifier: FontIdentifier, - descriptor: Option<FontTemplateDescriptor>, - weak_ref: Option<Weak<FontTemplateData>>, - // GWTODO: Add code path to unset the strong_ref for web fonts! - strong_ref: Option<Arc<FontTemplateData>>, - is_valid: bool, + pub identifier: FontIdentifier, + pub descriptor: Option<FontTemplateDescriptor>, + /// The data to use for this [`FontTemplate`]. For web fonts, this is always filled, but + /// for local fonts, this is loaded only lazily in layout. + /// + /// TODO: There is no mechanism for web fonts to unset their data! + pub data: Option<Arc<Vec<u8>>>, + pub is_valid: bool, } impl Debug for FontTemplate { @@ -102,24 +110,11 @@ impl Debug for FontTemplate { /// is common, regardless of the number of instances of /// this font handle per thread. impl FontTemplate { - pub fn new( - identifier: FontIdentifier, - maybe_bytes: Option<Vec<u8>>, - ) -> Result<FontTemplate, IoError> { - let maybe_data = match maybe_bytes { - Some(_) => Some(FontTemplateData::new(identifier.clone(), maybe_bytes)?), - None => None, - }; - - let maybe_strong_ref = maybe_data.map(Arc::new); - - let maybe_weak_ref = maybe_strong_ref.as_ref().map(Arc::downgrade); - + pub fn new(identifier: FontIdentifier, data: Option<Vec<u8>>) -> Result<FontTemplate, IoError> { Ok(FontTemplate { identifier, descriptor: None, - weak_ref: maybe_weak_ref, - strong_ref: maybe_strong_ref, + data: data.map(Arc::new), is_valid: true, }) } @@ -128,63 +123,79 @@ impl FontTemplate { &self.identifier } + /// Returns a reference to the bytes in this font if they are in memory. + /// This function never performs disk I/O. + pub fn data_if_in_memory(&self) -> Option<Arc<Vec<u8>>> { + self.data.clone() + } + + /// Returns a [`NativeFontHandle`] for this font template, if it is local. + pub fn native_font_handle(&self) -> Option<NativeFontHandle> { + match &self.identifier { + FontIdentifier::Local(local_identifier) => local_identifier.native_font_handle(), + FontIdentifier::Web(_) => None, + } + } +} + +pub trait FontTemplateRefMethods { + /// Returns a reference to the data in this font. This may be a hugely expensive + /// operation (depending on the platform) which performs synchronous disk I/O + /// and should never be done lightly. + fn data(&self) -> Arc<Vec<u8>>; + /// Return true if this is a valid [`FontTemplate`] ie it is possible to construct a [`FontHandle`] + /// from its data. + fn is_valid(&self) -> bool; + /// If not done already for this [`FontTemplate`] load the font into a platform font face and + /// populate the `descriptor` field. Note that calling [`FontTemplateRefMethods::descriptor()`] + /// does this implicitly. If this fails, [`FontTemplateRefMethods::is_valid()`] will return + /// false in the future. + fn instantiate(&self) -> Result<(), &'static str>; /// Get the descriptor. Returns `None` when instantiating the data fails. - pub fn descriptor(&mut self) -> Option<FontTemplateDescriptor> { - // The font template data can be unloaded when nothing is referencing - // it (via the Weak reference to the Arc above). However, if we have - // already loaded a font, store the style information about it separately, - // so that we can do font matching against it again in the future - // without having to reload the font (unless it is an actual match). - - self.descriptor.or_else(|| { - if self.instantiate().is_err() { - return None; - }; - - Some( - self.descriptor - .expect("Instantiation succeeded but no descriptor?"), - ) - }) + fn descriptor(&self) -> Option<FontTemplateDescriptor>; + /// Returns true if the given descriptor matches the one in this [`FontTemplate`]. + fn descriptor_matches(&self, requested_desc: &FontTemplateDescriptor) -> bool; + /// Calculate the distance from this [`FontTemplate`]s descriptor and return it + /// or None if this is not a valid [`FontTemplate`]. + fn descriptor_distance(&self, requested_descriptor: &FontTemplateDescriptor) -> Option<f32>; +} + +impl FontTemplateRefMethods for FontTemplateRef { + fn descriptor(&self) -> Option<FontTemplateDescriptor> { + // Store the style information about the template separately from the data, + // so that we can do font matching against it again in the future without + // having to reload the font (unless it is an actual match). + if let Some(descriptor) = self.borrow().descriptor { + return Some(descriptor); + } + + if let Err(error) = self.instantiate() { + warn!("Could not initiate FonteTemplate descriptor: {error:?}"); + } + + self.borrow().descriptor } - /// Get the data for creating a font if it matches a given descriptor. - pub fn data_for_descriptor( - &mut self, - requested_desc: &FontTemplateDescriptor, - ) -> Option<Arc<FontTemplateData>> { - self.descriptor().and_then(|descriptor| { - if *requested_desc == descriptor { - self.data().ok() - } else { - None - } - }) + fn descriptor_matches(&self, requested_desc: &FontTemplateDescriptor) -> bool { + self.descriptor() + .map_or(false, |descriptor| descriptor == *requested_desc) } - /// Returns the font data along with the distance between this font's descriptor and the given - /// descriptor, if the font can be loaded. - pub fn data_for_approximate_descriptor( - &mut self, - requested_descriptor: &FontTemplateDescriptor, - ) -> Option<(Arc<FontTemplateData>, f32)> { - self.descriptor().and_then(|descriptor| { - self.data() - .ok() - .map(|data| (data, descriptor.distance_from(requested_descriptor))) - }) + fn descriptor_distance(&self, requested_descriptor: &FontTemplateDescriptor) -> Option<f32> { + self.descriptor() + .map(|descriptor| descriptor.distance_from(requested_descriptor)) } - fn instantiate(&mut self) -> Result<(), &'static str> { - if !self.is_valid { + fn instantiate(&self) -> Result<(), &'static str> { + if !self.borrow().is_valid { return Err("Invalid font template"); } - let data = self.data().map_err(|_| "Could not get FontTemplate data")?; - let handle = FontHandleMethods::new_from_template(data, None); - self.is_valid = handle.is_ok(); + let handle = FontHandleMethods::new_from_template(self.clone(), None); + let mut template = self.borrow_mut(); + template.is_valid = handle.is_ok(); let handle: FontHandle = handle?; - self.descriptor = Some(FontTemplateDescriptor::new( + template.descriptor = Some(FontTemplateDescriptor::new( handle.boldness(), handle.stretchiness(), handle.style(), @@ -192,31 +203,21 @@ impl FontTemplate { Ok(()) } - /// Get the data for creating a font. - pub fn get(&mut self) -> Option<Arc<FontTemplateData>> { - if self.is_valid { - self.data().ok() - } else { - None - } + fn is_valid(&self) -> bool { + self.instantiate().is_ok() } - /// Get the font template data. If any strong references still - /// exist, it will return a clone, otherwise it will load the - /// font data and store a weak reference to it internally. - pub fn data(&mut self) -> Result<Arc<FontTemplateData>, IoError> { - let maybe_data = match self.weak_ref { - Some(ref data) => data.upgrade(), - None => None, - }; - - if let Some(data) = maybe_data { - return Ok(data); - } - - assert!(self.strong_ref.is_none()); - let template_data = Arc::new(FontTemplateData::new(self.identifier.clone(), None)?); - self.weak_ref = Some(Arc::downgrade(&template_data)); - Ok(template_data) + fn data(&self) -> Arc<Vec<u8>> { + let mut template = self.borrow_mut(); + let identifier = template.identifier.clone(); + template + .data + .get_or_insert_with(|| match identifier { + FontIdentifier::Local(local_identifier) => { + Arc::new(local_identifier.read_data_from_file()) + }, + FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."), + }) + .clone() } } diff --git a/components/gfx/platform/freetype/android/font_list.rs b/components/gfx/platform/freetype/android/font_list.rs index 968cb460589..f2130dff45f 100644 --- a/components/gfx/platform/freetype/android/font_list.rs +++ b/components/gfx/platform/freetype/android/font_list.rs @@ -2,12 +2,15 @@ * 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/. */ -use std::path::Path; +use std::fs::File; +use std::io::Read; +use std::path::{Path, PathBuf}; use log::warn; use serde::{Deserialize, Serialize}; use style::Atom; use ucd::{Codepoint, UnicodeBlock}; +use webrender_api::NativeFontHandle; use super::xml::{Attribute, Node}; use crate::text::util::is_cjk; @@ -23,6 +26,24 @@ pub struct LocalFontIdentifier { pub path: Atom, } +impl LocalFontIdentifier { + pub(crate) fn native_font_handle(&self) -> Option<NativeFontHandle> { + Some(NativeFontHandle { + path: PathBuf::from(&*self.path), + index: 0, + }) + } + + pub(crate) fn read_data_from_file(&self) -> Vec<u8> { + let mut bytes = Vec::new(); + File::open(Path::new(&*self.path)) + .expect("Couldn't open font file!") + .read_to_end(&mut bytes) + .unwrap(); + bytes + } +} + // Android doesn't provide an API to query system fonts until Android O: // https://developer.android.com/reference/android/text/FontConfig.html // System font configuration files must be parsed until Android O version is set as the minimum target. diff --git a/components/gfx/platform/freetype/font.rs b/components/gfx/platform/freetype/font.rs index 44e51bc218c..7d9f9c6a507 100644 --- a/components/gfx/platform/freetype/font.rs +++ b/components/gfx/platform/freetype/font.rs @@ -4,7 +4,6 @@ use std::ffi::CString; use std::os::raw::{c_char, c_long}; -use std::sync::Arc; use std::{mem, ptr}; use app_units::Au; @@ -28,7 +27,7 @@ use crate::font::{ KERN, }; use crate::font_cache_thread::FontIdentifier; -use crate::platform::font_template::FontTemplateData; +use crate::font_template::FontTemplateRef; use crate::text::glyph::GlyphId; use crate::text::util::fixed_to_float; @@ -72,9 +71,9 @@ struct OS2Table { #[derive(Debug)] #[allow(unused)] pub struct FontHandle { - // The font binary. This must stay valid for the lifetime of the font, - // if the font is created using FT_Memory_Face. - font_data: Arc<FontTemplateData>, + // The template contains the font data, which must stay valid for the + // lifetime of the platform [`FT_Face`], if it's created via [`FT_Memory_Face`]. + font_template: FontTemplateRef, face: FT_Face, can_do_fast_shaping: bool, } @@ -94,15 +93,18 @@ impl Drop for FontHandle { } } -fn create_face(template: &FontTemplateData, pt_size: Option<Au>) -> Result<FT_Face, &'static str> { +fn create_face(template: &FontTemplateRef, pt_size: Option<Au>) -> Result<FT_Face, &'static str> { unsafe { let mut face: FT_Face = ptr::null_mut(); let face_index = 0 as FT_Long; let library = FreeTypeLibraryHandle::get().lock(); - let result = match template.identifier { + let result = match template.borrow().identifier { FontIdentifier::Web(_) => { - let bytes = template.bytes(); + let bytes = template + .borrow() + .data_if_in_memory() + .expect("Web font should always have data."); FT_New_Memory_Face( library.freetype_library, bytes.as_ptr(), @@ -141,13 +143,13 @@ fn create_face(template: &FontTemplateData, pt_size: Option<Au>) -> Result<FT_Fa impl FontHandleMethods for FontHandle { fn new_from_template( - template: Arc<FontTemplateData>, + template: FontTemplateRef, pt_size: Option<Au>, ) -> Result<FontHandle, &'static str> { let face = create_face(&template, pt_size)?; let mut handle = FontHandle { face, - font_data: template, + font_template: template.clone(), can_do_fast_shaping: false, }; // TODO (#11310): Implement basic support for GPOS and GSUB. @@ -156,8 +158,8 @@ impl FontHandleMethods for FontHandle { Ok(handle) } - fn template(&self) -> Arc<FontTemplateData> { - self.font_data.clone() + fn template(&self) -> FontTemplateRef { + self.font_template.clone() } fn family_name(&self) -> Option<String> { @@ -358,10 +360,6 @@ impl FontHandleMethods for FontHandle { Some(FontTable { buffer: buf }) } } - - fn identifier(&self) -> &FontIdentifier { - &self.font_data.identifier - } } impl<'a> FontHandle { diff --git a/components/gfx/platform/freetype/font_list.rs b/components/gfx/platform/freetype/font_list.rs index 4f17d0bd1b2..9cd7e7663d9 100644 --- a/components/gfx/platform/freetype/font_list.rs +++ b/components/gfx/platform/freetype/font_list.rs @@ -3,6 +3,9 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::ffi::CString; +use std::fs::File; +use std::io::Read; +use std::path::{Path, PathBuf}; use std::ptr; use fontconfig_sys::{ @@ -15,6 +18,7 @@ use libc::{c_char, c_int}; use log::debug; use serde::{Deserialize, Serialize}; use style::Atom; +use webrender_api::NativeFontHandle; use super::c_str_to_string; use crate::text::util::is_cjk; @@ -33,6 +37,25 @@ pub struct LocalFontIdentifier { pub variation_index: i32, } +impl LocalFontIdentifier { + pub(crate) fn native_font_handle(&self) -> Option<NativeFontHandle> { + Some(NativeFontHandle { + path: PathBuf::from(&*self.path), + // TODO: Shouldn't this be using the variation index from the LocalFontIdentifier? + index: 0, + }) + } + + pub(crate) fn read_data_from_file(&self) -> Vec<u8> { + let mut bytes = Vec::new(); + File::open(Path::new(&*self.path)) + .expect("Couldn't open font file!") + .read_to_end(&mut bytes) + .unwrap(); + bytes + } +} + pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String), diff --git a/components/gfx/platform/freetype/font_template.rs b/components/gfx/platform/freetype/font_template.rs deleted file mode 100644 index b9896e15ea8..00000000000 --- a/components/gfx/platform/freetype/font_template.rs +++ /dev/null @@ -1,93 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::fmt; -use std::fs::File; -use std::io::{Error, Read}; -use std::path::{Path, PathBuf}; -use std::sync::{Arc, RwLock}; - -use serde::{Deserialize, Serialize}; -use webrender_api::NativeFontHandle; - -use crate::font_cache_thread::FontIdentifier; - -/// Platform specific font representation for Linux. -#[derive(Deserialize, Serialize)] -pub struct FontTemplateData { - /// Lazily-loaded (for local fonts) byte data that can be passed - /// to Freetype or Raqote directly. - pub font_data: RwLock<Option<Arc<Vec<u8>>>>, - pub identifier: FontIdentifier, -} - -impl fmt::Debug for FontTemplateData { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("FontTemplateData") - .field("identifier", &self.identifier) - .field( - "font_data", - &self - .font_data - .read() - .unwrap() - .as_ref() - .map(|bytes| format!("[{} bytes]", bytes.len())), - ) - .finish() - } -} - -impl FontTemplateData { - pub fn new( - identifier: FontIdentifier, - font_data: Option<Vec<u8>>, - ) -> Result<FontTemplateData, Error> { - Ok(FontTemplateData { - identifier, - font_data: RwLock::new(font_data.map(Arc::new)), - }) - } - - /// Returns a reference to the data in this font. This may be a hugely expensive - /// operation (depending on the platform) which performs synchronous disk I/O - /// and should never be done lightly. - pub fn bytes(&self) -> Arc<Vec<u8>> { - self.font_data - .write() - .unwrap() - .get_or_insert_with(|| { - let path = match &self.identifier { - FontIdentifier::Local(local) => local.path.clone(), - FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."), - }; - let mut bytes = Vec::new(); - File::open(Path::new(&*path)) - .expect("Couldn't open font file!") - .read_to_end(&mut bytes) - .unwrap(); - Arc::new(bytes) - }) - .clone() - } - - /// Returns a reference to the bytes in this font if they are in memory. This function - /// never performs disk I/O. - pub fn bytes_if_in_memory(&self) -> Option<Arc<Vec<u8>>> { - self.font_data.read().unwrap().as_ref().cloned() - } - - /// Returns the native font that underlies this font template, if applicable. - pub fn native_font(&self) -> Option<NativeFontHandle> { - let local_identifier = match &self.identifier { - FontIdentifier::Local(local_identifier) => local_identifier, - FontIdentifier::Web(_) => return None, - }; - - Some(NativeFontHandle { - path: PathBuf::from(&*local_identifier.path), - index: 0, - }) - } -} diff --git a/components/gfx/platform/macos/font.rs b/components/gfx/platform/macos/font.rs index 3e355c7d1ce..05e8fdc0614 100644 --- a/components/gfx/platform/macos/font.rs +++ b/components/gfx/platform/macos/font.rs @@ -4,7 +4,6 @@ use std::cmp::Ordering; use std::ops::Range; -use std::sync::Arc; use std::{fmt, ptr}; /// Implementation of Quartz (CoreGraphics) fonts. @@ -21,12 +20,12 @@ use core_text::font_descriptor::{ use log::debug; use style::values::computed::font::{FontStretch, FontStyle, FontWeight}; +use super::font_template::CoreTextFontTemplateMethods; use crate::font::{ FontHandleMethods, FontMetrics, FontTableMethods, FontTableTag, FractionalPixel, GPOS, GSUB, KERN, }; -use crate::font_cache_thread::FontIdentifier; -use crate::platform::font_template::FontTemplateData; +use crate::font_template::FontTemplateRef; use crate::text::glyph::GlyphId; const KERN_PAIR_LEN: usize = 6; @@ -54,7 +53,7 @@ impl FontTableMethods for FontTable { #[derive(Debug)] pub struct FontHandle { - font_data: Arc<FontTemplateData>, + font_template: FontTemplateRef, ctfont: CTFont, h_kern_subtable: Option<CachedKernTable>, can_do_fast_shaping: bool, @@ -157,34 +156,33 @@ impl fmt::Debug for CachedKernTable { impl FontHandleMethods for FontHandle { fn new_from_template( - template: Arc<FontTemplateData>, + font_template: FontTemplateRef, pt_size: Option<Au>, ) -> Result<FontHandle, &'static str> { let size = match pt_size { Some(s) => s.to_f64_px(), None => 0.0, }; - match template.ctfont(size) { - Some(ref ctfont) => { - let mut handle = FontHandle { - font_data: template.clone(), - ctfont: ctfont.clone_with_font_size(size), - h_kern_subtable: None, - can_do_fast_shaping: false, - }; - handle.h_kern_subtable = handle.find_h_kern_subtable(); - // TODO (#11310): Implement basic support for GPOS and GSUB. - handle.can_do_fast_shaping = handle.h_kern_subtable.is_some() && - handle.table_for_tag(GPOS).is_none() && - handle.table_for_tag(GSUB).is_none(); - Ok(handle) - }, - None => Err("Could not generate CTFont for FontTemplateData"), - } + let Some(core_text_font) = font_template.core_text_font(size) else { + return Err("Could not generate CTFont for FontTemplateData"); + }; + + let mut handle = FontHandle { + font_template, + ctfont: core_text_font.clone_with_font_size(size), + h_kern_subtable: None, + can_do_fast_shaping: false, + }; + handle.h_kern_subtable = handle.find_h_kern_subtable(); + // TODO (#11310): Implement basic support for GPOS and GSUB. + handle.can_do_fast_shaping = handle.h_kern_subtable.is_some() && + handle.table_for_tag(GPOS).is_none() && + handle.table_for_tag(GSUB).is_none(); + Ok(handle) } - fn template(&self) -> Arc<FontTemplateData> { - self.font_data.clone() + fn template(&self) -> FontTemplateRef { + self.font_template.clone() } fn family_name(&self) -> Option<String> { @@ -317,8 +315,4 @@ impl FontHandleMethods for FontHandle { let result: Option<CFData> = self.ctfont.get_font_table(tag); result.map(FontTable::wrap) } - - fn identifier(&self) -> &FontIdentifier { - &self.font_data.identifier - } } diff --git a/components/gfx/platform/macos/font_list.rs b/components/gfx/platform/macos/font_list.rs index 14f9f1fa34a..8d3dc2a09e8 100644 --- a/components/gfx/platform/macos/font_list.rs +++ b/components/gfx/platform/macos/font_list.rs @@ -2,10 +2,15 @@ * 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/. */ +use std::fs::File; +use std::io::Read; +use std::path::Path; + use log::debug; use serde::{Deserialize, Serialize}; use style::Atom; use ucd::{Codepoint, UnicodeBlock}; +use webrender_api::NativeFontHandle; use crate::text::util::unicode_plane; @@ -18,6 +23,24 @@ pub struct LocalFontIdentifier { pub path: Atom, } +impl LocalFontIdentifier { + pub(crate) fn native_font_handle(&self) -> Option<NativeFontHandle> { + Some(NativeFontHandle { + name: self.postscript_name.to_string(), + path: self.path.to_string(), + }) + } + + pub(crate) fn read_data_from_file(&self) -> Vec<u8> { + let mut bytes = Vec::new(); + File::open(Path::new(&*self.path)) + .expect("Couldn't open font file!") + .read_to_end(&mut bytes) + .unwrap(); + bytes + } +} + pub fn for_each_available_family<F>(mut callback: F) where F: FnMut(String), diff --git a/components/gfx/platform/macos/font_template.rs b/components/gfx/platform/macos/font_template.rs index 8320a358622..f7a4cde5efa 100644 --- a/components/gfx/platform/macos/font_template.rs +++ b/components/gfx/platform/macos/font_template.rs @@ -2,182 +2,73 @@ * 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/. */ -use std::collections::hash_map::Entry; use std::collections::HashMap; -use std::fmt; -use std::fs::File; -use std::io::{Error as IoError, Read}; -use std::ops::Deref; -use std::path::Path; -use std::sync::{Arc, Mutex, RwLock}; +use std::sync::OnceLock; use app_units::Au; use core_graphics::data_provider::CGDataProvider; use core_graphics::font::CGFont; use core_text::font::CTFont; -use serde::de::{Error, Visitor}; -use serde::{Deserialize, Deserializer, Serialize, Serializer}; -use webrender_api::NativeFontHandle; +use parking_lot::RwLock; use crate::font_cache_thread::FontIdentifier; +use crate::font_template::{FontTemplate, FontTemplateRef, FontTemplateRefMethods}; -/// Platform specific font representation for MacOS. CTFont object is cached here for use -/// by the paint functions that create CGFont references. -#[derive(Deserialize, Serialize)] -pub struct FontTemplateData { - // If you add members here, review the Debug impl below - /// The `CTFont` object, if present. This is cached here so that we don't have to keep creating - /// `CTFont` instances over and over. It can always be recreated from the `identifier` and/or - /// `font_data` fields. - /// - /// When sending a `FontTemplateData` instance across processes, this will be cleared out on - /// the other side, because `CTFont` instances cannot be sent across processes. This is - /// harmless, however, because it can always be recreated. - ctfont: CachedCTFont, - pub identifier: FontIdentifier, - pub font_data: RwLock<Option<Arc<Vec<u8>>>>, -} +// A cache of `CTFont` to avoid having to create `CTFont` instances over and over. It is +// always possible to create a `CTFont` using a `FontTemplate` even if it isn't in this +// cache. +static CTFONT_CACHE: OnceLock<RwLock<HashMap<FontIdentifier, CachedCTFont>>> = OnceLock::new(); -impl fmt::Debug for FontTemplateData { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("FontTemplateData") - .field("ctfont", &self.ctfont) - .field("identifier", &self.identifier) - .field( - "font_data", - &self - .font_data - .read() - .unwrap() - .as_ref() - .map(|bytes| format!("[{} bytes]", bytes.len())), - ) - .finish() - } -} +/// A [`HashMap`] of cached [`CTFont`] for a single [`FontIdentifier`]. There is one [`CTFont`] +/// for each cached font size. +type CachedCTFont = HashMap<Au, CTFont>; -unsafe impl Send for FontTemplateData {} -unsafe impl Sync for FontTemplateData {} +pub(crate) trait CoreTextFontTemplateMethods { + /// Retrieves a [`CTFont`] instance, instantiating it if necessary if it is not + /// stored in the shared Core Text font cache. + fn core_text_font(&self, pt_size: f64) -> Option<CTFont>; +} -impl FontTemplateData { - pub fn new( - identifier: FontIdentifier, - font_data: Option<Vec<u8>>, - ) -> Result<FontTemplateData, IoError> { - Ok(FontTemplateData { - ctfont: CachedCTFont(Mutex::new(HashMap::new())), - identifier, - font_data: RwLock::new(font_data.map(Arc::new)), - }) - } +impl CoreTextFontTemplateMethods for FontTemplateRef { + fn core_text_font(&self, pt_size: f64) -> Option<CTFont> { + //// If you pass a zero font size to one of the Core Text APIs, it'll replace it with + //// 12.0. We don't want that! (Issue #10492.) + let clamped_pt_size = pt_size.max(0.01); + let au_size = Au::from_f64_px(clamped_pt_size); + + let cache = CTFONT_CACHE.get_or_init(Default::default); + let identifier = self.borrow().identifier.clone(); + { + let cache = cache.read(); + if let Some(core_text_font) = cache + .get(&identifier) + .and_then(|identifier_cache| identifier_cache.get(&au_size)) + { + return Some(core_text_font.clone()); + } + } - /// Retrieves the Core Text font instance, instantiating it if necessary. - pub fn ctfont(&self, pt_size: f64) -> Option<CTFont> { - let mut ctfonts = self.ctfont.lock().unwrap(); + let mut cache = cache.write(); + let identifier_cache = cache.entry(identifier).or_insert_with(Default::default); - let entry = ctfonts.entry(Au::from_f64_px(pt_size)); - match entry { - Entry::Occupied(entry) => return Some(entry.get().clone()), - Entry::Vacant(_) => {}, + // It could be that between the time of the cache miss above and now, after the write lock + // on the cache has been acquired, the cache was populated with the data that we need. Thus + // check again and return the CTFont if it is is already cached. + if let Some(core_text_font) = identifier_cache.get(&au_size) { + return Some(core_text_font.clone()); } - // If you pass a zero font size to one of the Core Text APIs, it'll replace it with - // 12.0. We don't want that! (Issue #10492.) - let clamped_pt_size = pt_size.max(0.01); - - let provider = CGDataProvider::from_buffer(self.bytes()); + let provider = CGDataProvider::from_buffer(self.data()); let cgfont = CGFont::from_data_provider(provider).ok()?; - let ctfont = core_text::font::new_from_CGFont(&cgfont, clamped_pt_size); - - // Cache the newly created CTFont font. - entry.or_insert(ctfont.clone()); - - Some(ctfont) - } - - /// Returns a reference to the data in this font. This may be a hugely expensive - /// operation (depending on the platform) which performs synchronous disk I/O - /// and should never be done lightly. - pub fn bytes(&self) -> Arc<Vec<u8>> { - self.font_data - .write() - .unwrap() - .get_or_insert_with(|| { - let path = match &self.identifier { - FontIdentifier::Local(local) => local.path.clone(), - FontIdentifier::Web(_) => unreachable!("Web fonts should always have data."), - }; - let mut bytes = Vec::new(); - File::open(Path::new(&*path)) - .expect("Couldn't open font file!") - .read_to_end(&mut bytes) - .unwrap(); - Arc::new(bytes) - }) - .clone() - } - - /// Returns a reference to the bytes in this font if they are in memory. - /// This function never performs disk I/O. - pub fn bytes_if_in_memory(&self) -> Option<Arc<Vec<u8>>> { - self.font_data.read().unwrap().as_ref().cloned() - } - - /// Returns the native font that underlies this font template, if applicable. - pub fn native_font(&self) -> Option<NativeFontHandle> { - let local_identifier = match &self.identifier { - FontIdentifier::Local(local_identifier) => local_identifier, - FontIdentifier::Web(_) => return None, - }; - Some(NativeFontHandle { - name: local_identifier.postscript_name.to_string(), - path: local_identifier.path.to_string(), - }) - } -} - -#[derive(Debug)] -pub struct CachedCTFont(Mutex<HashMap<Au, CTFont>>); - -impl Deref for CachedCTFont { - type Target = Mutex<HashMap<Au, CTFont>>; - fn deref(&self) -> &Mutex<HashMap<Au, CTFont>> { - &self.0 - } -} - -impl Serialize for CachedCTFont { - fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> - where - S: Serializer, - { - serializer.serialize_none() + let core_text_font = core_text::font::new_from_CGFont(&cgfont, clamped_pt_size); + identifier_cache.insert(au_size, core_text_font.clone()); + Some(core_text_font) } } -impl<'de> Deserialize<'de> for CachedCTFont { - fn deserialize<D>(deserializer: D) -> Result<CachedCTFont, D::Error> - where - D: Deserializer<'de>, - { - struct NoneOptionVisitor; - - impl<'de> Visitor<'de> for NoneOptionVisitor { - type Value = CachedCTFont; - - fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - write!(fmt, "none") - } - - #[inline] - fn visit_none<E>(self) -> Result<CachedCTFont, E> - where - E: Error, - { - Ok(CachedCTFont(Mutex::new(HashMap::new()))) - } - } - - deserializer.deserialize_option(NoneOptionVisitor) +impl FontTemplate { + pub(crate) fn clear_core_text_font_cache() { + let cache = CTFONT_CACHE.get_or_init(Default::default); + cache.write().clear(); } } diff --git a/components/gfx/platform/mod.rs b/components/gfx/platform/mod.rs index 6e3d191529e..72482e7df61 100644 --- a/components/gfx/platform/mod.rs +++ b/components/gfx/platform/mod.rs @@ -3,11 +3,11 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #[cfg(any(target_os = "linux", target_os = "android"))] -pub use crate::platform::freetype::{font, font_list, font_template, library_handle}; +pub use crate::platform::freetype::{font, font_list, library_handle}; #[cfg(target_os = "macos")] pub use crate::platform::macos::{font, font_list, font_template}; #[cfg(target_os = "windows")] -pub use crate::platform::windows::{font, font_list, font_template}; +pub use crate::platform::windows::{font, font_list}; #[cfg(any(target_os = "linux", target_os = "android"))] mod freetype { @@ -36,7 +36,6 @@ mod freetype { #[cfg(target_os = "android")] pub use self::android::font_list; - pub mod font_template; pub mod library_handle; } @@ -51,5 +50,4 @@ mod macos { mod windows { pub mod font; pub mod font_list; - pub mod font_template; } diff --git a/components/gfx/platform/windows/font.rs b/components/gfx/platform/windows/font.rs index 03b82022068..a2156f85e73 100644 --- a/components/gfx/platform/windows/font.rs +++ b/components/gfx/platform/windows/font.rs @@ -8,7 +8,6 @@ use std::fmt; use std::ops::Deref; -use std::sync::Arc; use app_units::Au; use dwrote::{Font, FontFace, FontFile, FontStretch, FontStyle}; @@ -22,7 +21,7 @@ use crate::font::{ FontHandleMethods, FontMetrics, FontTableMethods, FontTableTag, FractionalPixel, }; use crate::font_cache_thread::FontIdentifier; -use crate::platform::font_template::FontTemplateData; +use crate::font_template::{FontTemplateRef, FontTemplateRefMethods}; use crate::text::glyph::GlyphId; // 1em = 12pt = 16px, assuming 72 points per inch and 96 px per inch @@ -200,7 +199,7 @@ impl FontInfo { #[derive(Debug)] pub struct FontHandle { - font_data: Arc<FontTemplateData>, + font_template: FontTemplateRef, face: Nondebug<FontFace>, info: FontInfo, em_size: f32, @@ -227,13 +226,18 @@ impl FontHandle {} impl FontHandleMethods for FontHandle { fn new_from_template( - template: Arc<FontTemplateData>, + font_template: FontTemplateRef, pt_size: Option<Au>, ) -> Result<Self, &'static str> { - let (face, info) = match template.get_font() { + let direct_write_font = match font_template.borrow().identifier { + FontIdentifier::Local(ref local_identifier) => local_identifier.direct_write_font(), + FontIdentifier::Web(_) => None, + }; + + let (face, info) = match direct_write_font { Some(font) => (font.create_font_face(), FontInfo::new_from_font(&font)?), None => { - let bytes = template.bytes(); + let bytes = font_template.data(); let font_file = FontFile::new_from_data(bytes).ok_or("Could not create FontFile")?; let face = font_file @@ -254,7 +258,7 @@ impl FontHandleMethods for FontHandle { let scaled_design_units_to_pixels = em_size / design_units_per_pixel; Ok(FontHandle { - font_data: template.clone(), + font_template, face: Nondebug(face), info, em_size, @@ -263,8 +267,8 @@ impl FontHandleMethods for FontHandle { }) } - fn template(&self) -> Arc<FontTemplateData> { - self.font_data.clone() + fn template(&self) -> FontTemplateRef { + self.font_template.clone() } fn family_name(&self) -> Option<String> { @@ -352,8 +356,4 @@ impl FontHandleMethods for FontHandle { .get_font_table(tag) .map(|bytes| FontTable { data: bytes }) } - - fn identifier(&self) -> &FontIdentifier { - &self.font_data.identifier - } } diff --git a/components/gfx/platform/windows/font_list.rs b/components/gfx/platform/windows/font_list.rs index bc5e0030972..d90c7d1aab8 100644 --- a/components/gfx/platform/windows/font_list.rs +++ b/components/gfx/platform/windows/font_list.rs @@ -5,9 +5,10 @@ use std::hash::Hash; use std::sync::Arc; -use dwrote::{FontCollection, FontDescriptor}; +use dwrote::{Font, FontCollection, FontDescriptor}; use serde::{Deserialize, Serialize}; use ucd::{Codepoint, UnicodeBlock}; +use webrender_api::NativeFontHandle; use crate::text::util::unicode_plane; @@ -34,6 +35,32 @@ pub struct LocalFontIdentifier { pub font_descriptor: Arc<FontDescriptor>, } +impl LocalFontIdentifier { + /// Create a [`Font`] for this font. + pub fn direct_write_font(&self) -> Option<Font> { + FontCollection::system().get_font_from_descriptor(&self.font_descriptor) + } + + pub fn native_font_handle(&self) -> Option<NativeFontHandle> { + let face = self.direct_write_font()?.create_font_face(); + let path = face.get_files().first()?.get_font_file_path()?; + Some(NativeFontHandle { + path, + index: face.get_index(), + }) + } + + pub(crate) fn read_data_from_file(&self) -> Vec<u8> { + let font = FontCollection::system() + .get_font_from_descriptor(&self.font_descriptor) + .unwrap(); + let face = font.create_font_face(); + let files = face.get_files(); + assert!(!files.is_empty()); + files[0].get_font_file_bytes() + } +} + impl Eq for LocalFontIdentifier {} impl Hash for LocalFontIdentifier { diff --git a/components/gfx/platform/windows/font_template.rs b/components/gfx/platform/windows/font_template.rs deleted file mode 100644 index 9cdcb980e9a..00000000000 --- a/components/gfx/platform/windows/font_template.rs +++ /dev/null @@ -1,101 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ - -use std::sync::{Arc, RwLock}; -use std::{fmt, io}; - -use dwrote::{Font, FontCollection}; -use serde::{Deserialize, Serialize}; -use webrender_api::NativeFontHandle; - -use crate::font_cache_thread::FontIdentifier; - -#[derive(Deserialize, Serialize)] -pub struct FontTemplateData { - /// The identifier for this font. - pub identifier: FontIdentifier, - /// The bytes of this font, lazily loaded if this is a local font. - pub font_data: RwLock<Option<Arc<Vec<u8>>>>, -} - -impl fmt::Debug for FontTemplateData { - fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { - fmt.debug_struct("FontTemplateData") - .field( - "font_data", - &self - .font_data - .read() - .unwrap() - .as_ref() - .map(|bytes| format!("[{} bytes]", bytes.len())), - ) - .field("identifier", &self.identifier) - .finish() - } -} - -impl FontTemplateData { - pub fn new( - identifier: FontIdentifier, - font_data: Option<Vec<u8>>, - ) -> Result<FontTemplateData, io::Error> { - Ok(FontTemplateData { - identifier, - font_data: RwLock::new(font_data.map(Arc::new)), - }) - } - - /// Returns a reference to the data in this font. This may be a hugely expensive - /// operation (depending on the platform) which performs synchronous disk I/O - /// and should never be done lightly. - pub fn bytes(&self) -> Arc<Vec<u8>> { - self.font_data - .write() - .unwrap() - .get_or_insert_with(|| { - let font_descriptor = match &self.identifier { - FontIdentifier::Local(local_identifier) => { - local_identifier.font_descriptor.clone() - }, - FontIdentifier::Web(_) => unreachable!("Created a web font without data."), - }; - - let font = FontCollection::system() - .get_font_from_descriptor(&font_descriptor) - .unwrap(); - let face = font.create_font_face(); - let files = face.get_files(); - assert!(!files.is_empty()); - Arc::new(files[0].get_font_file_bytes()) - }) - .clone() - } - - /// Returns a reference to the bytes in this font if they are in memory. - /// This function never performs disk I/O. - pub fn bytes_if_in_memory(&self) -> Option<Arc<Vec<u8>>> { - self.font_data.read().unwrap().as_ref().cloned() - } - - /// Get a [`Font`] for this font if it is a local font or return `None` if it's a - /// web font. - pub fn get_font(&self) -> Option<Font> { - let font_descriptor = match &self.identifier { - FontIdentifier::Local(local_identifier) => local_identifier.font_descriptor.clone(), - FontIdentifier::Web(_) => return None, - }; - - FontCollection::system().get_font_from_descriptor(&font_descriptor) - } - - pub fn native_font(&self) -> Option<NativeFontHandle> { - let face = self.get_font()?.create_font_face(); - let path = face.get_files().first()?.get_font_file_path()?; - Some(NativeFontHandle { - path, - index: face.get_index(), - }) - } -} diff --git a/components/gfx/tests/font_context.rs b/components/gfx/tests/font_context.rs index 24965e272f1..9d26f7603dd 100644 --- a/components/gfx/tests/font_context.rs +++ b/components/gfx/tests/font_context.rs @@ -11,7 +11,8 @@ use std::rc::Rc; use app_units::Au; use gfx::font::{ - fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, FontSearchScope, + fallback_font_families, FontDescriptor, FontFamilyDescriptor, FontFamilyName, + FontHandleMethods, FontSearchScope, }; use gfx::font_cache_thread::{FontIdentifier, FontTemplateInfo, FontTemplates}; use gfx::font_context::{FontContext, FontSource}; @@ -175,7 +176,7 @@ fn test_font_group_find_by_codepoint() { .find_by_codepoint(&mut context, 'a') .unwrap(); assert_eq!( - *font.borrow().identifier(), + font.borrow().handle.template().borrow().identifier, TestFontSource::identifier_for_font_name("csstest-ascii") ); assert_eq!( @@ -189,7 +190,7 @@ fn test_font_group_find_by_codepoint() { .find_by_codepoint(&mut context, 'a') .unwrap(); assert_eq!( - *font.borrow().identifier(), + font.borrow().handle.template().borrow().identifier, TestFontSource::identifier_for_font_name("csstest-ascii") ); assert_eq!( @@ -203,7 +204,7 @@ fn test_font_group_find_by_codepoint() { .find_by_codepoint(&mut context, 'á') .unwrap(); assert_eq!( - *font.borrow().identifier(), + font.borrow().handle.template().borrow().identifier, TestFontSource::identifier_for_font_name("csstest-basic-regular") ); assert_eq!(count.get(), 2, "both fonts should now have been loaded"); @@ -224,7 +225,7 @@ fn test_font_fallback() { .find_by_codepoint(&mut context, 'a') .unwrap(); assert_eq!( - *font.borrow().identifier(), + font.borrow().handle.template().borrow().identifier, TestFontSource::identifier_for_font_name("csstest-ascii"), "a family in the group should be used if there is a matching glyph" ); @@ -234,7 +235,7 @@ fn test_font_fallback() { .find_by_codepoint(&mut context, 'á') .unwrap(); assert_eq!( - *font.borrow().identifier(), + font.borrow().handle.template().borrow().identifier, TestFontSource::identifier_for_font_name("csstest-basic-regular"), "a fallback font should be used if there is no matching glyph in the group" ); diff --git a/components/gfx/tests/font_template.rs b/components/gfx/tests/font_template.rs index d35c3607497..5c8e9f8b37a 100644 --- a/components/gfx/tests/font_template.rs +++ b/components/gfx/tests/font_template.rs @@ -6,12 +6,14 @@ #[cfg(not(target_os = "macos"))] #[test] fn test_font_template_descriptor() { + use std::cell::RefCell; use std::fs::File; use std::io::prelude::*; use std::path::PathBuf; + use std::rc::Rc; use gfx::font_cache_thread::FontIdentifier; - use gfx::font_template::{FontTemplate, FontTemplateDescriptor}; + use gfx::font_template::{FontTemplate, FontTemplateDescriptor, FontTemplateRefMethods}; use servo_url::ServoUrl; use style::values::computed::font::{FontStretch, FontStyle, FontWeight}; @@ -28,11 +30,12 @@ fn test_font_template_descriptor() { path.push(format!("{}.ttf", filename)); let file = File::open(path.clone()).unwrap(); - let mut template = FontTemplate::new( + let template = FontTemplate::new( FontIdentifier::Web(ServoUrl::from_file_path(path).unwrap()), Some(file.bytes().map(|b| b.unwrap()).collect()), ) .unwrap(); + let template = Rc::new(RefCell::new(template)); template.descriptor().unwrap() } diff --git a/components/gfx/text/text_run.rs b/components/gfx/text/text_run.rs index a411c7125db..56f66d6cd1f 100644 --- a/components/gfx/text/text_run.rs +++ b/components/gfx/text/text_run.rs @@ -17,7 +17,7 @@ use webrender_api::FontInstanceKey; use xi_unicode::LineBreakLeafIter; use crate::font::{Font, FontHandleMethods, FontMetrics, RunMetrics, ShapingFlags, ShapingOptions}; -use crate::platform::font_template::FontTemplateData; +use crate::font_template::FontTemplateRefMethods; use crate::text::glyph::{ByteIndex, GlyphStore}; thread_local! { @@ -30,7 +30,8 @@ thread_local! { pub struct TextRun { /// The UTF-8 string represented by this text run. pub text: Arc<String>, - pub font_template: Arc<FontTemplateData>, + /// This ensures the data stays alive as long as this TextRun is using this font. + font_data: Arc<Vec<u8>>, pub pt_size: Au, pub font_metrics: FontMetrics, pub font_key: FontInstanceKey, @@ -193,7 +194,7 @@ impl<'a> TextRun { TextRun { text: Arc::new(text), font_metrics: font.metrics.clone(), - font_template: font.handle.template(), + font_data: font.handle.template().data(), font_key: font.font_key, pt_size: font.descriptor.pt_size, glyphs: Arc::new(glyphs), |