avm2: Implement `BitmapData`'s constructor.
This includes support for both embedded bitmap data (resolved via the SymbolClass mechanism) as well as empty bitmaps configured via arguments.
This commit is contained in:
parent
e5151d147d
commit
335aec5be0
|
@ -7,41 +7,11 @@ use crate::avm1::object::bitmap_data::BitmapDataObject;
|
||||||
use crate::avm1::property_decl::{define_properties_on, Declaration};
|
use crate::avm1::property_decl::{define_properties_on, Declaration};
|
||||||
use crate::avm1::{Object, TObject, Value};
|
use crate::avm1::{Object, TObject, Value};
|
||||||
use crate::bitmap::bitmap_data::{BitmapData, ChannelOptions, Color};
|
use crate::bitmap::bitmap_data::{BitmapData, ChannelOptions, Color};
|
||||||
|
use crate::bitmap::is_size_valid;
|
||||||
use crate::character::Character;
|
use crate::character::Character;
|
||||||
use crate::display_object::TDisplayObject;
|
use crate::display_object::TDisplayObject;
|
||||||
use gc_arena::{GcCell, MutationContext};
|
use gc_arena::{GcCell, MutationContext};
|
||||||
|
|
||||||
fn is_size_valid(swf_version: u8, width: u32, height: u32) -> bool {
|
|
||||||
// From https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html:
|
|
||||||
// "In AIR 1.5 and Flash Player 10, the maximum size for a BitmapData object is 8,191 pixels in
|
|
||||||
// width or height, and the total number of pixels cannot exceed 16,777,215 pixels. (So, if a
|
|
||||||
// BitmapData object is 8,191 pixels wide, it can only be 2,048 pixels high.) In Flash Player 9
|
|
||||||
// and earlier and AIR 1.1 and earlier, the limitation is 2,880 pixels in height and 2,880 in width.
|
|
||||||
// Starting with AIR 3 and Flash player 11, the size limits for a BitmapData object have been removed.
|
|
||||||
// The maximum size of a bitmap is now dependent on the operating system."
|
|
||||||
//
|
|
||||||
// In addition, width and height of 0 are invalid in all versions.
|
|
||||||
if width == 0 || height == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if swf_version <= 9 {
|
|
||||||
if width > 2880 || height > 2880 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else if swf_version <= 12 {
|
|
||||||
if width >= 0x2000 || height >= 0x2000 || width * height >= 0x1000000 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// These limits are undocumented, but seem to be reliable.
|
|
||||||
// TODO: Do they vary across different machines?
|
|
||||||
if width > 0x6666666 || height > 0x6666666 || width as u64 * height as u64 >= 0x20000000 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
const PROTO_DECLS: &[Declaration] = declare_properties! {
|
const PROTO_DECLS: &[Declaration] = declare_properties! {
|
||||||
"height" => property(height);
|
"height" => property(height);
|
||||||
"width" => property(width);
|
"width" => property(width);
|
||||||
|
|
|
@ -4,19 +4,109 @@ use crate::avm2::activation::Activation;
|
||||||
use crate::avm2::class::{Class, ClassAttributes};
|
use crate::avm2::class::{Class, ClassAttributes};
|
||||||
use crate::avm2::method::Method;
|
use crate::avm2::method::Method;
|
||||||
use crate::avm2::names::{Namespace, QName};
|
use crate::avm2::names::{Namespace, QName};
|
||||||
use crate::avm2::object::{bitmapdata_allocator, Object};
|
use crate::avm2::object::{bitmapdata_allocator, Object, TObject};
|
||||||
use crate::avm2::value::Value;
|
use crate::avm2::value::Value;
|
||||||
use crate::avm2::Error;
|
use crate::avm2::Error;
|
||||||
|
use crate::bitmap::bitmap_data::BitmapData;
|
||||||
|
use crate::bitmap::is_size_valid;
|
||||||
|
use crate::character::Character;
|
||||||
use gc_arena::{GcCell, MutationContext};
|
use gc_arena::{GcCell, MutationContext};
|
||||||
|
|
||||||
/// Implements `flash.display.BitmapData`'s instance constructor.
|
/// Implements `flash.display.BitmapData`'s instance constructor.
|
||||||
pub fn instance_init<'gc>(
|
pub fn instance_init<'gc>(
|
||||||
activation: &mut Activation<'_, 'gc, '_>,
|
activation: &mut Activation<'_, 'gc, '_>,
|
||||||
this: Option<Object<'gc>>,
|
this: Option<Object<'gc>>,
|
||||||
_args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<Value<'gc>, Error> {
|
) -> Result<Value<'gc>, Error> {
|
||||||
if let Some(this) = this {
|
if let Some(this) = this {
|
||||||
activation.super_init(this, &[])?;
|
activation.super_init(this, &[])?;
|
||||||
|
|
||||||
|
let name = this
|
||||||
|
.as_class_object()
|
||||||
|
.and_then(|t| t.as_class())
|
||||||
|
.map(|c| c.read().name().clone());
|
||||||
|
let character = this
|
||||||
|
.as_class_object()
|
||||||
|
.and_then(|t| {
|
||||||
|
activation
|
||||||
|
.context
|
||||||
|
.library
|
||||||
|
.avm2_class_registry()
|
||||||
|
.class_symbol(t)
|
||||||
|
})
|
||||||
|
.and_then(|(movie, chara_id)| {
|
||||||
|
activation
|
||||||
|
.context
|
||||||
|
.library
|
||||||
|
.library_for_movie_mut(movie)
|
||||||
|
.character_by_id(chara_id)
|
||||||
|
.cloned()
|
||||||
|
});
|
||||||
|
|
||||||
|
let new_bitmap_data =
|
||||||
|
GcCell::allocate(activation.context.gc_context, BitmapData::default());
|
||||||
|
|
||||||
|
if let Some(Character::Bitmap(bd)) = character {
|
||||||
|
let bitmap_handle = bd.bitmap_handle();
|
||||||
|
|
||||||
|
if let Some(bitmap_pixels) =
|
||||||
|
activation.context.renderer.get_bitmap_pixels(bitmap_handle)
|
||||||
|
{
|
||||||
|
let bitmap_pixels: Vec<i32> = bitmap_pixels.data.into();
|
||||||
|
new_bitmap_data
|
||||||
|
.write(activation.context.gc_context)
|
||||||
|
.set_pixels(
|
||||||
|
bd.width().into(),
|
||||||
|
bd.height().into(),
|
||||||
|
true,
|
||||||
|
bitmap_pixels.into_iter().map(|p| p.into()).collect(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log::warn!(
|
||||||
|
"Could not read bitmap data associated with class {:?}",
|
||||||
|
name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if character.is_some() {
|
||||||
|
//TODO: Determine if mismatched symbols will still work as a
|
||||||
|
//regular BitmapData subclass, or if this should throw
|
||||||
|
log::warn!(
|
||||||
|
"BitmapData subclass {:?} is associated with a non-bitmap symbol",
|
||||||
|
name
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = args
|
||||||
|
.get(0)
|
||||||
|
.unwrap_or(&Value::Undefined)
|
||||||
|
.coerce_to_i32(activation)? as u32;
|
||||||
|
let height = args
|
||||||
|
.get(1)
|
||||||
|
.unwrap_or(&Value::Undefined)
|
||||||
|
.coerce_to_i32(activation)? as u32;
|
||||||
|
let transparency = args
|
||||||
|
.get(2)
|
||||||
|
.unwrap_or(&Value::Bool(true))
|
||||||
|
.coerce_to_boolean();
|
||||||
|
let fill_color = args
|
||||||
|
.get(3)
|
||||||
|
.unwrap_or(&Value::Unsigned(0xFFFFFFFF))
|
||||||
|
.coerce_to_u32(activation)?;
|
||||||
|
|
||||||
|
if !is_size_valid(activation.context.swf.version(), width, height) {
|
||||||
|
return Err("Bitmap size is not valid".into());
|
||||||
|
}
|
||||||
|
|
||||||
|
new_bitmap_data
|
||||||
|
.write(activation.context.gc_context)
|
||||||
|
.init_pixels(width, height, transparency, fill_color as i32);
|
||||||
|
}
|
||||||
|
|
||||||
|
new_bitmap_data
|
||||||
|
.write(activation.context.gc_context)
|
||||||
|
.init_object2(this);
|
||||||
|
this.init_bitmap_data(activation.context.gc_context, new_bitmap_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Value::Undefined)
|
Ok(Value::Undefined)
|
||||||
|
@ -34,7 +124,7 @@ pub fn class_init<'gc>(
|
||||||
/// Construct `BitmapData`'s class.
|
/// Construct `BitmapData`'s class.
|
||||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||||
let class = Class::new(
|
let class = Class::new(
|
||||||
QName::new(Namespace::package("flash.media"), "BitmapData"),
|
QName::new(Namespace::package("flash.display"), "BitmapData"),
|
||||||
Some(QName::new(Namespace::package(""), "Object").into()),
|
Some(QName::new(Namespace::package(""), "Object").into()),
|
||||||
Method::from_builtin(instance_init, "<BitmapData instance initializer>", mc),
|
Method::from_builtin(instance_init, "<BitmapData instance initializer>", mc),
|
||||||
Method::from_builtin(class_init, "<BitmapData class initializer>", mc),
|
Method::from_builtin(class_init, "<BitmapData class initializer>", mc),
|
||||||
|
|
|
@ -1,3 +1,49 @@
|
||||||
pub mod bitmap_data;
|
pub mod bitmap_data;
|
||||||
pub mod color_transform_params;
|
pub mod color_transform_params;
|
||||||
pub mod turbulence;
|
pub mod turbulence;
|
||||||
|
|
||||||
|
/// Determine if a particular bitmap data size is valid.
|
||||||
|
///
|
||||||
|
/// This enforces limits on BitmapData as specified in the Flash documentation.
|
||||||
|
/// Specifically, from https://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display/BitmapData.html:
|
||||||
|
///
|
||||||
|
/// "In AIR 1.5 and Flash Player 10, the maximum size for a BitmapData object
|
||||||
|
/// is 8,191 pixels in width or height, and the total number of pixels cannot
|
||||||
|
/// exceed 16,777,215 pixels. (So, if a BitmapData object is 8,191 pixels wide,
|
||||||
|
/// it can only be 2,048 pixels high.) In Flash Player 9 and earlier and
|
||||||
|
/// AIR 1.1 and earlier, the limitation is 2,880 pixels in height and 2,880 in
|
||||||
|
/// width. Starting with AIR 3 and Flash player 11, the size limits for a
|
||||||
|
/// BitmapData object have been removed. The maximum size of a bitmap is now
|
||||||
|
/// dependent on the operating system."
|
||||||
|
///
|
||||||
|
/// In addition, we found the following undocumented limits:
|
||||||
|
///
|
||||||
|
/// - Width and height of 0 are invalid in all versions
|
||||||
|
/// - Widths and heights exceeding 0x666666 are invalid in all versions
|
||||||
|
/// - Pixel counts (of any width/height) exceeding 0x20000000 pixels
|
||||||
|
///
|
||||||
|
/// All of these are curently enforced.
|
||||||
|
pub fn is_size_valid(swf_version: u8, width: u32, height: u32) -> bool {
|
||||||
|
// From :
|
||||||
|
//
|
||||||
|
// In addition, width and height of 0 are invalid in all versions.
|
||||||
|
if width == 0 || height == 0 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if swf_version <= 9 {
|
||||||
|
if width > 2880 || height > 2880 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if swf_version <= 12 {
|
||||||
|
if width >= 0x2000 || height >= 0x2000 || width * height >= 0x1000000 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// These limits are undocumented, but seem to be reliable.
|
||||||
|
// TODO: Do they vary across different machines?
|
||||||
|
if width > 0x6666666 || height > 0x6666666 || width as u64 * height as u64 >= 0x20000000 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue