avm2: Made a FontObject type and switch Font to use that instead of looking up per getter

This commit is contained in:
Nathan Adams 2023-10-23 16:04:45 +02:00
parent 5682e0101d
commit 8be05d8673
5 changed files with 131 additions and 80 deletions

View File

@ -166,6 +166,7 @@ pub struct SystemClasses<'gc> {
pub shaderfilter: ClassObject<'gc>,
pub statusevent: ClassObject<'gc>,
pub contextmenuevent: ClassObject<'gc>,
pub font: ClassObject<'gc>,
}
impl<'gc> SystemClasses<'gc> {
@ -291,6 +292,7 @@ impl<'gc> SystemClasses<'gc> {
shaderfilter: object,
statusevent: object,
contextmenuevent: object,
font: object,
}
}
}
@ -794,6 +796,7 @@ fn load_playerglobal<'gc>(
("flash.net", "URLVariables", urlvariables),
("flash.utils", "ByteArray", bytearray),
("flash.system", "ApplicationDomain", application_domain),
("flash.text", "Font", font),
("flash.text", "StaticText", statictext),
("flash.text", "TextFormat", textformat),
("flash.text", "TextField", textfield),

View File

@ -1,4 +1,5 @@
package flash.text {
[Ruffle(InstanceAllocator)]
public class Font {
public static native function enumerateFonts(enumerateDeviceFonts:Boolean = false):Array;
public static native function registerFont(font:Class):void;

View File

@ -6,95 +6,56 @@ use crate::avm2::parameters::ParametersExt;
use crate::avm2::value::Value;
use crate::avm2::{ArrayObject, ArrayStorage, Error};
use crate::avm2_stub_method;
use crate::character::Character;
use crate::string::AvmString;
pub use crate::avm2::object::font_allocator;
/// Implements `Font.fontName`
pub fn get_font_name<'gc>(
activation: &mut Activation<'_, 'gc>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some((movie, character_id)) = this.instance_of().and_then(|this| {
activation
.context
.library
.avm2_class_registry()
.class_symbol(this)
}) {
if let Some(Character::Font(font)) = activation
.context
.library
.library_for_movie_mut(movie)
.character_by_id(character_id)
{
return Ok(AvmString::new_utf8(
activation.context.gc_context,
font.descriptor().name(),
)
.into());
}
if let Some(font) = this.as_font() {
return Ok(
AvmString::new_utf8(activation.context.gc_context, font.descriptor().name()).into(),
);
}
Ok(Value::Undefined)
Ok(Value::Null)
}
/// Implements `Font.fontStyle`
pub fn get_font_style<'gc>(
activation: &mut Activation<'_, 'gc>,
_activation: &mut Activation<'_, 'gc>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some((movie, character_id)) = this.instance_of().and_then(|this| {
activation
.context
.library
.avm2_class_registry()
.class_symbol(this)
}) {
if let Some(Character::Font(font)) = activation
.context
.library
.library_for_movie_mut(movie)
.character_by_id(character_id)
{
return match (font.descriptor().bold(), font.descriptor().italic()) {
(false, false) => Ok("regular".into()),
(false, true) => Ok("italic".into()),
(true, false) => Ok("bold".into()),
(true, true) => Ok("boldItalic".into()),
};
}
if let Some(font) = this.as_font() {
return match (font.descriptor().bold(), font.descriptor().italic()) {
(false, false) => Ok("regular".into()),
(false, true) => Ok("italic".into()),
(true, false) => Ok("bold".into()),
(true, true) => Ok("boldItalic".into()),
};
}
Ok(Value::Undefined)
Ok(Value::Null)
}
/// Implements `Font.fontType`
pub fn get_font_type<'gc>(
activation: &mut Activation<'_, 'gc>,
_activation: &mut Activation<'_, 'gc>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some((movie, character_id)) = this.instance_of().and_then(|this| {
activation
.context
.library
.avm2_class_registry()
.class_symbol(this)
}) {
if let Some(Character::Font(_)) = activation
.context
.library
.library_for_movie_mut(movie)
.character_by_id(character_id)
{
//TODO: How do we distinguish between CFF and non-CFF embedded fonts?
return Ok("embedded".into());
}
if let Some(_font) = this.as_font() {
// [?] How do we distinguish between CFF and non-CFF embedded fonts?
// [NA] DefineFont4 is CFF. This should be a property of Font struct
return Ok("embedded".into());
}
Ok(Value::Undefined)
Ok(Value::Null)
}
/// Implements `Font.hasGlyphs`
@ -103,26 +64,12 @@ pub fn has_glyphs<'gc>(
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some((movie, character_id)) = this.instance_of().and_then(|this| {
activation
.context
.library
.avm2_class_registry()
.class_symbol(this)
}) {
if let Some(font) = this.as_font() {
let my_str = args.get_string(activation, 0)?;
if let Some(Character::Font(font)) = activation
.context
.library
.library_for_movie_mut(movie)
.character_by_id(character_id)
{
return Ok(font.has_glyphs_for_str(&my_str).into());
}
return Ok(font.has_glyphs_for_str(&my_str).into());
}
Ok(Value::Undefined)
Ok(Value::Bool(false))
}
/// `Font.enumerateFonts`

View File

@ -40,6 +40,7 @@ mod dispatch_object;
mod domain_object;
mod error_object;
mod event_object;
mod font_object;
mod function_object;
mod index_buffer_3d_object;
mod loaderinfo_object;
@ -83,6 +84,7 @@ pub use crate::avm2::object::domain_object::{
};
pub use crate::avm2::object::error_object::{error_allocator, ErrorObject, ErrorObjectWeak};
pub use crate::avm2::object::event_object::{event_allocator, EventObject, EventObjectWeak};
pub use crate::avm2::object::font_object::{font_allocator, FontObject, FontObjectWeak};
pub use crate::avm2::object::function_object::{
function_allocator, FunctionObject, FunctionObjectWeak,
};
@ -132,6 +134,7 @@ pub use crate::avm2::object::xml_list_object::{
xml_list_allocator, E4XOrXml, XmlListObject, XmlListObjectWeak,
};
pub use crate::avm2::object::xml_object::{xml_allocator, XmlObject, XmlObjectWeak};
use crate::font::Font;
/// Represents an object that can be directly interacted with by the AVM2
/// runtime.
@ -173,7 +176,8 @@ pub use crate::avm2::object::xml_object::{xml_allocator, XmlObject, XmlObjectWea
Program3DObject(Program3DObject<'gc>),
NetStreamObject(NetStreamObject<'gc>),
ShaderDataObject(ShaderDataObject<'gc>),
SocketObject(SocketObject<'gc>)
SocketObject(SocketObject<'gc>),
FontObject(FontObject<'gc>)
}
)]
pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy {
@ -1276,6 +1280,11 @@ pub trait TObject<'gc>: 'gc + Collect + Debug + Into<Object<'gc>> + Clone + Copy
None
}
/// Unwrap this object as a font.
fn as_font(&self) -> Option<Font<'gc>> {
None
}
/// Unwrap this object as a mutable regexp.
fn as_regexp_mut(&self, _mc: &Mutation<'gc>) -> Option<RefMut<RegExp<'gc>>> {
None
@ -1424,7 +1433,8 @@ impl<'gc> Object<'gc> {
Self::Program3DObject(o) => WeakObject::Program3DObject(Program3DObjectWeak(Gc::downgrade(o.0))),
Self::NetStreamObject(o) => WeakObject::NetStreamObject(NetStreamObjectWeak(GcCell::downgrade(o.0))),
Self::ShaderDataObject(o) => WeakObject::ShaderDataObject(ShaderDataObjectWeak(Gc::downgrade(o.0))),
Self::SocketObject(o) => WeakObject::SocketObject(SocketObjectWeak(Gc::downgrade(o.0)))
Self::SocketObject(o) => WeakObject::SocketObject(SocketObjectWeak(Gc::downgrade(o.0))),
Self::FontObject(o) => WeakObject::FontObject(FontObjectWeak(GcCell::downgrade(o.0))),
}
}
}
@ -1481,6 +1491,7 @@ pub enum WeakObject<'gc> {
NetStreamObject(NetStreamObjectWeak<'gc>),
ShaderDataObject(ShaderDataObjectWeak<'gc>),
SocketObject(SocketObjectWeak<'gc>),
FontObject(FontObjectWeak<'gc>),
}
impl<'gc> WeakObject<'gc> {
@ -1520,6 +1531,7 @@ impl<'gc> WeakObject<'gc> {
Self::NetStreamObject(o) => NetStreamObject(o.0.upgrade(mc)?).into(),
Self::ShaderDataObject(o) => ShaderDataObject(o.0.upgrade(mc)?).into(),
Self::SocketObject(o) => SocketObject(o.0.upgrade(mc)?).into(),
Self::FontObject(o) => FontObject(o.0.upgrade(mc)?).into(),
})
}
}

View File

@ -0,0 +1,88 @@
use crate::avm2::object::script_object::ScriptObjectData;
use crate::avm2::object::{Object, ObjectPtr, TObject};
use crate::avm2::value::Value;
use crate::avm2::{Activation, ClassObject, Error};
use crate::character::Character;
use crate::font::Font;
use gc_arena::Mutation;
use gc_arena::{Collect, GcCell, GcWeakCell};
use std::cell::{Ref, RefMut};
use std::fmt;
/// A class instance allocator that allocates Font objects.
pub fn font_allocator<'gc>(
class: ClassObject<'gc>,
activation: &mut Activation<'_, 'gc>,
) -> Result<Object<'gc>, Error<'gc>> {
let base = ScriptObjectData::new(class);
let font = if let Some((movie, id)) = activation
.context
.library
.avm2_class_registry()
.class_symbol(class)
{
if let Some(lib) = activation.context.library.library_for_movie(movie) {
if let Some(Character::Font(font)) = lib.character_by_id(id) {
Some(*font)
} else {
None
}
} else {
None
}
} else {
None
};
Ok(FontObject(GcCell::new(
activation.context.gc_context,
FontObjectData { base, font },
))
.into())
}
#[derive(Clone, Collect, Copy)]
#[collect(no_drop)]
pub struct FontObject<'gc>(pub GcCell<'gc, FontObjectData<'gc>>);
#[derive(Clone, Collect, Copy, Debug)]
#[collect(no_drop)]
pub struct FontObjectWeak<'gc>(pub GcWeakCell<'gc, FontObjectData<'gc>>);
impl<'gc> TObject<'gc> for FontObject<'gc> {
fn base(&self) -> Ref<ScriptObjectData<'gc>> {
Ref::map(self.0.read(), |read| &read.base)
}
fn base_mut(&self, mc: &Mutation<'gc>) -> RefMut<ScriptObjectData<'gc>> {
RefMut::map(self.0.write(mc), |write| &mut write.base)
}
fn as_ptr(&self) -> *const ObjectPtr {
self.0.as_ptr() as *const ObjectPtr
}
fn value_of(&self, _mc: &Mutation<'gc>) -> Result<Value<'gc>, Error<'gc>> {
Ok(Value::Object(Object::from(*self)))
}
fn as_font(&self) -> Option<Font<'gc>> {
self.0.read().font
}
}
#[derive(Collect)]
#[collect(no_drop)]
pub struct FontObjectData<'gc> {
/// Base script object
base: ScriptObjectData<'gc>,
font: Option<Font<'gc>>,
}
impl fmt::Debug for FontObject<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FontObject")
}
}