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::{Object, TObject, Value};
|
||||
use crate::bitmap::bitmap_data::{BitmapData, ChannelOptions, Color};
|
||||
use crate::bitmap::is_size_valid;
|
||||
use crate::character::Character;
|
||||
use crate::display_object::TDisplayObject;
|
||||
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! {
|
||||
"height" => property(height);
|
||||
"width" => property(width);
|
||||
|
|
|
@ -4,19 +4,109 @@ use crate::avm2::activation::Activation;
|
|||
use crate::avm2::class::{Class, ClassAttributes};
|
||||
use crate::avm2::method::Method;
|
||||
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::Error;
|
||||
use crate::bitmap::bitmap_data::BitmapData;
|
||||
use crate::bitmap::is_size_valid;
|
||||
use crate::character::Character;
|
||||
use gc_arena::{GcCell, MutationContext};
|
||||
|
||||
/// Implements `flash.display.BitmapData`'s instance constructor.
|
||||
pub fn instance_init<'gc>(
|
||||
activation: &mut Activation<'_, 'gc, '_>,
|
||||
this: Option<Object<'gc>>,
|
||||
_args: &[Value<'gc>],
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error> {
|
||||
if let Some(this) = 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)
|
||||
|
@ -34,7 +124,7 @@ pub fn class_init<'gc>(
|
|||
/// Construct `BitmapData`'s class.
|
||||
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
|
||||
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()),
|
||||
Method::from_builtin(instance_init, "<BitmapData instance initializer>", mc),
|
||||
Method::from_builtin(class_init, "<BitmapData class initializer>", mc),
|
||||
|
|
|
@ -1,3 +1,49 @@
|
|||
pub mod bitmap_data;
|
||||
pub mod color_transform_params;
|
||||
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