From 09fa755405877aba2d77db6f2167cf5851650030 Mon Sep 17 00:00:00 2001 From: Mike Welsh Date: Fri, 25 Oct 2019 23:44:12 -0700 Subject: [PATCH] core: Make Library::device_font optional --- core/src/edit_text.rs | 77 ++++++++++++++++++++++--------------------- core/src/library.rs | 21 +++++++++--- core/src/player.rs | 44 +++++++++++++++---------- 3 files changed, 82 insertions(+), 60 deletions(-) diff --git a/core/src/edit_text.rs b/core/src/edit_text.rs index 7478d807f..db807da7b 100644 --- a/core/src/edit_text.rs +++ b/core/src/edit_text.rs @@ -65,52 +65,53 @@ impl<'gc> DisplayObject<'gc> for EditText<'gc> { transform.color_transform.g_mult = f32::from(color.g) / 255.0; transform.color_transform.b_mult = f32::from(color.b) / 255.0; transform.color_transform.a_mult = f32::from(color.a) / 255.0; - let device_font = context.library.device_font(); // If the font can't be found or has no glyph information, use the "device font" instead. // We're cheating a bit and not actually rendering text using the OS/web. // Instead, we embed an SWF version of Noto Sans to use as the "device font", and render // it the same as any other SWF outline text. - let font = context + if let Some(font) = context .library .get_font(font_id) .filter(|font| font.has_glyphs()) - .unwrap_or(device_font); - let scale = if let Some(height) = static_data.height { - transform.matrix.ty += f32::from(height); - f32::from(height) / font.scale() - } else { - 1.0 - }; - if let Some(layout) = &static_data.layout { - transform.matrix.ty -= layout.leading.get() as f32; - } - transform.matrix.a = scale; - transform.matrix.d = scale; - let mut chars = self.text.chars().peekable(); - let has_kerning_info = font.has_kerning_info(); - while let Some(c) = chars.next() { - // TODO: SWF text fields can contain a limited subset of HTML (and often do in SWF versions >6). - // This is a quicky-and-dirty way to skip the HTML tags. This is obviously not correct - // and we will need to properly parse and handle the HTML at some point. - // See SWF19 pp. 173-174 for supported HTML tags. - if self.static_data.0.is_html && c == '<' { - // Skip characters until we see a close bracket. - chars.by_ref().skip_while(|&x| x != '>').next(); - } else if let Some(glyph) = font.get_glyph_for_char(c) { - // Render glyph. - context.transform_stack.push(&transform); - context - .renderer - .render_shape(glyph.shape, context.transform_stack.transform()); - context.transform_stack.pop(); - // Step horizontally. - let mut advance = f32::from(glyph.advance); - if has_kerning_info { - advance += font - .get_kerning_offset(c, chars.peek().cloned().unwrap_or('\0')) - .get() as f32; + .or_else(|| context.library.device_font()) + { + let scale = if let Some(height) = static_data.height { + transform.matrix.ty += f32::from(height); + f32::from(height) / font.scale() + } else { + 1.0 + }; + if let Some(layout) = &static_data.layout { + transform.matrix.ty -= layout.leading.get() as f32; + } + transform.matrix.a = scale; + transform.matrix.d = scale; + let mut chars = self.text.chars().peekable(); + let has_kerning_info = font.has_kerning_info(); + while let Some(c) = chars.next() { + // TODO: SWF text fields can contain a limited subset of HTML (and often do in SWF versions >6). + // This is a quicky-and-dirty way to skip the HTML tags. This is obviously not correct + // and we will need to properly parse and handle the HTML at some point. + // See SWF19 pp. 173-174 for supported HTML tags. + if self.static_data.0.is_html && c == '<' { + // Skip characters until we see a close bracket. + chars.by_ref().skip_while(|&x| x != '>').next(); + } else if let Some(glyph) = font.get_glyph_for_char(c) { + // Render glyph. + context.transform_stack.push(&transform); + context + .renderer + .render_shape(glyph.shape, context.transform_stack.transform()); + context.transform_stack.pop(); + // Step horizontally. + let mut advance = f32::from(glyph.advance); + if has_kerning_info { + advance += font + .get_kerning_offset(c, chars.peek().cloned().unwrap_or('\0')) + .get() as f32; + } + transform.matrix.tx += advance * scale; } - transform.matrix.tx += advance * scale; } } context.transform_stack.pop(); diff --git a/core/src/library.rs b/core/src/library.rs index c8c06b799..dc1ef920c 100644 --- a/core/src/library.rs +++ b/core/src/library.rs @@ -10,15 +10,15 @@ use swf::CharacterId; pub struct Library<'gc> { characters: HashMap>, jpeg_tables: Option>, - device_font: Box, + device_font: Option>, } impl<'gc> Library<'gc> { - pub fn new(device_font: Box) -> Self { + pub fn new() -> Self { Library { characters: HashMap::new(), jpeg_tables: None, - device_font, + device_font: None, } } @@ -95,8 +95,13 @@ impl<'gc> Library<'gc> { } /// Returns the device font for use when a font is unavailable. - pub fn device_font(&self) -> &Font { - &*self.device_font + pub fn device_font(&self) -> Option<&Font> { + self.device_font.as_ref().map(AsRef::as_ref) + } + + /// Sets the device font. + pub fn set_device_font(&mut self, font: Option>) { + self.device_font = font; } } @@ -108,3 +113,9 @@ unsafe impl<'gc> gc_arena::Collect for Library<'gc> { } } } + +impl Default for Library<'_> { + fn default() -> Self { + Self::new() + } +} diff --git a/core/src/player.rs b/core/src/player.rs index 3b8031fd7..cb5eb15d3 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -119,8 +119,14 @@ impl // Load and parse the device font. // TODO: We could use lazy_static here. - let device_font = Self::load_device_font(DEVICE_FONT_TAG, &mut renderer) - .expect("Unable to load device font"); + 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, @@ -145,22 +151,26 @@ impl rng: SmallRng::from_seed([0u8; 16]), // TODO(Herschel): Get a proper seed on all platforms. - gc_arena: GcArena::new(ArenaParameters::default(), |gc_context| GcRoot { - library: GcCell::allocate(gc_context, Library::new(device_font)), - root: GcCell::allocate( - gc_context, - Box::new(MovieClip::new_with_data( - header.version, + gc_arena: GcArena::new(ArenaParameters::default(), |gc_context| { + let mut library = Library::new(); + library.set_device_font(device_font); + GcRoot { + library: GcCell::allocate(gc_context, library), + root: GcCell::allocate( gc_context, - 0, - 0, - swf_len, - header.num_frames, - )), - ), - mouse_hover_node: GcCell::allocate(gc_context, None), - avm: GcCell::allocate(gc_context, Avm1::new(gc_context, NEWEST_PLAYER_VERSION)), - action_queue: GcCell::allocate(gc_context, ActionQueue::new()), + Box::new(MovieClip::new_with_data( + header.version, + gc_context, + 0, + 0, + swf_len, + header.num_frames, + )), + ), + mouse_hover_node: GcCell::allocate(gc_context, None), + avm: GcCell::allocate(gc_context, Avm1::new(gc_context, NEWEST_PLAYER_VERSION)), + action_queue: GcCell::allocate(gc_context, ActionQueue::new()), + } }), frame_rate: header.frame_rate.into(),