diff --git a/core/src/character.rs b/core/src/character.rs index 414e87ee6..c20d04004 100644 --- a/core/src/character.rs +++ b/core/src/character.rs @@ -9,7 +9,7 @@ pub enum Character<'gc> { MovieClip(MovieClip<'gc>), Bitmap(Bitmap<'gc>), Button(Button<'gc>), - Font(Box), + Font(Font<'gc>), MorphShape(MorphShape<'gc>), Text(Text<'gc>), Sound(SoundHandle), diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index 1c0bd4230..3b93d522d 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -1245,10 +1245,10 @@ impl<'gc, 'a> MovieClipData<'gc> { is_bold: false, is_italic: false, }; - let font_object = Font::from_swf_tag(context.renderer, &font).unwrap(); + let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); context .library - .register_character(font.id, Character::Font(Box::new(font_object))); + .register_character(font.id, Character::Font(font_object)); Ok(()) } @@ -1259,10 +1259,10 @@ impl<'gc, 'a> MovieClipData<'gc> { reader: &mut SwfStream<&'a [u8]>, ) -> DecodeResult { let font = reader.read_define_font_2(2)?; - let font_object = Font::from_swf_tag(context.renderer, &font).unwrap(); + let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); context .library - .register_character(font.id, Character::Font(Box::new(font_object))); + .register_character(font.id, Character::Font(font_object)); Ok(()) } @@ -1273,10 +1273,10 @@ impl<'gc, 'a> MovieClipData<'gc> { reader: &mut SwfStream<&'a [u8]>, ) -> DecodeResult { let font = reader.read_define_font_2(3)?; - let font_object = Font::from_swf_tag(context.renderer, &font).unwrap(); + let font_object = Font::from_swf_tag(context.gc_context, context.renderer, &font).unwrap(); context .library - .register_character(font.id, Character::Font(Box::new(font_object))); + .register_character(font.id, Character::Font(font_object)); Ok(()) } diff --git a/core/src/font.rs b/core/src/font.rs index 062d05836..b66012478 100644 --- a/core/src/font.rs +++ b/core/src/font.rs @@ -1,10 +1,16 @@ use crate::backend::render::{RenderBackend, ShapeHandle}; use crate::prelude::*; +use gc_arena::{Collect, Gc, MutationContext}; type Error = Box; -#[derive(Clone)] -pub struct Font { +#[derive(Debug, Clone, Collect, Copy)] +#[collect(no_drop)] +pub struct Font<'gc>(Gc<'gc, FontData>); + +#[derive(Debug, Clone, Collect)] +#[collect(require_static)] +struct FontData { /// The list of glyphs defined in the font. /// Used directly by `DefineText` tags. glyphs: Vec, @@ -22,8 +28,12 @@ pub struct Font { kerning_pairs: fnv::FnvHashMap<(u16, u16), Twips>, } -impl Font { - pub fn from_swf_tag(renderer: &mut dyn RenderBackend, tag: &swf::Font) -> Result { +impl<'gc> Font<'gc> { + pub fn from_swf_tag( + gc_context: MutationContext<'gc, '_>, + renderer: &mut dyn RenderBackend, + tag: &swf::Font, + ) -> Result, Error> { let mut glyphs = vec![]; let mut code_point_to_glyph = fnv::FnvHashMap::default(); for swf_glyph in &tag.glyphs { @@ -44,36 +54,38 @@ impl Font { } else { fnv::FnvHashMap::default() }; - Ok(Font { - glyphs, - code_point_to_glyph, + Ok(Font(Gc::allocate( + gc_context, + FontData { + glyphs, + code_point_to_glyph, - /// DefineFont3 stores coordinates at 20x the scale of DefineFont1/2. - /// (SWF19 p.164) - scale: if tag.version >= 3 { 20480.0 } else { 1024.0 }, - kerning_pairs, - }) + /// DefineFont3 stores coordinates at 20x the scale of DefineFont1/2. + /// (SWF19 p.164) + scale: if tag.version >= 3 { 20480.0 } else { 1024.0 }, + kerning_pairs, + }, + ))) } /// Returns whether this font contains glyph shapes. /// If not, this font should be rendered as a device font. - #[inline] - pub fn has_glyphs(&self) -> bool { - !self.glyphs.is_empty() + pub fn has_glyphs(self) -> bool { + !self.0.glyphs.is_empty() } /// Returns a glyph entry by index. /// Used by `Text` display objects. - pub fn get_glyph(&self, i: usize) -> Option { - self.glyphs.get(i).cloned() + pub fn get_glyph(self, i: usize) -> Option { + self.0.glyphs.get(i).cloned() } /// Returns a glyph entry by character. /// Used by `EditText` display objects. - pub fn get_glyph_for_char(&self, c: char) -> Option { + pub fn get_glyph_for_char(self, c: char) -> Option { // TODO: Properly handle UTF-16/out-of-bounds code points. let code_point = c as u16; - if let Some(index) = self.code_point_to_glyph.get(&code_point) { + if let Some(index) = self.0.code_point_to_glyph.get(&code_point) { self.get_glyph(*index) } else { None @@ -83,25 +95,24 @@ impl Font { /// Given a pair of characters, applies the offset that should be applied /// to the advance value between these two characters. /// Returns 0 twips if no kerning offset exists between these two characters. - pub fn get_kerning_offset(&self, left: char, right: char) -> Twips { + pub fn get_kerning_offset(self, left: char, right: char) -> Twips { // TODO: Properly handle UTF-16/out-of-bounds code points. let left_code_point = left as u16; let right_code_point = right as u16; - self.kerning_pairs + self.0 + .kerning_pairs .get(&(left_code_point, right_code_point)) .cloned() .unwrap_or_default() } /// Returns whether this font contains kerning information. - #[inline] - pub fn has_kerning_info(&self) -> bool { - !self.kerning_pairs.is_empty() + pub fn has_kerning_info(self) -> bool { + !self.0.kerning_pairs.is_empty() } - #[inline] - pub fn scale(&self) -> f32 { - self.scale + pub fn scale(self) -> f32 { + self.0.scale } } diff --git a/core/src/library.rs b/core/src/library.rs index 4908304c0..223bfc3f0 100644 --- a/core/src/library.rs +++ b/core/src/library.rs @@ -13,7 +13,7 @@ pub struct Library<'gc> { characters: HashMap>, export_characters: HashMap>, jpeg_tables: Option>, - device_font: Option>, + device_font: Option>, } impl<'gc> Library<'gc> { @@ -106,8 +106,8 @@ impl<'gc> Library<'gc> { Ok(obj) } - pub fn get_font(&self, id: CharacterId) -> Option<&Font> { - if let Some(&Character::Font(ref font)) = self.characters.get(&id) { + pub fn get_font(&self, id: CharacterId) -> Option> { + if let Some(&Character::Font(font)) = self.characters.get(&id) { Some(font) } else { None @@ -143,12 +143,12 @@ impl<'gc> Library<'gc> { } /// Returns the device font for use when a font is unavailable. - pub fn device_font(&self) -> Option<&Font> { - self.device_font.as_ref().map(AsRef::as_ref) + pub fn device_font(&self) -> Option> { + self.device_font } /// Sets the device font. - pub fn set_device_font(&mut self, font: Option>) { + pub fn set_device_font(&mut self, font: Option>) { self.device_font = font; } } @@ -159,6 +159,7 @@ unsafe impl<'gc> gc_arena::Collect for Library<'gc> { for character in self.characters.values() { character.trace(cc); } + self.device_font.trace(cc); } } diff --git a/core/src/player.rs b/core/src/player.rs index 37fe215e4..5714ecc67 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -141,16 +141,6 @@ impl let movie_width = (header.stage_size.x_max - header.stage_size.x_min).to_pixels() as u32; let movie_height = (header.stage_size.y_max - header.stage_size.y_min).to_pixels() as u32; - // Load and parse the device font. - // TODO: We could use lazy_static here. - let device_font = match Self::load_device_font(DEVICE_FONT_TAG, &mut renderer) { - Ok(font) => Some(font), - Err(e) => { - log::error!("Unable to load device font: {}", e); - None - } - }; - let mut player = Player { player_version: NEWEST_PLAYER_VERSION, @@ -159,10 +149,6 @@ impl is_playing: false, - renderer, - audio, - navigator, - background_color: Color { r: 255, g: 255, @@ -176,6 +162,16 @@ impl rng: SmallRng::from_seed([0u8; 16]), // TODO(Herschel): Get a proper seed on all platforms. gc_arena: GcArena::new(ArenaParameters::default(), |gc_context| { + // Load and parse the device font. + let device_font = + match Self::load_device_font(gc_context, DEVICE_FONT_TAG, &mut renderer) { + Ok(font) => Some(font), + Err(e) => { + log::error!("Unable to load device font: {}", e); + None + } + }; + let mut library = Library::new(); library.set_device_font(device_font); GcRoot(GcCell::allocate( @@ -210,6 +206,10 @@ impl mouse_pos: (Twips::new(0), Twips::new(0)), is_mouse_down: false, + + renderer, + audio, + navigator, }; player.gc_arena.mutate(|gc_context, gc_root| { @@ -674,15 +674,14 @@ impl /// Loads font data from the given buffer. /// The buffer should be the `DefineFont3` info for the tag. /// The tag header should not be included. - fn load_device_font( + fn load_device_font<'gc>( + gc_context: gc_arena::MutationContext<'gc, '_>, data: &[u8], renderer: &mut Renderer, - ) -> Result, Error> { + ) -> Result, Error> { let mut reader = swf::read::Reader::new(data, 8); - let device_font = Box::new(crate::font::Font::from_swf_tag( - renderer, - &reader.read_define_font_2(3)?, - )?); + let device_font = + crate::font::Font::from_swf_tag(gc_context, renderer, &reader.read_define_font_2(3)?)?; Ok(device_font) } }