avm1: Migrate `BitmapData` to `NativeObject`

This commit is contained in:
relrelb 2023-05-15 21:55:15 +03:00
parent 5d48484d99
commit c230dcf0f8
11 changed files with 319 additions and 450 deletions

View File

@ -508,7 +508,6 @@ pub struct SystemPrototypes<'gc> {
pub gradient_glow_filter: Object<'gc>, pub gradient_glow_filter: Object<'gc>,
pub gradient_glow_filter_constructor: Object<'gc>, pub gradient_glow_filter_constructor: Object<'gc>,
pub date_constructor: Object<'gc>, pub date_constructor: Object<'gc>,
pub bitmap_data: Object<'gc>,
pub bitmap_data_constructor: Object<'gc>, pub bitmap_data_constructor: Object<'gc>,
pub video: Object<'gc>, pub video: Object<'gc>,
pub video_constructor: Object<'gc>, pub video_constructor: Object<'gc>,
@ -863,9 +862,7 @@ pub fn create_globals<'gc>(
Attribute::empty(), Attribute::empty(),
); );
let bitmap_data_proto = bitmap_data::create_proto(context, object_proto, function_proto); let bitmap_data = bitmap_data::create_constructor(context, object_proto, function_proto);
let bitmap_data =
bitmap_data::create_bitmap_data_object(context, bitmap_data_proto, function_proto);
display.define_value( display.define_value(
gc_context, gc_context,
@ -1130,7 +1127,6 @@ pub fn create_globals<'gc>(
gradient_glow_filter: gradient_glow_filter_proto, gradient_glow_filter: gradient_glow_filter_proto,
gradient_glow_filter_constructor: gradient_glow_filter, gradient_glow_filter_constructor: gradient_glow_filter,
date_constructor: date, date_constructor: date,
bitmap_data: bitmap_data_proto,
bitmap_data_constructor: bitmap_data, bitmap_data_constructor: bitmap_data,
video: video_proto, video: video_proto,
video_constructor: video, video_constructor: video,

View File

@ -3,17 +3,19 @@
use super::matrix::object_to_matrix; use super::matrix::object_to_matrix;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::globals::color_transform::ColorTransformObject; use crate::avm1::globals::color_transform::ColorTransformObject;
use crate::avm1::object::bitmap_data::BitmapDataObject; use crate::avm1::object::NativeObject;
use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::property_decl::{define_properties_on, Declaration};
use crate::avm1::{Activation, Error, Object, TObject, Value}; use crate::avm1::{Activation, Attribute, Error, Object, ScriptObject, TObject, Value};
use crate::bitmap::bitmap_data::{BitmapData, BitmapDataWrapper};
use crate::bitmap::bitmap_data::{BitmapDataDrawError, IBitmapDrawable}; use crate::bitmap::bitmap_data::{BitmapDataDrawError, IBitmapDrawable};
use crate::bitmap::bitmap_data::{ChannelOptions, ThresholdOperation}; use crate::bitmap::bitmap_data::{ChannelOptions, ThresholdOperation};
use crate::bitmap::{is_size_valid, operations}; use crate::bitmap::{is_size_valid, operations};
use crate::character::Character; use crate::character::Character;
use crate::context::GcContext; use crate::context::GcContext;
use crate::display_object::TDisplayObject; use crate::display_object::DisplayObject;
use crate::swf::BlendMode; use crate::swf::BlendMode;
use crate::{avm1_stub, avm_error}; use crate::{avm1_stub, avm_error};
use gc_arena::{GcCell, MutationContext};
use ruffle_render::transform::Transform; use ruffle_render::transform::Transform;
use std::str::FromStr; use std::str::FromStr;
@ -52,93 +54,117 @@ const OBJECT_DECLS: &[Declaration] = declare_properties! {
"loadBitmap" => method(load_bitmap); "loadBitmap" => method(load_bitmap);
}; };
pub fn constructor<'gc>( fn new_bitmap_data<'gc>(
gc_context: MutationContext<'gc, '_>,
proto: Option<Value<'gc>>,
bitmap_data: BitmapData<'gc>,
) -> ScriptObject<'gc> {
let object = ScriptObject::new(gc_context, None);
// Set `__proto__` manually since `ScriptObject::new()` doesn't support primitive prototypes.
// TODO: Pass `proto` to `ScriptObject::new()` once possible.
if let Some(proto) = proto {
object.define_value(
gc_context,
"__proto__",
proto,
Attribute::DONT_ENUM | Attribute::DONT_DELETE,
);
}
object.set_native(
gc_context,
NativeObject::BitmapData(BitmapDataWrapper::new(GcCell::allocate(
gc_context,
bitmap_data,
))),
);
object
}
fn constructor<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let width = args.get(0).unwrap_or(&0.into()).coerce_to_i32(activation)? as u32; let (width, height) = match args {
[width, height, ..] => (
let height = args.get(1).unwrap_or(&0.into()).coerce_to_i32(activation)? as u32; width.coerce_to_u32(activation)?,
height.coerce_to_u32(activation)?,
let transparency = args ),
.get(2) [] | [_] => return Ok(Value::Undefined),
.unwrap_or(&true.into()) };
.as_bool(activation.swf_version()); let transparency = match args.get(2) {
Some(transparency) => transparency.as_bool(activation.swf_version()),
let fill_color = args None => true,
.get(3) };
.unwrap_or(&(-1).into()) let fill_color = match args.get(3) {
.coerce_to_i32(activation)?; Some(fill_color) => fill_color.coerce_to_i32(activation)?,
None => -1,
};
if !is_size_valid(activation.swf_version(), width, height) { if !is_size_valid(activation.swf_version(), width, height) {
tracing::warn!("Invalid BitmapData size: {}x{}", width, height); tracing::warn!("Invalid BitmapData size: {}x{}", width, height);
return Ok(Value::Undefined); return Ok(Value::Undefined);
} }
if let Some(bitmap_data) = this.as_bitmap_data_object() { let bitmap_data = BitmapData::new(width, height, transparency, fill_color);
let (sync, _) = bitmap_data this.set_native(
.bitmap_data() activation.context.gc_context,
.overwrite_cpu_pixels_from_gpu(activation.context.gc_context); NativeObject::BitmapData(BitmapDataWrapper::new(GcCell::allocate(
sync.write(activation.context.gc_context).init_pixels( activation.context.gc_context,
width, bitmap_data,
height, ))),
transparency,
fill_color,
); );
}
Ok(this.into()) Ok(this.into())
} }
pub fn height<'gc>( fn height<'gc>(
_activation: &mut Activation<'_, 'gc>, _activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
return Ok(bitmap_data.bitmap_data().height().into()); return Ok(bitmap_data.height().into());
} }
} }
Ok((-1).into()) Ok((-1).into())
} }
pub fn width<'gc>( fn width<'gc>(
_activation: &mut Activation<'_, 'gc>, _activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
return Ok(bitmap_data.bitmap_data().width().into()); return Ok(bitmap_data.width().into());
} }
} }
Ok((-1).into()) Ok((-1).into())
} }
pub fn get_transparent<'gc>( fn get_transparent<'gc>(
_activation: &mut Activation<'_, 'gc>, _activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
return Ok(bitmap_data.bitmap_data().transparency().into()); return Ok(bitmap_data.transparency().into());
} }
} }
Ok((-1).into()) Ok((-1).into())
} }
pub fn get_rectangle<'gc>( fn get_rectangle<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let proto = activation.context.avm1.prototypes().rectangle_constructor; let proto = activation.context.avm1.prototypes().rectangle_constructor;
let rect = proto.construct( let rect = proto.construct(
@ -157,17 +183,17 @@ pub fn get_rectangle<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn get_pixel<'gc>( fn get_pixel<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
if let (Some(x_val), Some(y_val)) = (args.get(0), args.get(1)) { if let (Some(x_val), Some(y_val)) = (args.get(0), args.get(1)) {
let x = x_val.coerce_to_u32(activation)?; let x = x_val.coerce_to_u32(activation)?;
let y = y_val.coerce_to_u32(activation)?; let y = y_val.coerce_to_u32(activation)?;
let col = operations::get_pixel(bitmap_data.bitmap_data(), x, y); let col = operations::get_pixel(bitmap_data, x, y);
return Ok(col.into()); return Ok(col.into());
} }
} }
@ -176,17 +202,17 @@ pub fn get_pixel<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn get_pixel32<'gc>( fn get_pixel32<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
if let (Some(x_val), Some(y_val)) = (args.get(0), args.get(1)) { if let (Some(x_val), Some(y_val)) = (args.get(0), args.get(1)) {
let x = x_val.coerce_to_u32(activation)?; let x = x_val.coerce_to_u32(activation)?;
let y = y_val.coerce_to_u32(activation)?; let y = y_val.coerce_to_u32(activation)?;
let col = operations::get_pixel32(bitmap_data.bitmap_data(), x, y); let col = operations::get_pixel32(bitmap_data, x, y);
return Ok(col.into()); return Ok(col.into());
} }
} }
@ -195,12 +221,12 @@ pub fn get_pixel32<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn set_pixel<'gc>( fn set_pixel<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
if let (Some(x_val), Some(y_val), Some(color_val)) = if let (Some(x_val), Some(y_val), Some(color_val)) =
(args.get(0), args.get(1), args.get(2)) (args.get(0), args.get(1), args.get(2))
@ -211,7 +237,7 @@ pub fn set_pixel<'gc>(
operations::set_pixel( operations::set_pixel(
activation.context.gc_context, activation.context.gc_context,
bitmap_data.bitmap_data(), bitmap_data,
x, x,
y, y,
color.into(), color.into(),
@ -225,12 +251,12 @@ pub fn set_pixel<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn set_pixel32<'gc>( fn set_pixel32<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
if let (Some(x_val), Some(y_val), Some(color_val)) = if let (Some(x_val), Some(y_val), Some(color_val)) =
(args.get(0), args.get(1), args.get(2)) (args.get(0), args.get(1), args.get(2))
@ -239,13 +265,7 @@ pub fn set_pixel32<'gc>(
let y = y_val.coerce_to_u32(activation)?; let y = y_val.coerce_to_u32(activation)?;
let color = color_val.coerce_to_i32(activation)?; let color = color_val.coerce_to_i32(activation)?;
operations::set_pixel32( operations::set_pixel32(activation.context.gc_context, bitmap_data, x, y, color);
activation.context.gc_context,
bitmap_data.bitmap_data(),
x,
y,
color,
);
} }
return Ok(Value::Undefined); return Ok(Value::Undefined);
@ -255,7 +275,7 @@ pub fn set_pixel32<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn copy_channel<'gc>( fn copy_channel<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
@ -285,9 +305,9 @@ pub fn copy_channel<'gc>(
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_i32(activation)?; .coerce_to_i32(activation)?;
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
if let Some(source_bitmap) = source_bitmap.as_bitmap_data_object() { if let NativeObject::BitmapData(source_bitmap) = source_bitmap.native() {
//TODO: what if source is disposed //TODO: what if source is disposed
let min_x = dest_point.get("x", activation)?.coerce_to_i32(activation)?; let min_x = dest_point.get("x", activation)?.coerce_to_i32(activation)?;
let min_y = dest_point.get("y", activation)?.coerce_to_i32(activation)?; let min_y = dest_point.get("y", activation)?.coerce_to_i32(activation)?;
@ -307,10 +327,10 @@ pub fn copy_channel<'gc>(
operations::copy_channel( operations::copy_channel(
activation.context.gc_context, activation.context.gc_context,
bitmap_data.bitmap_data(), bitmap_data,
(min_x, min_y), (min_x, min_y),
(src_min_x, src_min_y, src_width, src_height), (src_min_x, src_min_y, src_width, src_height),
source_bitmap.bitmap_data(), source_bitmap,
source_channel, source_channel,
dest_channel, dest_channel,
); );
@ -323,7 +343,7 @@ pub fn copy_channel<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn fill_rect<'gc>( fn fill_rect<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
@ -333,7 +353,7 @@ pub fn fill_rect<'gc>(
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_object(activation); .coerce_to_object(activation);
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
if let Some(color_val) = args.get(1) { if let Some(color_val) = args.get(1) {
let color = color_val.coerce_to_i32(activation)?; let color = color_val.coerce_to_i32(activation)?;
@ -349,7 +369,7 @@ pub fn fill_rect<'gc>(
operations::fill_rect( operations::fill_rect(
activation.context.gc_context, activation.context.gc_context,
bitmap_data.bitmap_data(), bitmap_data,
x, x,
y, y,
width, width,
@ -364,35 +384,33 @@ pub fn fill_rect<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn clone<'gc>( fn clone<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let new_bitmap_data = operations::clone(bitmap_data.bitmap_data()); return Ok(new_bitmap_data(
let new_bitmap_data = BitmapDataObject::with_bitmap_data(
activation.context.gc_context, activation.context.gc_context,
activation.context.avm1.prototypes().bitmap_data, this.get_local_stored("__proto__", activation),
new_bitmap_data, operations::clone(bitmap_data),
); )
.into());
return Ok(new_bitmap_data.into());
} }
} }
Ok((-1).into()) Ok((-1).into())
} }
pub fn dispose<'gc>( fn dispose<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
bitmap_data.dispose(&mut activation.context); bitmap_data.dispose(activation.context.gc_context);
return Ok(Value::Undefined); return Ok(Value::Undefined);
} }
} }
@ -400,12 +418,12 @@ pub fn dispose<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn flood_fill<'gc>( fn flood_fill<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
if let (Some(x_val), Some(y_val), Some(color_val)) = if let (Some(x_val), Some(y_val), Some(color_val)) =
(args.get(0), args.get(1), args.get(2)) (args.get(0), args.get(1), args.get(2))
@ -414,13 +432,7 @@ pub fn flood_fill<'gc>(
let y = y_val.coerce_to_u32(activation)?; let y = y_val.coerce_to_u32(activation)?;
let color = color_val.coerce_to_i32(activation)?; let color = color_val.coerce_to_i32(activation)?;
operations::flood_fill( operations::flood_fill(activation.context.gc_context, bitmap_data, x, y, color);
activation.context.gc_context,
bitmap_data.bitmap_data(),
x,
y,
color,
);
} }
return Ok(Value::Undefined); return Ok(Value::Undefined);
} }
@ -429,7 +441,7 @@ pub fn flood_fill<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn noise<'gc>( fn noise<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
@ -452,13 +464,13 @@ pub fn noise<'gc>(
.unwrap_or(&false.into()) .unwrap_or(&false.into())
.as_bool(activation.swf_version()); .as_bool(activation.swf_version());
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
if let Some(random_seed_val) = args.get(0) { if let Some(random_seed_val) = args.get(0) {
let random_seed = random_seed_val.coerce_to_i32(activation)?; let random_seed = random_seed_val.coerce_to_i32(activation)?;
operations::noise( operations::noise(
activation.context.gc_context, activation.context.gc_context,
bitmap_data.bitmap_data(), bitmap_data,
random_seed, random_seed,
low, low,
high.max(low), high.max(low),
@ -474,12 +486,12 @@ pub fn noise<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn draw<'gc>( fn draw<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let matrix = args let matrix = args
.get(1) .get(1)
@ -521,8 +533,8 @@ pub fn draw<'gc>(
.coerce_to_object(activation); .coerce_to_object(activation);
let source = if let Some(source_object) = source.as_display_object() { let source = if let Some(source_object) = source.as_display_object() {
IBitmapDrawable::DisplayObject(source_object) IBitmapDrawable::DisplayObject(source_object)
} else if let Some(source_bitmap) = source.as_bitmap_data_object() { } else if let NativeObject::BitmapData(source_bitmap) = source.native() {
IBitmapDrawable::BitmapData(source_bitmap.bitmap_data()) IBitmapDrawable::BitmapData(source_bitmap)
} else { } else {
avm_error!( avm_error!(
activation, activation,
@ -538,7 +550,7 @@ pub fn draw<'gc>(
let quality = activation.context.stage.quality(); let quality = activation.context.stage.quality();
match operations::draw( match operations::draw(
&mut activation.context, &mut activation.context,
bitmap_data.bitmap_data(), bitmap_data,
source, source,
Transform { Transform {
matrix, matrix,
@ -564,7 +576,7 @@ pub fn draw<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn apply_filter<'gc>( fn apply_filter<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
_this: Object<'gc>, _this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
@ -573,12 +585,12 @@ pub fn apply_filter<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn generate_filter_rect<'gc>( fn generate_filter_rect<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
_args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
avm1_stub!(activation, "BitmapData", "generateFilterRect"); avm1_stub!(activation, "BitmapData", "generateFilterRect");
return Ok(Value::Undefined); return Ok(Value::Undefined);
@ -588,12 +600,12 @@ pub fn generate_filter_rect<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn color_transform<'gc>( fn color_transform<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
if let [rectangle, color_transform, ..] = args { if let [rectangle, color_transform, ..] = args {
// TODO: Re-use `object_to_rectangle` in `movie_clip.rs`. // TODO: Re-use `object_to_rectangle` in `movie_clip.rs`.
@ -619,7 +631,7 @@ pub fn color_transform<'gc>(
operations::color_transform( operations::color_transform(
activation.context.gc_context, activation.context.gc_context,
bitmap_data.bitmap_data(), bitmap_data,
x_min, x_min,
y_min, y_min,
x_max, x_max,
@ -633,12 +645,12 @@ pub fn color_transform<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn get_color_bounds_rect<'gc>( fn get_color_bounds_rect<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let find_color = args let find_color = args
.get(2) .get(2)
@ -649,12 +661,8 @@ pub fn get_color_bounds_rect<'gc>(
let mask = mask_val.coerce_to_i32(activation)?; let mask = mask_val.coerce_to_i32(activation)?;
let color = color_val.coerce_to_i32(activation)?; let color = color_val.coerce_to_i32(activation)?;
let (x, y, w, h) = operations::color_bounds_rect( let (x, y, w, h) =
bitmap_data.bitmap_data(), operations::color_bounds_rect(bitmap_data, find_color, mask, color);
find_color,
mask,
color,
);
let proto = activation.context.avm1.prototypes().rectangle_constructor; let proto = activation.context.avm1.prototypes().rectangle_constructor;
let rect = let rect =
@ -667,12 +675,12 @@ pub fn get_color_bounds_rect<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn perlin_noise<'gc>( fn perlin_noise<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let base_x = args let base_x = args
.get(0) .get(0)
@ -727,7 +735,7 @@ pub fn perlin_noise<'gc>(
operations::perlin_noise( operations::perlin_noise(
activation.context.gc_context, activation.context.gc_context,
bitmap_data.bitmap_data(), bitmap_data,
(base_x, base_y), (base_x, base_y),
num_octaves, num_octaves,
seed, seed,
@ -743,12 +751,12 @@ pub fn perlin_noise<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn hit_test<'gc>( fn hit_test<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let first_point = args let first_point = args
.get(0) .get(0)
@ -776,7 +784,7 @@ pub fn hit_test<'gc>(
// Overload based on the object we are hit-testing against. // Overload based on the object we are hit-testing against.
// BitmapData vs. BitmapData // BitmapData vs. BitmapData
if let Some(other_bmd) = compare_object.as_bitmap_data_object() { if let NativeObject::BitmapData(other_bmd) = compare_object.native() {
if other_bmd.disposed() { if other_bmd.disposed() {
return Ok((-3).into()); return Ok((-3).into());
} }
@ -801,10 +809,10 @@ pub fn hit_test<'gc>(
.clamp(0, u8::MAX.into()) as u8; .clamp(0, u8::MAX.into()) as u8;
let result = operations::hit_test_bitmapdata( let result = operations::hit_test_bitmapdata(
bitmap_data.bitmap_data(), bitmap_data,
top_left, top_left,
source_threshold, source_threshold,
other_bmd.bitmap_data(), other_bmd,
second_point, second_point,
second_threshold, second_threshold,
); );
@ -826,7 +834,7 @@ pub fn hit_test<'gc>(
test_y.coerce_to_i32(activation)? - top_left.1, test_y.coerce_to_i32(activation)? - top_left.1,
); );
return Ok(Value::Bool(operations::hit_test_point( return Ok(Value::Bool(operations::hit_test_point(
bitmap_data.bitmap_data(), bitmap_data,
source_threshold, source_threshold,
test_point, test_point,
))); )));
@ -843,7 +851,7 @@ pub fn hit_test<'gc>(
test_height.coerce_to_i32(activation)?, test_height.coerce_to_i32(activation)?,
); );
return Ok(Value::Bool(operations::hit_test_rectangle( return Ok(Value::Bool(operations::hit_test_rectangle(
bitmap_data.bitmap_data(), bitmap_data,
source_threshold, source_threshold,
test_point, test_point,
size, size,
@ -863,12 +871,12 @@ pub fn hit_test<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn copy_pixels<'gc>( fn copy_pixels<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let source_bitmap = args let source_bitmap = args
.get(0) .get(0)
@ -901,7 +909,7 @@ pub fn copy_pixels<'gc>(
let dest_x = dest_point.get("x", activation)?.coerce_to_f64(activation)? as i32; let dest_x = dest_point.get("x", activation)?.coerce_to_f64(activation)? as i32;
let dest_y = dest_point.get("y", activation)?.coerce_to_f64(activation)? as i32; let dest_y = dest_point.get("y", activation)?.coerce_to_f64(activation)? as i32;
if let Some(src_bitmap) = source_bitmap.as_bitmap_data_object() { if let NativeObject::BitmapData(src_bitmap) = source_bitmap.native() {
if !src_bitmap.disposed() { if !src_bitmap.disposed() {
let merge_alpha = if args.len() >= 6 { let merge_alpha = if args.len() >= 6 {
Some( Some(
@ -918,7 +926,7 @@ pub fn copy_pixels<'gc>(
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_object(activation); .coerce_to_object(activation);
if let Some(alpha_bitmap) = alpha_bitmap.as_bitmap_data_object() { if let NativeObject::BitmapData(alpha_bitmap) = alpha_bitmap.native() {
if !alpha_bitmap.disposed() { if !alpha_bitmap.disposed() {
let alpha_point = args let alpha_point = args
.get(4) .get(4)
@ -937,11 +945,11 @@ pub fn copy_pixels<'gc>(
operations::copy_pixels_with_alpha_source( operations::copy_pixels_with_alpha_source(
&mut activation.context, &mut activation.context,
bitmap_data.bitmap_data(), bitmap_data,
src_bitmap.bitmap_data(), src_bitmap,
(src_min_x, src_min_y, src_width, src_height), (src_min_x, src_min_y, src_width, src_height),
(dest_x, dest_y), (dest_x, dest_y),
alpha_bitmap.bitmap_data(), alpha_bitmap,
(alpha_x, alpha_y), (alpha_x, alpha_y),
merge_alpha.unwrap_or(true), merge_alpha.unwrap_or(true),
); );
@ -949,8 +957,8 @@ pub fn copy_pixels<'gc>(
} else { } else {
operations::copy_pixels( operations::copy_pixels(
&mut activation.context, &mut activation.context,
bitmap_data.bitmap_data(), bitmap_data,
src_bitmap.bitmap_data(), src_bitmap,
(src_min_x, src_min_y, src_width, src_height), (src_min_x, src_min_y, src_width, src_height),
(dest_x, dest_y), (dest_x, dest_y),
// Despite what the docs claim, mergeAlpa appears to be treated as 'false' // Despite what the docs claim, mergeAlpa appears to be treated as 'false'
@ -968,12 +976,12 @@ pub fn copy_pixels<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn merge<'gc>( fn merge<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let source_bitmap = args let source_bitmap = args
.get(0) .get(0)
@ -1026,12 +1034,12 @@ pub fn merge<'gc>(
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_i32(activation)?; .coerce_to_i32(activation)?;
if let Some(src_bitmap) = source_bitmap.as_bitmap_data_object() { if let NativeObject::BitmapData(src_bitmap) = source_bitmap.native() {
if !src_bitmap.disposed() { if !src_bitmap.disposed() {
operations::merge( operations::merge(
activation.context.gc_context, activation.context.gc_context,
bitmap_data.bitmap_data(), bitmap_data,
src_bitmap.bitmap_data(), src_bitmap,
(src_min_x, src_min_y, src_width, src_height), (src_min_x, src_min_y, src_width, src_height),
(dest_x, dest_y), (dest_x, dest_y),
(red_mult, green_mult, blue_mult, alpha_mult), (red_mult, green_mult, blue_mult, alpha_mult),
@ -1046,12 +1054,12 @@ pub fn merge<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn palette_map<'gc>( fn palette_map<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let source_bitmap = args let source_bitmap = args
.get(0) .get(0)
@ -1105,12 +1113,12 @@ pub fn palette_map<'gc>(
let blue_array = get_channel(5, 0)?; let blue_array = get_channel(5, 0)?;
let alpha_array = get_channel(6, 24)?; let alpha_array = get_channel(6, 24)?;
if let Some(src_bitmap) = source_bitmap.as_bitmap_data_object() { if let NativeObject::BitmapData(src_bitmap) = source_bitmap.native() {
if !src_bitmap.disposed() { if !src_bitmap.disposed() {
operations::palette_map( operations::palette_map(
activation.context.gc_context, activation.context.gc_context,
bitmap_data.bitmap_data(), bitmap_data,
src_bitmap.bitmap_data(), src_bitmap,
(src_min_x, src_min_y, src_width, src_height), (src_min_x, src_min_y, src_width, src_height),
(dest_x, dest_y), (dest_x, dest_y),
(red_array, green_array, blue_array, alpha_array), (red_array, green_array, blue_array, alpha_array),
@ -1125,12 +1133,12 @@ pub fn palette_map<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn pixel_dissolve<'gc>( fn pixel_dissolve<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let src_bitmap_data = args let src_bitmap_data = args
.get(0) .get(0)
@ -1159,7 +1167,7 @@ pub fn pixel_dissolve<'gc>(
return Ok((-4).into()); return Ok((-4).into());
}; };
if let Some(src_bitmap_data) = src_bitmap_data.as_bitmap_data_object() { if let NativeObject::BitmapData(src_bitmap_data) = src_bitmap_data.native() {
if !src_bitmap_data.disposed() { if !src_bitmap_data.disposed() {
let dest_point = args let dest_point = args
.get(2) .get(2)
@ -1186,8 +1194,8 @@ pub fn pixel_dissolve<'gc>(
return Ok(operations::pixel_dissolve( return Ok(operations::pixel_dissolve(
activation.context.gc_context, activation.context.gc_context,
bitmap_data.bitmap_data(), bitmap_data,
src_bitmap_data.bitmap_data(), src_bitmap_data,
(src_min_x, src_min_y, src_width, src_height), (src_min_x, src_min_y, src_width, src_height),
dest_point, dest_point,
random_seed, random_seed,
@ -1203,12 +1211,12 @@ pub fn pixel_dissolve<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn scroll<'gc>( fn scroll<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let x = args let x = args
.get(0) .get(0)
@ -1219,12 +1227,7 @@ pub fn scroll<'gc>(
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_i32(activation)?; .coerce_to_i32(activation)?;
operations::scroll( operations::scroll(activation.context.gc_context, bitmap_data, x, y);
activation.context.gc_context,
bitmap_data.bitmap_data(),
x,
y,
);
return Ok(Value::Undefined); return Ok(Value::Undefined);
} }
@ -1233,12 +1236,12 @@ pub fn scroll<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn threshold<'gc>( fn threshold<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let NativeObject::BitmapData(bitmap_data) = this.native() {
if !bitmap_data.disposed() { if !bitmap_data.disposed() {
let source_bitmap = args let source_bitmap = args
.get(0) .get(0)
@ -1298,12 +1301,12 @@ pub fn threshold<'gc>(
.unwrap_or(&false.into()) .unwrap_or(&false.into())
.as_bool(activation.swf_version()); .as_bool(activation.swf_version());
if let Some(src_bitmap) = source_bitmap.as_bitmap_data_object() { if let NativeObject::BitmapData(src_bitmap) = source_bitmap.native() {
if !src_bitmap.disposed() { if !src_bitmap.disposed() {
let modified_count = operations::threshold( let modified_count = operations::threshold(
activation.context.gc_context, activation.context.gc_context,
bitmap_data.bitmap_data(), bitmap_data,
src_bitmap.bitmap_data(), src_bitmap,
(src_min_x, src_min_y, src_width, src_height), (src_min_x, src_min_y, src_width, src_height),
(dest_x, dest_y), (dest_x, dest_y),
operation, operation,
@ -1324,7 +1327,7 @@ pub fn threshold<'gc>(
Ok((-1).into()) Ok((-1).into())
} }
pub fn compare<'gc>( fn compare<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
@ -1335,11 +1338,7 @@ pub fn compare<'gc>(
const DIFFERENT_WIDTHS: i32 = -3; const DIFFERENT_WIDTHS: i32 = -3;
const DIFFERENT_HEIGHTS: i32 = -4; const DIFFERENT_HEIGHTS: i32 = -4;
let this_bitmap_data = if let Some(bitmap_data) = this.as_bitmap_data_object() { let NativeObject::BitmapData(this_bitmap_data) = this.native() else { return Ok(NOT_BITMAP.into()); };
bitmap_data
} else {
return Ok(NOT_BITMAP.into());
};
if this_bitmap_data.disposed() { if this_bitmap_data.disposed() {
// The documentation says that -2 should be returned here, but -1 is actually returned. // The documentation says that -2 should be returned here, but -1 is actually returned.
@ -1351,9 +1350,7 @@ pub fn compare<'gc>(
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
.coerce_to_object(activation); .coerce_to_object(activation);
let other_bitmap_data = if let Some(other_bitmap_data) = other.as_bitmap_data_object() { let NativeObject::BitmapData(other_bitmap_data) = other.native() else {
other_bitmap_data
} else {
// The documentation says that -1 should be returned here, but -2 is actually returned. // The documentation says that -1 should be returned here, but -2 is actually returned.
return Ok(BITMAP_DISPOSED.into()); return Ok(BITMAP_DISPOSED.into());
}; };
@ -1362,9 +1359,6 @@ pub fn compare<'gc>(
return Ok(BITMAP_DISPOSED.into()); return Ok(BITMAP_DISPOSED.into());
} }
let this_bitmap_data = this_bitmap_data.bitmap_data();
let other_bitmap_data = other_bitmap_data.bitmap_data();
if this_bitmap_data.width() != other_bitmap_data.width() { if this_bitmap_data.width() != other_bitmap_data.width() {
return Ok(DIFFERENT_WIDTHS.into()); return Ok(DIFFERENT_WIDTHS.into());
} }
@ -1374,9 +1368,9 @@ pub fn compare<'gc>(
} }
match operations::compare(this_bitmap_data, other_bitmap_data) { match operations::compare(this_bitmap_data, other_bitmap_data) {
Some(bitmap_data) => Ok(BitmapDataObject::with_bitmap_data( Some(bitmap_data) => Ok(new_bitmap_data(
activation.context.gc_context, activation.context.gc_context,
activation.context.avm1.prototypes().bitmap_data, this.get_local_stored("__proto__", activation),
bitmap_data, bitmap_data,
) )
.into()), .into()),
@ -1384,20 +1378,9 @@ pub fn compare<'gc>(
} }
} }
pub fn create_proto<'gc>( fn load_bitmap<'gc>(
context: &mut GcContext<'_, 'gc>,
proto: Object<'gc>,
fn_proto: Object<'gc>,
) -> Object<'gc> {
let bitmap_data_object = BitmapDataObject::empty_object(context.gc_context, proto);
let object = bitmap_data_object.raw_script_object();
define_properties_on(PROTO_DECLS, context, object, fn_proto);
bitmap_data_object.into()
}
pub fn load_bitmap<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
_this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let name = args let name = args
@ -1407,50 +1390,47 @@ pub fn load_bitmap<'gc>(
let library = &*activation.context.library; let library = &*activation.context.library;
let movie = activation.target_clip_or_root().movie(); let movie = <DisplayObject as crate::display_object::TDisplayObject>::movie(
&activation.target_clip_or_root(),
);
let character = library let character = library
.library_for_movie(movie) .library_for_movie(movie)
.and_then(|l| l.character_by_export_name(name)); .and_then(|l| l.character_by_export_name(name));
if let Some(Character::Bitmap(bitmap)) = character { let Some(Character::Bitmap(bitmap)) = character else { return Ok(Value::Undefined); };
let new_bitmap_data = BitmapDataObject::empty_object(
activation.context.gc_context, let transparency = true;
activation.context.avm1.prototypes().bitmap_data, let bitmap_data = BitmapData::new_with_pixels(
bitmap.width().into(),
bitmap.height().into(),
transparency,
bitmap.bitmap_data().read().pixels().to_vec(),
); );
Ok(new_bitmap_data(
let width = bitmap.width() as u32; activation.context.gc_context,
let height = bitmap.height() as u32; this.get_local_stored("prototype", activation),
bitmap_data,
let pixels: Vec<_> = bitmap.bitmap_data().read().pixels().to_vec(); )
let (sync, _) = new_bitmap_data .into())
.as_bitmap_data_object()
.unwrap()
.bitmap_data()
.overwrite_cpu_pixels_from_gpu(activation.context.gc_context);
sync.write(activation.context.gc_context)
.set_pixels(width, height, true, pixels);
return Ok(new_bitmap_data.into());
} }
Ok(Value::Undefined) pub fn create_constructor<'gc>(
}
pub fn create_bitmap_data_object<'gc>(
context: &mut GcContext<'_, 'gc>, context: &mut GcContext<'_, 'gc>,
bitmap_data_proto: Object<'gc>, proto: Object<'gc>,
fn_proto: Object<'gc>, fn_proto: Object<'gc>,
) -> Object<'gc> { ) -> Object<'gc> {
let bitmap_data = FunctionObject::constructor( let bitmap_data_proto = ScriptObject::new(context.gc_context, Some(proto));
define_properties_on(PROTO_DECLS, context, bitmap_data_proto, fn_proto);
let bitmap_data_constructor = FunctionObject::constructor(
context.gc_context, context.gc_context,
Executable::Native(constructor), Executable::Native(constructor),
constructor_to_fn!(constructor), constructor_to_fn!(constructor),
fn_proto, fn_proto,
bitmap_data_proto, bitmap_data_proto.into(),
); );
let object = bitmap_data.raw_script_object(); let object = bitmap_data_constructor.raw_script_object();
define_properties_on(OBJECT_DECLS, context, object, fn_proto); define_properties_on(OBJECT_DECLS, context, object, fn_proto);
bitmap_data bitmap_data_constructor
} }

View File

@ -4,6 +4,7 @@ use crate::avm1::activation::Activation;
use crate::avm1::clamp::Clamp; use crate::avm1::clamp::Clamp;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::object::displacement_map_filter::DisplacementMapFilterObject; use crate::avm1::object::displacement_map_filter::DisplacementMapFilterObject;
use crate::avm1::object::NativeObject;
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::context::GcContext; use crate::context::GcContext;
@ -169,14 +170,11 @@ pub fn set_map_bitmap<'gc>(
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let bitmap = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_object(activation);
if let Some(object) = this.as_displacement_map_filter_object() { if let Some(object) = this.as_displacement_map_filter_object() {
if bitmap.as_bitmap_data_object().is_some() { if let [Value::Object(map_bitmap), ..] = args {
object.set_map_bitmap(activation.context.gc_context, Some(bitmap)); if let NativeObject::BitmapData(_) = map_bitmap.native() {
object.set_map_bitmap(activation.context.gc_context, Some(*map_bitmap));
}
} }
} }

View File

@ -4,6 +4,7 @@ use crate::avm1::activation::Activation;
use crate::avm1::error::Error; use crate::avm1::error::Error;
use crate::avm1::globals::matrix::gradient_object_to_matrix; use crate::avm1::globals::matrix::gradient_object_to_matrix;
use crate::avm1::globals::{self, AVM_DEPTH_BIAS, AVM_MAX_DEPTH}; use crate::avm1::globals::{self, AVM_DEPTH_BIAS, AVM_MAX_DEPTH};
use crate::avm1::object::NativeObject;
use crate::avm1::property_decl::{define_properties_on, Declaration}; use crate::avm1::property_decl::{define_properties_on, Declaration};
use crate::avm1::{self, Object, ScriptObject, TObject, Value}; use crate::avm1::{self, Object, ScriptObject, TObject, Value};
use crate::avm_error; use crate::avm_error;
@ -17,7 +18,6 @@ use crate::ecma_conversions::f64_to_wrapping_i32;
use crate::prelude::*; use crate::prelude::*;
use crate::string::AvmString; use crate::string::AvmString;
use crate::vminterface::Instantiator; use crate::vminterface::Instantiator;
use ruffle_render::shape_utils::DrawCommand; use ruffle_render::shape_utils::DrawCommand;
use std::str::FromStr; use std::str::FromStr;
use swf::{ use swf::{
@ -243,12 +243,8 @@ fn attach_bitmap<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap) = args.get(0) { if let [Value::Object(bitmap_data), ..] = args {
if let Some(bitmap_data) = bitmap if let NativeObject::BitmapData(bitmap_data) = bitmap_data.native() {
.coerce_to_object(activation)
.as_bitmap_data_object()
.map(|bd| bd.bitmap_data())
{
if let Some(depth) = args.get(1) { if let Some(depth) = args.get(1) {
let depth = depth let depth = depth
.coerce_to_i32(activation)? .coerce_to_i32(activation)?
@ -395,14 +391,11 @@ fn begin_bitmap_fill<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = args let fill_style = if let [Value::Object(bitmap_data), ..] = args {
.get(0) if let NativeObject::BitmapData(bitmap_data) = bitmap_data.native() {
.and_then(|val| val.coerce_to_object(activation).as_bitmap_data_object())
{
// Register the bitmap data with the drawing. // Register the bitmap data with the drawing.
let bitmap_data = bitmap_data.bitmap_data(); let handle = bitmap_data
let handle = .bitmap_handle(activation.context.gc_context, activation.context.renderer);
bitmap_data.bitmap_handle(activation.context.gc_context, activation.context.renderer);
let bitmap = ruffle_render::bitmap::BitmapInfo { let bitmap = ruffle_render::bitmap::BitmapInfo {
handle, handle,
width: bitmap_data.width() as u16, width: bitmap_data.width() as u16,
@ -431,19 +424,21 @@ fn begin_bitmap_fill<'gc>(
.get(3) .get(3)
.unwrap_or(&false.into()) .unwrap_or(&false.into())
.as_bool(activation.swf_version()); .as_bool(activation.swf_version());
movie_clip Some(FillStyle::Bitmap {
.drawing(activation.context.gc_context)
.set_fill_style(Some(FillStyle::Bitmap {
id, id,
matrix: matrix.into(), matrix: matrix.into(),
is_smoothed, is_smoothed,
is_repeating, is_repeating,
})); })
} else { } else {
None
}
} else {
None
};
movie_clip movie_clip
.drawing(activation.context.gc_context) .drawing(activation.context.gc_context)
.set_fill_style(None); .set_fill_style(fill_style);
}
Ok(Value::Undefined) Ok(Value::Undefined)
} }

View File

@ -9,7 +9,6 @@ use crate::avm1::globals::date::Date;
use crate::avm1::globals::drop_shadow_filter::DropShadowFilter; use crate::avm1::globals::drop_shadow_filter::DropShadowFilter;
use crate::avm1::globals::glow_filter::GlowFilter; use crate::avm1::globals::glow_filter::GlowFilter;
use crate::avm1::object::array_object::ArrayObject; use crate::avm1::object::array_object::ArrayObject;
use crate::avm1::object::bitmap_data::BitmapDataObject;
use crate::avm1::object::convolution_filter::ConvolutionFilterObject; use crate::avm1::object::convolution_filter::ConvolutionFilterObject;
use crate::avm1::object::displacement_map_filter::DisplacementMapFilterObject; use crate::avm1::object::displacement_map_filter::DisplacementMapFilterObject;
use crate::avm1::object::gradient_bevel_filter::GradientBevelFilterObject; use crate::avm1::object::gradient_bevel_filter::GradientBevelFilterObject;
@ -21,6 +20,7 @@ use crate::avm1::object::value_object::ValueObject;
use crate::avm1::object::xml_node_object::XmlNodeObject; use crate::avm1::object::xml_node_object::XmlNodeObject;
use crate::avm1::object::xml_object::XmlObject; use crate::avm1::object::xml_object::XmlObject;
use crate::avm1::{Activation, Attribute, Error, ScriptObject, SoundObject, StageObject, Value}; use crate::avm1::{Activation, Attribute, Error, ScriptObject, SoundObject, StageObject, Value};
use crate::bitmap::bitmap_data::BitmapDataWrapper;
use crate::display_object::DisplayObject; use crate::display_object::DisplayObject;
use crate::display_object::TDisplayObject; use crate::display_object::TDisplayObject;
use crate::html::TextFormat; use crate::html::TextFormat;
@ -32,7 +32,6 @@ use ruffle_macros::enum_trait_object;
use std::fmt::Debug; use std::fmt::Debug;
pub mod array_object; pub mod array_object;
pub mod bitmap_data;
pub mod convolution_filter; pub mod convolution_filter;
mod custom_object; mod custom_object;
pub mod displacement_map_filter; pub mod displacement_map_filter;
@ -61,6 +60,7 @@ pub enum NativeObject<'gc> {
ColorTransform(GcCell<'gc, ColorTransformObject>), ColorTransform(GcCell<'gc, ColorTransformObject>),
TextFormat(GcCell<'gc, TextFormat>), TextFormat(GcCell<'gc, TextFormat>),
NetStream(NetStream<'gc>), NetStream(NetStream<'gc>),
BitmapData(BitmapDataWrapper<'gc>),
} }
/// Represents an object that can be directly interacted with by the AVM /// Represents an object that can be directly interacted with by the AVM
@ -85,7 +85,6 @@ pub enum NativeObject<'gc> {
ConvolutionFilterObject(ConvolutionFilterObject<'gc>), ConvolutionFilterObject(ConvolutionFilterObject<'gc>),
GradientBevelFilterObject(GradientBevelFilterObject<'gc>), GradientBevelFilterObject(GradientBevelFilterObject<'gc>),
GradientGlowFilterObject(GradientGlowFilterObject<'gc>), GradientGlowFilterObject(GradientGlowFilterObject<'gc>),
BitmapData(BitmapDataObject<'gc>),
} }
)] )]
pub trait TObject<'gc>: 'gc + Collect + Into<Object<'gc>> + Clone + Copy { pub trait TObject<'gc>: 'gc + Collect + Into<Object<'gc>> + Clone + Copy {
@ -627,11 +626,6 @@ pub trait TObject<'gc>: 'gc + Collect + Into<Object<'gc>> + Clone + Copy {
None None
} }
/// Get the underlying `BitmapDataObject`, if it exists
fn as_bitmap_data_object(&self) -> Option<BitmapDataObject<'gc>> {
None
}
fn as_ptr(&self) -> *const ObjectPtr; fn as_ptr(&self) -> *const ObjectPtr;
/// Check if this object is in the prototype chain of the specified test object. /// Check if this object is in the prototype chain of the specified test object.

View File

@ -1,78 +0,0 @@
use crate::avm1::{Object, ScriptObject, TObject};
use crate::context::UpdateContext;
use crate::impl_custom_object;
use gc_arena::{Collect, GcCell, MutationContext};
use crate::bitmap::bitmap_data::{BitmapData, BitmapDataWrapper};
use std::fmt;
/// A BitmapData
#[derive(Clone, Copy, Collect)]
#[collect(no_drop)]
pub struct BitmapDataObject<'gc>(GcCell<'gc, BitmapDataData<'gc>>);
#[derive(Clone, Collect)]
#[collect(no_drop)]
pub struct BitmapDataData<'gc> {
/// The underlying script object.
base: ScriptObject<'gc>,
data: BitmapDataWrapper<'gc>,
}
impl fmt::Debug for BitmapDataObject<'_> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("BitmapData")
.field("ptr", &self.0.as_ptr())
.finish()
}
}
impl<'gc> BitmapDataObject<'gc> {
pub fn bitmap_data(&self) -> BitmapDataWrapper<'gc> {
self.0.read().data
}
pub fn empty_object(gc_context: MutationContext<'gc, '_>, proto: Object<'gc>) -> Self {
Self::with_bitmap_data(gc_context, proto, Default::default())
}
pub fn with_bitmap_data(
gc_context: MutationContext<'gc, '_>,
proto: Object<'gc>,
bitmap_data: BitmapData<'gc>,
) -> Self {
Self(GcCell::allocate(
gc_context,
BitmapDataData {
base: ScriptObject::new(gc_context, Some(proto)),
data: BitmapDataWrapper::new(GcCell::allocate(gc_context, bitmap_data)),
},
))
}
pub fn width(&self) -> u32 {
self.0.read().data.width()
}
pub fn height(&self) -> u32 {
self.0.read().data.height()
}
pub fn transparency(&self) -> bool {
self.0.read().data.transparency()
}
pub fn disposed(&self) -> bool {
self.0.read().data.disposed()
}
pub fn dispose(&self, context: &mut UpdateContext<'_, 'gc>) {
self.bitmap_data().dispose(context.gc_context);
}
}
impl<'gc> TObject<'gc> for BitmapDataObject<'gc> {
impl_custom_object!(base {
bare_object(as_bitmap_data_object -> BitmapDataObject::empty_object);
});
}

View File

@ -55,7 +55,8 @@ pub fn init<'gc>(
.character_by_id(symbol_id) .character_by_id(symbol_id)
.cloned() .cloned()
{ {
let new_bitmap_data = fill_bitmap_data_from_symbol(activation, bitmap); let new_bitmap_data =
fill_bitmap_data_from_symbol(activation.context.gc_context, bitmap);
BitmapDataObject::from_bitmap_data( BitmapDataObject::from_bitmap_data(
activation, activation,
new_bitmap_data, new_bitmap_data,

View File

@ -3,7 +3,9 @@
use crate::avm2::activation::Activation; use crate::avm2::activation::Activation;
use crate::avm2::error::{argument_error, make_error_2008, range_error}; use crate::avm2::error::{argument_error, make_error_2008, range_error};
use crate::avm2::filters::FilterAvm2Ext; use crate::avm2::filters::FilterAvm2Ext;
pub use crate::avm2::object::bitmap_data_allocator;
use crate::avm2::object::{BitmapDataObject, ByteArrayObject, Object, TObject, VectorObject}; use crate::avm2::object::{BitmapDataObject, ByteArrayObject, Object, TObject, VectorObject};
use crate::avm2::parameters::{null_parameter_error, ParametersExt};
use crate::avm2::value::Value; use crate::avm2::value::Value;
use crate::avm2::vector::VectorStorage; use crate::avm2::vector::VectorStorage;
use crate::avm2::Error; use crate::avm2::Error;
@ -14,34 +16,29 @@ use crate::bitmap::bitmap_data::{BitmapDataDrawError, IBitmapDrawable};
use crate::bitmap::{is_size_valid, operations}; use crate::bitmap::{is_size_valid, operations};
use crate::character::Character; use crate::character::Character;
use crate::display_object::Bitmap; use crate::display_object::Bitmap;
use crate::display_object::TDisplayObject;
use crate::swf::BlendMode; use crate::swf::BlendMode;
use gc_arena::GcCell; use gc_arena::{GcCell, MutationContext};
use ruffle_render::filters::Filter; use ruffle_render::filters::Filter;
use ruffle_render::transform::Transform; use ruffle_render::transform::Transform;
use std::str::FromStr; use std::str::FromStr;
pub use crate::avm2::object::bitmap_data_allocator;
use crate::avm2::parameters::{null_parameter_error, ParametersExt};
use crate::display_object::TDisplayObject;
/// Copy the static data from a given Bitmap into a new BitmapData. /// Copy the static data from a given Bitmap into a new BitmapData.
/// ///
/// `bd` is assumed to be an uninstantiated library symbol, associated with the /// `bd` is assumed to be an uninstantiated library symbol, associated with the
/// class named by `name`. /// class named by `name`.
pub fn fill_bitmap_data_from_symbol<'gc>( pub fn fill_bitmap_data_from_symbol<'gc>(
activation: &mut Activation<'_, 'gc>, gc_context: MutationContext<'gc, '_>,
bd: Bitmap<'gc>, bitmap: Bitmap<'gc>,
) -> BitmapDataWrapper<'gc> { ) -> BitmapDataWrapper<'gc> {
let new_bitmap_data = GcCell::allocate(activation.context.gc_context, BitmapData::default()); let transparency = true;
new_bitmap_data let bitmap_data = BitmapData::new_with_pixels(
.write(activation.context.gc_context) bitmap.width().into(),
.set_pixels( bitmap.height().into(),
bd.width().into(), transparency,
bd.height().into(), bitmap.bitmap_data().read().pixels().to_vec(),
true,
bd.bitmap_data().read().pixels().to_vec(),
); );
BitmapDataWrapper::new(new_bitmap_data) BitmapDataWrapper::new(GcCell::allocate(gc_context, bitmap_data))
} }
/// Implements `flash.display.BitmapData`'s 'init' method (invoked from the AS3 constructor) /// Implements `flash.display.BitmapData`'s 'init' method (invoked from the AS3 constructor)
@ -75,11 +72,8 @@ pub fn init<'gc>(
let new_bitmap_data = if let Some(Character::Bitmap(bitmap)) = character { let new_bitmap_data = if let Some(Character::Bitmap(bitmap)) = character {
// Instantiating BitmapData from an Animate-style bitmap asset // Instantiating BitmapData from an Animate-style bitmap asset
fill_bitmap_data_from_symbol(activation, bitmap) fill_bitmap_data_from_symbol(activation.context.gc_context, bitmap)
} else { } else {
let new_bitmap_data =
GcCell::allocate(activation.context.gc_context, BitmapData::default());
if character.is_some() { if character.is_some() {
//TODO: Determine if mismatched symbols will still work as a //TODO: Determine if mismatched symbols will still work as a
//regular BitmapData subclass, or if this should throw //regular BitmapData subclass, or if this should throw
@ -92,7 +86,7 @@ pub fn init<'gc>(
let width = args.get_u32(activation, 0)?; let width = args.get_u32(activation, 0)?;
let height = args.get_u32(activation, 1)?; let height = args.get_u32(activation, 1)?;
let transparency = args.get_bool(2); let transparency = args.get_bool(2);
let fill_color = args.get_u32(activation, 3)?; let fill_color = args.get_i32(activation, 3)?;
if !is_size_valid(activation.context.swf.version(), width, height) { if !is_size_valid(activation.context.swf.version(), width, height) {
return Err(Error::AvmError(argument_error( return Err(Error::AvmError(argument_error(
@ -102,10 +96,11 @@ pub fn init<'gc>(
)?)); )?));
} }
new_bitmap_data let new_bitmap_data = BitmapData::new(width, height, transparency, fill_color);
.write(activation.context.gc_context) BitmapDataWrapper::new(GcCell::allocate(
.init_pixels(width, height, transparency, fill_color as i32); activation.context.gc_context,
BitmapDataWrapper::new(new_bitmap_data) new_bitmap_data,
))
}; };
new_bitmap_data.init_object2(activation.context.gc_context, this); new_bitmap_data.init_object2(activation.context.gc_context, this);

View File

@ -199,7 +199,7 @@ bitflags! {
} }
} }
#[derive(Clone, Collect, Default)] #[derive(Clone, Collect)]
#[collect(no_drop)] #[collect(no_drop)]
pub struct BitmapData<'gc> { pub struct BitmapData<'gc> {
/// The pixels in the bitmap, stored as a array of pre-multiplied ARGB colour values /// The pixels in the bitmap, stored as a array of pre-multiplied ARGB colour values
@ -229,14 +229,15 @@ pub struct BitmapData<'gc> {
dirty_state: DirtyState, dirty_state: DirtyState,
} }
#[derive(Clone, Collect, Default, Debug)] #[derive(Clone, Collect, Debug)]
#[collect(require_static)] #[collect(require_static)]
enum DirtyState { enum DirtyState {
// Both the CPU and GPU pixels are up to date. We do not need to wait for any syncs to complete // Both the CPU and GPU pixels are up to date. We do not need to wait for any syncs to complete
#[default]
Clean, Clean,
// The CPU pixels have been modified, and need to be synced to the GPU via `update_dirty_texture` // The CPU pixels have been modified, and need to be synced to the GPU via `update_dirty_texture`
CpuModified(PixelRegion), CpuModified(PixelRegion),
// The GPU pixels have been modified, and need to be synced to the CPU via `BitmapDataWrapper::sync` // The GPU pixels have been modified, and need to be synced to the CPU via `BitmapDataWrapper::sync`
GpuModified(Box<dyn SyncHandle>, PixelRegion), GpuModified(Box<dyn SyncHandle>, PixelRegion),
} }
@ -483,15 +484,20 @@ impl fmt::Debug for BitmapData<'_> {
} }
impl<'gc> BitmapData<'gc> { impl<'gc> BitmapData<'gc> {
pub fn init_pixels(&mut self, width: u32, height: u32, transparency: bool, fill_color: i32) { pub fn new(width: u32, height: u32, transparency: bool, fill_color: i32) -> Self {
self.width = width; Self {
self.height = height; pixels: vec![
self.transparency = transparency; Color(fill_color).to_premultiplied_alpha(transparency);
self.pixels = vec![
Color(fill_color).to_premultiplied_alpha(self.transparency());
width as usize * height as usize width as usize * height as usize
]; ],
self.set_cpu_dirty(PixelRegion::for_whole_size(width, height)); width,
height,
transparency,
disposed: false,
bitmap_handle: None,
avm2_object: None,
dirty_state: DirtyState::Clean,
}
} }
pub fn new_with_pixels( pub fn new_with_pixels(
@ -568,14 +574,6 @@ impl<'gc> BitmapData<'gc> {
&self.pixels &self.pixels
} }
pub fn set_pixels(&mut self, width: u32, height: u32, transparency: bool, pixels: Vec<Color>) {
self.width = width;
self.height = height;
self.transparency = transparency;
self.pixels = pixels;
self.set_cpu_dirty(PixelRegion::for_whole_size(width, height));
}
pub fn pixels_rgba(&self) -> Vec<u8> { pub fn pixels_rgba(&self) -> Vec<u8> {
// TODO: This could have been implemented as follows: // TODO: This could have been implemented as follows:
// //

View File

@ -5,7 +5,7 @@ use crate::avm2::{
Activation as Avm2Activation, ClassObject as Avm2ClassObject, Object as Avm2Object, Activation as Avm2Activation, ClassObject as Avm2ClassObject, Object as Avm2Object,
StageObject as Avm2StageObject, Value as Avm2Value, StageObject as Avm2StageObject, Value as Avm2Value,
}; };
use crate::bitmap::bitmap_data::BitmapDataWrapper; use crate::bitmap::bitmap_data::{BitmapData, BitmapDataWrapper};
use crate::context::{RenderContext, UpdateContext}; use crate::context::{RenderContext, UpdateContext};
use crate::display_object::{DisplayObjectBase, DisplayObjectPtr, TDisplayObject}; use crate::display_object::{DisplayObjectBase, DisplayObjectPtr, TDisplayObject};
use crate::prelude::*; use crate::prelude::*;
@ -135,30 +135,24 @@ impl<'gc> Bitmap<'gc> {
) -> Result<Self, ruffle_render::error::Error> { ) -> Result<Self, ruffle_render::error::Error> {
let width = bitmap.width(); let width = bitmap.width();
let height = bitmap.height(); let height = bitmap.height();
let pixels: Vec<_> = bitmap let transparency = match bitmap.format() {
.as_colors()
.map(crate::bitmap::bitmap_data::Color::from)
.collect();
let mut bitmap_data = crate::bitmap::bitmap_data::BitmapData::default();
bitmap_data.set_pixels(
width,
height,
match bitmap.format() {
BitmapFormat::Rgba => true, BitmapFormat::Rgba => true,
BitmapFormat::Rgb => false, BitmapFormat::Rgb => false,
_ => unreachable!( _ => unreachable!(
"Bitmap objects can only be constructed from RGB or RGBA source bitmaps" "Bitmap objects can only be constructed from RGB or RGBA source bitmaps"
), ),
}, };
pixels, let pixels: Vec<_> = bitmap
); .as_colors()
let bitmap_data = GcCell::allocate(context.gc_context, bitmap_data); .map(crate::bitmap::bitmap_data::Color::from)
.collect();
let bitmap_data = BitmapData::new_with_pixels(width, height, transparency, pixels);
let smoothing = true; let smoothing = true;
Ok(Self::new_with_bitmap_data( Ok(Self::new_with_bitmap_data(
context, context,
id, id,
BitmapDataWrapper::new(bitmap_data), BitmapDataWrapper::new(GcCell::allocate(context.gc_context, bitmap_data)),
smoothing, smoothing,
)) ))
} }
@ -179,7 +173,7 @@ impl<'gc> Bitmap<'gc> {
} }
/// Retrieve the bitmap data associated with this `Bitmap`. /// Retrieve the bitmap data associated with this `Bitmap`.
pub fn bitmap_data(self) -> GcCell<'gc, crate::bitmap::bitmap_data::BitmapData<'gc>> { pub fn bitmap_data(self) -> GcCell<'gc, BitmapData<'gc>> {
self.0.read().bitmap_data.sync() self.0.read().bitmap_data.sync()
} }

View File

@ -5,16 +5,16 @@ use crate::avm1::ExecutionReason;
use crate::avm1::{Activation, ActivationIdentifier}; use crate::avm1::{Activation, ActivationIdentifier};
use crate::avm1::{Object, SoundObject, TObject, Value}; use crate::avm1::{Object, SoundObject, TObject, Value};
use crate::avm2::bytearray::ByteArrayStorage; use crate::avm2::bytearray::ByteArrayStorage;
use crate::avm2::object::ByteArrayObject; use crate::avm2::object::{
use crate::avm2::object::EventObject as Avm2EventObject; BitmapDataObject, ByteArrayObject, EventObject as Avm2EventObject, LoaderStream, TObject as _,
use crate::avm2::object::LoaderStream; };
use crate::avm2::object::TObject as _;
use crate::avm2::{ use crate::avm2::{
Activation as Avm2Activation, Avm2, Domain as Avm2Domain, Object as Avm2Object, Activation as Avm2Activation, Avm2, Domain as Avm2Domain, Object as Avm2Object,
Value as Avm2Value, Value as Avm2Value,
}; };
use crate::backend::navigator::{OwnedFuture, Request}; use crate::backend::navigator::{OwnedFuture, Request};
use crate::bitmap::bitmap_data::Color; use crate::bitmap::bitmap_data::Color;
use crate::bitmap::bitmap_data::{BitmapData, BitmapDataWrapper};
use crate::context::{ActionQueue, ActionType, UpdateContext}; use crate::context::{ActionQueue, ActionType, UpdateContext};
use crate::display_object::{ use crate::display_object::{
DisplayObject, TDisplayObject, TDisplayObjectContainer, TInteractiveObject, DisplayObject, TDisplayObject, TDisplayObjectContainer, TInteractiveObject,
@ -28,7 +28,7 @@ use crate::string::AvmString;
use crate::tag_utils::SwfMovie; use crate::tag_utils::SwfMovie;
use crate::vminterface::Instantiator; use crate::vminterface::Instantiator;
use encoding_rs::UTF_8; use encoding_rs::UTF_8;
use gc_arena::{Collect, CollectionContext}; use gc_arena::{Collect, CollectionContext, GcCell};
use generational_arena::{Arena, Index}; use generational_arena::{Arena, Index};
use ruffle_render::utils::{determine_jpeg_tag_format, JpegTagFormat}; use ruffle_render::utils::{determine_jpeg_tag_format, JpegTagFormat};
use std::fmt; use std::fmt;
@ -1492,28 +1492,24 @@ impl<'gc> Loader<'gc> {
// since Bitmap and BitmapData never have AVM1-side objects. // since Bitmap and BitmapData never have AVM1-side objects.
let bitmap = ruffle_render::utils::decode_define_bits_jpeg(data, None)?; let bitmap = ruffle_render::utils::decode_define_bits_jpeg(data, None)?;
let bitmapdata_avm2 = activation let transparency = true;
.avm2() let bitmap_data = BitmapData::new_with_pixels(
.classes()
.bitmapdata
.construct(
&mut activation,
&[bitmap.width().into(), bitmap.height().into(), true.into()],
)
.unwrap();
let bitmapdata_wrapper = bitmapdata_avm2.as_bitmap_data().unwrap();
bitmapdata_wrapper
.overwrite_cpu_pixels_from_gpu(activation.context.gc_context)
.0
.write(activation.context.gc_context)
.set_pixels(
bitmap.width(), bitmap.width(),
bitmap.height(), bitmap.height(),
true, transparency,
bitmap.as_colors().map(Color::from).collect(), bitmap.as_colors().map(Color::from).collect(),
); );
let bitmapdata_wrapper = BitmapDataWrapper::new(GcCell::allocate(
activation.context.gc_context,
bitmap_data,
));
let bitmapdata_class = activation.context.avm2.classes().bitmapdata;
let bitmapdata_avm2 = BitmapDataObject::from_bitmap_data(
&mut activation,
bitmapdata_wrapper,
bitmapdata_class,
)
.unwrap();
let bitmap_avm2 = activation let bitmap_avm2 = activation
.avm2() .avm2()