core: Make Library::device_font optional
This commit is contained in:
parent
57a737357b
commit
09fa755405
|
@ -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();
|
||||
|
|
|
@ -10,15 +10,15 @@ use swf::CharacterId;
|
|||
pub struct Library<'gc> {
|
||||
characters: HashMap<CharacterId, Character<'gc>>,
|
||||
jpeg_tables: Option<Vec<u8>>,
|
||||
device_font: Box<Font>,
|
||||
device_font: Option<Box<Font>>,
|
||||
}
|
||||
|
||||
impl<'gc> Library<'gc> {
|
||||
pub fn new(device_font: Box<Font>) -> 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<Box<Font>>) {
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,8 +119,14 @@ impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend>
|
|||
|
||||
// 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<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| 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(),
|
||||
|
|
Loading…
Reference in New Issue