core: Wrap Font in a Gc

This commit is contained in:
Mike Welsh 2019-12-16 21:21:59 -08:00
parent 289e0b8aff
commit 0f3bad8f1b
5 changed files with 71 additions and 60 deletions

View File

@ -9,7 +9,7 @@ pub enum Character<'gc> {
MovieClip(MovieClip<'gc>),
Bitmap(Bitmap<'gc>),
Button(Button<'gc>),
Font(Box<Font>),
Font(Font<'gc>),
MorphShape(MorphShape<'gc>),
Text(Text<'gc>),
Sound(SoundHandle),

View File

@ -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(())
}

View File

@ -1,10 +1,16 @@
use crate::backend::render::{RenderBackend, ShapeHandle};
use crate::prelude::*;
use gc_arena::{Collect, Gc, MutationContext};
type Error = Box<dyn std::error::Error>;
#[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<Glyph>,
@ -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<Font, Error> {
impl<'gc> Font<'gc> {
pub fn from_swf_tag(
gc_context: MutationContext<'gc, '_>,
renderer: &mut dyn RenderBackend,
tag: &swf::Font,
) -> Result<Font<'gc>, Error> {
let mut glyphs = vec![];
let mut code_point_to_glyph = fnv::FnvHashMap::default();
for swf_glyph in &tag.glyphs {
@ -44,7 +54,9 @@ impl Font {
} else {
fnv::FnvHashMap::default()
};
Ok(Font {
Ok(Font(Gc::allocate(
gc_context,
FontData {
glyphs,
code_point_to_glyph,
@ -52,28 +64,28 @@ impl Font {
/// (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<Glyph> {
self.glyphs.get(i).cloned()
pub fn get_glyph(self, i: usize) -> Option<Glyph> {
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<Glyph> {
pub fn get_glyph_for_char(self, c: char) -> Option<Glyph> {
// 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
}
}

View File

@ -13,7 +13,7 @@ pub struct Library<'gc> {
characters: HashMap<CharacterId, Character<'gc>>,
export_characters: HashMap<String, Character<'gc>>,
jpeg_tables: Option<Vec<u8>>,
device_font: Option<Box<Font>>,
device_font: Option<Font<'gc>>,
}
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<Font<'gc>> {
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<Font<'gc>> {
self.device_font
}
/// Sets the device font.
pub fn set_device_font(&mut self, font: Option<Box<Font>>) {
pub fn set_device_font(&mut self, font: Option<Font<'gc>>) {
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);
}
}

View File

@ -141,16 +141,6 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
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<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
is_playing: false,
renderer,
audio,
navigator,
background_color: Color {
r: 255,
g: 255,
@ -176,6 +162,16 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
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<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
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<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
/// 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<Box<crate::font::Font>, Error> {
) -> Result<crate::font::Font<'gc>, 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)
}
}