From c230dcf0f872afdfc6f182580b61d672270473fc Mon Sep 17 00:00:00 2001 From: relrelb Date: Mon, 15 May 2023 21:55:15 +0300 Subject: [PATCH] avm1: Migrate `BitmapData` to `NativeObject` --- core/src/avm1/globals.rs | 6 +- core/src/avm1/globals/bitmap_data.rs | 404 +++++++++--------- .../avm1/globals/displacement_map_filter.rs | 12 +- core/src/avm1/globals/movie_clip.rs | 93 ++-- core/src/avm1/object.rs | 10 +- core/src/avm1/object/bitmap_data.rs | 78 ---- core/src/avm2/globals/flash/display/bitmap.rs | 3 +- .../avm2/globals/flash/display/bitmap_data.rs | 47 +- core/src/bitmap/bitmap_data.rs | 38 +- core/src/display_object/bitmap.rs | 28 +- core/src/loader.rs | 50 +-- 11 files changed, 319 insertions(+), 450 deletions(-) delete mode 100644 core/src/avm1/object/bitmap_data.rs diff --git a/core/src/avm1/globals.rs b/core/src/avm1/globals.rs index 61336ef62..3338ab0ee 100644 --- a/core/src/avm1/globals.rs +++ b/core/src/avm1/globals.rs @@ -508,7 +508,6 @@ pub struct SystemPrototypes<'gc> { pub gradient_glow_filter: Object<'gc>, pub gradient_glow_filter_constructor: Object<'gc>, pub date_constructor: Object<'gc>, - pub bitmap_data: Object<'gc>, pub bitmap_data_constructor: Object<'gc>, pub video: Object<'gc>, pub video_constructor: Object<'gc>, @@ -863,9 +862,7 @@ pub fn create_globals<'gc>( Attribute::empty(), ); - let bitmap_data_proto = bitmap_data::create_proto(context, object_proto, function_proto); - let bitmap_data = - bitmap_data::create_bitmap_data_object(context, bitmap_data_proto, function_proto); + let bitmap_data = bitmap_data::create_constructor(context, object_proto, function_proto); display.define_value( gc_context, @@ -1130,7 +1127,6 @@ pub fn create_globals<'gc>( gradient_glow_filter: gradient_glow_filter_proto, gradient_glow_filter_constructor: gradient_glow_filter, date_constructor: date, - bitmap_data: bitmap_data_proto, bitmap_data_constructor: bitmap_data, video: video_proto, video_constructor: video, diff --git a/core/src/avm1/globals/bitmap_data.rs b/core/src/avm1/globals/bitmap_data.rs index af42cf797..8f49874c6 100644 --- a/core/src/avm1/globals/bitmap_data.rs +++ b/core/src/avm1/globals/bitmap_data.rs @@ -3,17 +3,19 @@ use super::matrix::object_to_matrix; use crate::avm1::function::{Executable, FunctionObject}; 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::{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::{ChannelOptions, ThresholdOperation}; use crate::bitmap::{is_size_valid, operations}; use crate::character::Character; use crate::context::GcContext; -use crate::display_object::TDisplayObject; +use crate::display_object::DisplayObject; use crate::swf::BlendMode; use crate::{avm1_stub, avm_error}; +use gc_arena::{GcCell, MutationContext}; use ruffle_render::transform::Transform; use std::str::FromStr; @@ -52,93 +54,117 @@ const OBJECT_DECLS: &[Declaration] = declare_properties! { "loadBitmap" => method(load_bitmap); }; -pub fn constructor<'gc>( +fn new_bitmap_data<'gc>( + gc_context: MutationContext<'gc, '_>, + proto: Option>, + 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>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - let width = args.get(0).unwrap_or(&0.into()).coerce_to_i32(activation)? as u32; - - let height = args.get(1).unwrap_or(&0.into()).coerce_to_i32(activation)? as u32; - - let transparency = args - .get(2) - .unwrap_or(&true.into()) - .as_bool(activation.swf_version()); - - let fill_color = args - .get(3) - .unwrap_or(&(-1).into()) - .coerce_to_i32(activation)?; + let (width, height) = match args { + [width, height, ..] => ( + width.coerce_to_u32(activation)?, + height.coerce_to_u32(activation)?, + ), + [] | [_] => return Ok(Value::Undefined), + }; + let transparency = match args.get(2) { + Some(transparency) => transparency.as_bool(activation.swf_version()), + None => true, + }; + let fill_color = match args.get(3) { + Some(fill_color) => fill_color.coerce_to_i32(activation)?, + None => -1, + }; if !is_size_valid(activation.swf_version(), width, height) { tracing::warn!("Invalid BitmapData size: {}x{}", width, height); return Ok(Value::Undefined); } - if let Some(bitmap_data) = this.as_bitmap_data_object() { - let (sync, _) = bitmap_data - .bitmap_data() - .overwrite_cpu_pixels_from_gpu(activation.context.gc_context); - sync.write(activation.context.gc_context).init_pixels( - width, - height, - transparency, - fill_color, - ); - } - + let bitmap_data = BitmapData::new(width, height, transparency, fill_color); + this.set_native( + activation.context.gc_context, + NativeObject::BitmapData(BitmapDataWrapper::new(GcCell::allocate( + activation.context.gc_context, + bitmap_data, + ))), + ); Ok(this.into()) } -pub fn height<'gc>( +fn height<'gc>( _activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { - return Ok(bitmap_data.bitmap_data().height().into()); + return Ok(bitmap_data.height().into()); } } Ok((-1).into()) } -pub fn width<'gc>( +fn width<'gc>( _activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { - return Ok(bitmap_data.bitmap_data().width().into()); + return Ok(bitmap_data.width().into()); } } Ok((-1).into()) } -pub fn get_transparent<'gc>( +fn get_transparent<'gc>( _activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { - return Ok(bitmap_data.bitmap_data().transparency().into()); + return Ok(bitmap_data.transparency().into()); } } Ok((-1).into()) } -pub fn get_rectangle<'gc>( +fn get_rectangle<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let proto = activation.context.avm1.prototypes().rectangle_constructor; let rect = proto.construct( @@ -157,17 +183,17 @@ pub fn get_rectangle<'gc>( Ok((-1).into()) } -pub fn get_pixel<'gc>( +fn get_pixel<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, 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 let (Some(x_val), Some(y_val)) = (args.get(0), args.get(1)) { let x = x_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()); } } @@ -176,17 +202,17 @@ pub fn get_pixel<'gc>( Ok((-1).into()) } -pub fn get_pixel32<'gc>( +fn get_pixel32<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, 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 let (Some(x_val), Some(y_val)) = (args.get(0), args.get(1)) { let x = x_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()); } } @@ -195,12 +221,12 @@ pub fn get_pixel32<'gc>( Ok((-1).into()) } -pub fn set_pixel<'gc>( +fn set_pixel<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, 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 let (Some(x_val), Some(y_val), Some(color_val)) = (args.get(0), args.get(1), args.get(2)) @@ -211,7 +237,7 @@ pub fn set_pixel<'gc>( operations::set_pixel( activation.context.gc_context, - bitmap_data.bitmap_data(), + bitmap_data, x, y, color.into(), @@ -225,12 +251,12 @@ pub fn set_pixel<'gc>( Ok((-1).into()) } -pub fn set_pixel32<'gc>( +fn set_pixel32<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, 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 let (Some(x_val), Some(y_val), Some(color_val)) = (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 color = color_val.coerce_to_i32(activation)?; - operations::set_pixel32( - activation.context.gc_context, - bitmap_data.bitmap_data(), - x, - y, - color, - ); + operations::set_pixel32(activation.context.gc_context, bitmap_data, x, y, color); } return Ok(Value::Undefined); @@ -255,7 +275,7 @@ pub fn set_pixel32<'gc>( Ok((-1).into()) } -pub fn copy_channel<'gc>( +fn copy_channel<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], @@ -285,9 +305,9 @@ pub fn copy_channel<'gc>( .unwrap_or(&Value::Undefined) .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 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 let min_x = dest_point.get("x", 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( activation.context.gc_context, - bitmap_data.bitmap_data(), + bitmap_data, (min_x, min_y), (src_min_x, src_min_y, src_width, src_height), - source_bitmap.bitmap_data(), + source_bitmap, source_channel, dest_channel, ); @@ -323,7 +343,7 @@ pub fn copy_channel<'gc>( Ok((-1).into()) } -pub fn fill_rect<'gc>( +fn fill_rect<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], @@ -333,7 +353,7 @@ pub fn fill_rect<'gc>( .unwrap_or(&Value::Undefined) .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 let Some(color_val) = args.get(1) { let color = color_val.coerce_to_i32(activation)?; @@ -349,7 +369,7 @@ pub fn fill_rect<'gc>( operations::fill_rect( activation.context.gc_context, - bitmap_data.bitmap_data(), + bitmap_data, x, y, width, @@ -364,35 +384,33 @@ pub fn fill_rect<'gc>( Ok((-1).into()) } -pub fn clone<'gc>( +fn clone<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { - let new_bitmap_data = operations::clone(bitmap_data.bitmap_data()); - let new_bitmap_data = BitmapDataObject::with_bitmap_data( + return Ok(new_bitmap_data( activation.context.gc_context, - activation.context.avm1.prototypes().bitmap_data, - new_bitmap_data, - ); - - return Ok(new_bitmap_data.into()); + this.get_local_stored("__proto__", activation), + operations::clone(bitmap_data), + ) + .into()); } } Ok((-1).into()) } -pub fn dispose<'gc>( +fn dispose<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { - bitmap_data.dispose(&mut activation.context); + bitmap_data.dispose(activation.context.gc_context); return Ok(Value::Undefined); } } @@ -400,12 +418,12 @@ pub fn dispose<'gc>( Ok((-1).into()) } -pub fn flood_fill<'gc>( +fn flood_fill<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, 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 let (Some(x_val), Some(y_val), Some(color_val)) = (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 color = color_val.coerce_to_i32(activation)?; - operations::flood_fill( - activation.context.gc_context, - bitmap_data.bitmap_data(), - x, - y, - color, - ); + operations::flood_fill(activation.context.gc_context, bitmap_data, x, y, color); } return Ok(Value::Undefined); } @@ -429,7 +441,7 @@ pub fn flood_fill<'gc>( Ok((-1).into()) } -pub fn noise<'gc>( +fn noise<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], @@ -452,13 +464,13 @@ pub fn noise<'gc>( .unwrap_or(&false.into()) .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 let Some(random_seed_val) = args.get(0) { let random_seed = random_seed_val.coerce_to_i32(activation)?; operations::noise( activation.context.gc_context, - bitmap_data.bitmap_data(), + bitmap_data, random_seed, low, high.max(low), @@ -474,12 +486,12 @@ pub fn noise<'gc>( Ok((-1).into()) } -pub fn draw<'gc>( +fn draw<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let matrix = args .get(1) @@ -521,8 +533,8 @@ pub fn draw<'gc>( .coerce_to_object(activation); let source = if let Some(source_object) = source.as_display_object() { IBitmapDrawable::DisplayObject(source_object) - } else if let Some(source_bitmap) = source.as_bitmap_data_object() { - IBitmapDrawable::BitmapData(source_bitmap.bitmap_data()) + } else if let NativeObject::BitmapData(source_bitmap) = source.native() { + IBitmapDrawable::BitmapData(source_bitmap) } else { avm_error!( activation, @@ -538,7 +550,7 @@ pub fn draw<'gc>( let quality = activation.context.stage.quality(); match operations::draw( &mut activation.context, - bitmap_data.bitmap_data(), + bitmap_data, source, Transform { matrix, @@ -564,7 +576,7 @@ pub fn draw<'gc>( Ok((-1).into()) } -pub fn apply_filter<'gc>( +fn apply_filter<'gc>( activation: &mut Activation<'_, 'gc>, _this: Object<'gc>, _args: &[Value<'gc>], @@ -573,12 +585,12 @@ pub fn apply_filter<'gc>( Ok((-1).into()) } -pub fn generate_filter_rect<'gc>( +fn generate_filter_rect<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, _args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { avm1_stub!(activation, "BitmapData", "generateFilterRect"); return Ok(Value::Undefined); @@ -588,12 +600,12 @@ pub fn generate_filter_rect<'gc>( Ok((-1).into()) } -pub fn color_transform<'gc>( +fn color_transform<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, 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 let [rectangle, color_transform, ..] = args { // TODO: Re-use `object_to_rectangle` in `movie_clip.rs`. @@ -619,7 +631,7 @@ pub fn color_transform<'gc>( operations::color_transform( activation.context.gc_context, - bitmap_data.bitmap_data(), + bitmap_data, x_min, y_min, x_max, @@ -633,12 +645,12 @@ pub fn color_transform<'gc>( Ok((-1).into()) } -pub fn get_color_bounds_rect<'gc>( +fn get_color_bounds_rect<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let find_color = args .get(2) @@ -649,12 +661,8 @@ pub fn get_color_bounds_rect<'gc>( let mask = mask_val.coerce_to_i32(activation)?; let color = color_val.coerce_to_i32(activation)?; - let (x, y, w, h) = operations::color_bounds_rect( - bitmap_data.bitmap_data(), - find_color, - mask, - color, - ); + let (x, y, w, h) = + operations::color_bounds_rect(bitmap_data, find_color, mask, color); let proto = activation.context.avm1.prototypes().rectangle_constructor; let rect = @@ -667,12 +675,12 @@ pub fn get_color_bounds_rect<'gc>( Ok((-1).into()) } -pub fn perlin_noise<'gc>( +fn perlin_noise<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let base_x = args .get(0) @@ -727,7 +735,7 @@ pub fn perlin_noise<'gc>( operations::perlin_noise( activation.context.gc_context, - bitmap_data.bitmap_data(), + bitmap_data, (base_x, base_y), num_octaves, seed, @@ -743,12 +751,12 @@ pub fn perlin_noise<'gc>( Ok((-1).into()) } -pub fn hit_test<'gc>( +fn hit_test<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let first_point = args .get(0) @@ -776,7 +784,7 @@ pub fn hit_test<'gc>( // Overload based on the object we are hit-testing against. // 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() { return Ok((-3).into()); } @@ -801,10 +809,10 @@ pub fn hit_test<'gc>( .clamp(0, u8::MAX.into()) as u8; let result = operations::hit_test_bitmapdata( - bitmap_data.bitmap_data(), + bitmap_data, top_left, source_threshold, - other_bmd.bitmap_data(), + other_bmd, second_point, second_threshold, ); @@ -826,7 +834,7 @@ pub fn hit_test<'gc>( test_y.coerce_to_i32(activation)? - top_left.1, ); return Ok(Value::Bool(operations::hit_test_point( - bitmap_data.bitmap_data(), + bitmap_data, source_threshold, test_point, ))); @@ -843,7 +851,7 @@ pub fn hit_test<'gc>( test_height.coerce_to_i32(activation)?, ); return Ok(Value::Bool(operations::hit_test_rectangle( - bitmap_data.bitmap_data(), + bitmap_data, source_threshold, test_point, size, @@ -863,12 +871,12 @@ pub fn hit_test<'gc>( Ok((-1).into()) } -pub fn copy_pixels<'gc>( +fn copy_pixels<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let source_bitmap = args .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_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() { let merge_alpha = if args.len() >= 6 { Some( @@ -918,7 +926,7 @@ pub fn copy_pixels<'gc>( .unwrap_or(&Value::Undefined) .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() { let alpha_point = args .get(4) @@ -937,11 +945,11 @@ pub fn copy_pixels<'gc>( operations::copy_pixels_with_alpha_source( &mut activation.context, - bitmap_data.bitmap_data(), - src_bitmap.bitmap_data(), + bitmap_data, + src_bitmap, (src_min_x, src_min_y, src_width, src_height), (dest_x, dest_y), - alpha_bitmap.bitmap_data(), + alpha_bitmap, (alpha_x, alpha_y), merge_alpha.unwrap_or(true), ); @@ -949,8 +957,8 @@ pub fn copy_pixels<'gc>( } else { operations::copy_pixels( &mut activation.context, - bitmap_data.bitmap_data(), - src_bitmap.bitmap_data(), + bitmap_data, + src_bitmap, (src_min_x, src_min_y, src_width, src_height), (dest_x, dest_y), // Despite what the docs claim, mergeAlpa appears to be treated as 'false' @@ -968,12 +976,12 @@ pub fn copy_pixels<'gc>( Ok((-1).into()) } -pub fn merge<'gc>( +fn merge<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let source_bitmap = args .get(0) @@ -1026,12 +1034,12 @@ pub fn merge<'gc>( .unwrap_or(&Value::Undefined) .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() { operations::merge( activation.context.gc_context, - bitmap_data.bitmap_data(), - src_bitmap.bitmap_data(), + bitmap_data, + src_bitmap, (src_min_x, src_min_y, src_width, src_height), (dest_x, dest_y), (red_mult, green_mult, blue_mult, alpha_mult), @@ -1046,12 +1054,12 @@ pub fn merge<'gc>( Ok((-1).into()) } -pub fn palette_map<'gc>( +fn palette_map<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let source_bitmap = args .get(0) @@ -1105,12 +1113,12 @@ pub fn palette_map<'gc>( let blue_array = get_channel(5, 0)?; 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() { operations::palette_map( activation.context.gc_context, - bitmap_data.bitmap_data(), - src_bitmap.bitmap_data(), + bitmap_data, + src_bitmap, (src_min_x, src_min_y, src_width, src_height), (dest_x, dest_y), (red_array, green_array, blue_array, alpha_array), @@ -1125,12 +1133,12 @@ pub fn palette_map<'gc>( Ok((-1).into()) } -pub fn pixel_dissolve<'gc>( +fn pixel_dissolve<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let src_bitmap_data = args .get(0) @@ -1159,7 +1167,7 @@ pub fn pixel_dissolve<'gc>( 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() { let dest_point = args .get(2) @@ -1186,8 +1194,8 @@ pub fn pixel_dissolve<'gc>( return Ok(operations::pixel_dissolve( activation.context.gc_context, - bitmap_data.bitmap_data(), - src_bitmap_data.bitmap_data(), + bitmap_data, + src_bitmap_data, (src_min_x, src_min_y, src_width, src_height), dest_point, random_seed, @@ -1203,12 +1211,12 @@ pub fn pixel_dissolve<'gc>( Ok((-1).into()) } -pub fn scroll<'gc>( +fn scroll<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let x = args .get(0) @@ -1219,12 +1227,7 @@ pub fn scroll<'gc>( .unwrap_or(&Value::Undefined) .coerce_to_i32(activation)?; - operations::scroll( - activation.context.gc_context, - bitmap_data.bitmap_data(), - x, - y, - ); + operations::scroll(activation.context.gc_context, bitmap_data, x, y); return Ok(Value::Undefined); } @@ -1233,12 +1236,12 @@ pub fn scroll<'gc>( Ok((-1).into()) } -pub fn threshold<'gc>( +fn threshold<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.as_bitmap_data_object() { + if let NativeObject::BitmapData(bitmap_data) = this.native() { if !bitmap_data.disposed() { let source_bitmap = args .get(0) @@ -1298,12 +1301,12 @@ pub fn threshold<'gc>( .unwrap_or(&false.into()) .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() { let modified_count = operations::threshold( activation.context.gc_context, - bitmap_data.bitmap_data(), - src_bitmap.bitmap_data(), + bitmap_data, + src_bitmap, (src_min_x, src_min_y, src_width, src_height), (dest_x, dest_y), operation, @@ -1324,7 +1327,7 @@ pub fn threshold<'gc>( Ok((-1).into()) } -pub fn compare<'gc>( +fn compare<'gc>( activation: &mut Activation<'_, 'gc>, this: Object<'gc>, args: &[Value<'gc>], @@ -1335,11 +1338,7 @@ pub fn compare<'gc>( const DIFFERENT_WIDTHS: i32 = -3; const DIFFERENT_HEIGHTS: i32 = -4; - let this_bitmap_data = if let Some(bitmap_data) = this.as_bitmap_data_object() { - bitmap_data - } else { - return Ok(NOT_BITMAP.into()); - }; + let NativeObject::BitmapData(this_bitmap_data) = this.native() else { return Ok(NOT_BITMAP.into()); }; if this_bitmap_data.disposed() { // 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) .coerce_to_object(activation); - let other_bitmap_data = if let Some(other_bitmap_data) = other.as_bitmap_data_object() { - other_bitmap_data - } else { + let NativeObject::BitmapData(other_bitmap_data) = other.native() else { // The documentation says that -1 should be returned here, but -2 is actually returned. return Ok(BITMAP_DISPOSED.into()); }; @@ -1362,9 +1359,6 @@ pub fn compare<'gc>( 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() { return Ok(DIFFERENT_WIDTHS.into()); } @@ -1374,9 +1368,9 @@ pub fn compare<'gc>( } 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.avm1.prototypes().bitmap_data, + this.get_local_stored("__proto__", activation), bitmap_data, ) .into()), @@ -1384,20 +1378,9 @@ pub fn compare<'gc>( } } -pub fn create_proto<'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>( +fn load_bitmap<'gc>( activation: &mut Activation<'_, 'gc>, - _this: Object<'gc>, + this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { let name = args @@ -1407,50 +1390,47 @@ pub fn load_bitmap<'gc>( let library = &*activation.context.library; - let movie = activation.target_clip_or_root().movie(); + let movie = ::movie( + &activation.target_clip_or_root(), + ); let character = library .library_for_movie(movie) .and_then(|l| l.character_by_export_name(name)); - if let Some(Character::Bitmap(bitmap)) = character { - let new_bitmap_data = BitmapDataObject::empty_object( - activation.context.gc_context, - activation.context.avm1.prototypes().bitmap_data, - ); + let Some(Character::Bitmap(bitmap)) = character else { return Ok(Value::Undefined); }; - let width = bitmap.width() as u32; - let height = bitmap.height() as u32; - - let pixels: Vec<_> = bitmap.bitmap_data().read().pixels().to_vec(); - let (sync, _) = new_bitmap_data - .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) + let transparency = true; + 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( + activation.context.gc_context, + this.get_local_stored("prototype", activation), + bitmap_data, + ) + .into()) } -pub fn create_bitmap_data_object<'gc>( +pub fn create_constructor<'gc>( context: &mut GcContext<'_, 'gc>, - bitmap_data_proto: Object<'gc>, + proto: Object<'gc>, fn_proto: 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, Executable::Native(constructor), constructor_to_fn!(constructor), 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); - bitmap_data + bitmap_data_constructor } diff --git a/core/src/avm1/globals/displacement_map_filter.rs b/core/src/avm1/globals/displacement_map_filter.rs index 776a211e4..42e79da2a 100644 --- a/core/src/avm1/globals/displacement_map_filter.rs +++ b/core/src/avm1/globals/displacement_map_filter.rs @@ -4,6 +4,7 @@ use crate::avm1::activation::Activation; use crate::avm1::clamp::Clamp; use crate::avm1::error::Error; 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::{Object, TObject, Value}; use crate::context::GcContext; @@ -169,14 +170,11 @@ pub fn set_map_bitmap<'gc>( this: Object<'gc>, args: &[Value<'gc>], ) -> Result, 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 bitmap.as_bitmap_data_object().is_some() { - object.set_map_bitmap(activation.context.gc_context, Some(bitmap)); + if let [Value::Object(map_bitmap), ..] = args { + if let NativeObject::BitmapData(_) = map_bitmap.native() { + object.set_map_bitmap(activation.context.gc_context, Some(*map_bitmap)); + } } } diff --git a/core/src/avm1/globals/movie_clip.rs b/core/src/avm1/globals/movie_clip.rs index e4cdc982e..c0f377ae6 100644 --- a/core/src/avm1/globals/movie_clip.rs +++ b/core/src/avm1/globals/movie_clip.rs @@ -4,6 +4,7 @@ use crate::avm1::activation::Activation; use crate::avm1::error::Error; use crate::avm1::globals::matrix::gradient_object_to_matrix; 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::{self, Object, ScriptObject, TObject, Value}; use crate::avm_error; @@ -17,7 +18,6 @@ use crate::ecma_conversions::f64_to_wrapping_i32; use crate::prelude::*; use crate::string::AvmString; use crate::vminterface::Instantiator; - use ruffle_render::shape_utils::DrawCommand; use std::str::FromStr; use swf::{ @@ -243,12 +243,8 @@ fn attach_bitmap<'gc>( activation: &mut Activation<'_, 'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap) = args.get(0) { - if let Some(bitmap_data) = bitmap - .coerce_to_object(activation) - .as_bitmap_data_object() - .map(|bd| bd.bitmap_data()) - { + if let [Value::Object(bitmap_data), ..] = args { + if let NativeObject::BitmapData(bitmap_data) = bitmap_data.native() { if let Some(depth) = args.get(1) { let depth = depth .coerce_to_i32(activation)? @@ -395,55 +391,54 @@ fn begin_bitmap_fill<'gc>( activation: &mut Activation<'_, 'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = args - .get(0) - .and_then(|val| val.coerce_to_object(activation).as_bitmap_data_object()) - { - // Register the bitmap data with the drawing. - let bitmap_data = bitmap_data.bitmap_data(); - let handle = - bitmap_data.bitmap_handle(activation.context.gc_context, activation.context.renderer); - let bitmap = ruffle_render::bitmap::BitmapInfo { - handle, - width: bitmap_data.width() as u16, - height: bitmap_data.height() as u16, - }; - let id = movie_clip - .drawing(activation.context.gc_context) - .add_bitmap(bitmap); + let fill_style = if let [Value::Object(bitmap_data), ..] = args { + if let NativeObject::BitmapData(bitmap_data) = bitmap_data.native() { + // Register the bitmap data with the drawing. + let handle = bitmap_data + .bitmap_handle(activation.context.gc_context, activation.context.renderer); + let bitmap = ruffle_render::bitmap::BitmapInfo { + handle, + width: bitmap_data.width() as u16, + height: bitmap_data.height() as u16, + }; + let id = movie_clip + .drawing(activation.context.gc_context) + .add_bitmap(bitmap); - let mut matrix = avm1::globals::matrix::object_to_matrix_or_default( - args.get(1) - .unwrap_or(&Value::Undefined) - .coerce_to_object(activation), - activation, - )?; - // Flash matrix is in pixels. Scale from pixels to twips. - matrix *= Matrix::scale(Twips::TWIPS_PER_PIXEL as f32, Twips::TWIPS_PER_PIXEL as f32); + let mut matrix = avm1::globals::matrix::object_to_matrix_or_default( + args.get(1) + .unwrap_or(&Value::Undefined) + .coerce_to_object(activation), + activation, + )?; + // Flash matrix is in pixels. Scale from pixels to twips. + matrix *= Matrix::scale(Twips::TWIPS_PER_PIXEL as f32, Twips::TWIPS_PER_PIXEL as f32); - // `repeating` defaults to true, `smoothed` to false. - // `smoothed` parameter may not be listed in some documentation. - let is_repeating = args - .get(2) - .unwrap_or(&true.into()) - .as_bool(activation.swf_version()); - let is_smoothed = args - .get(3) - .unwrap_or(&false.into()) - .as_bool(activation.swf_version()); - movie_clip - .drawing(activation.context.gc_context) - .set_fill_style(Some(FillStyle::Bitmap { + // `repeating` defaults to true, `smoothed` to false. + // `smoothed` parameter may not be listed in some documentation. + let is_repeating = args + .get(2) + .unwrap_or(&true.into()) + .as_bool(activation.swf_version()); + let is_smoothed = args + .get(3) + .unwrap_or(&false.into()) + .as_bool(activation.swf_version()); + Some(FillStyle::Bitmap { id, matrix: matrix.into(), is_smoothed, is_repeating, - })); + }) + } else { + None + } } else { - movie_clip - .drawing(activation.context.gc_context) - .set_fill_style(None); - } + None + }; + movie_clip + .drawing(activation.context.gc_context) + .set_fill_style(fill_style); Ok(Value::Undefined) } diff --git a/core/src/avm1/object.rs b/core/src/avm1/object.rs index 67cd91281..b188878d7 100644 --- a/core/src/avm1/object.rs +++ b/core/src/avm1/object.rs @@ -9,7 +9,6 @@ use crate::avm1::globals::date::Date; use crate::avm1::globals::drop_shadow_filter::DropShadowFilter; use crate::avm1::globals::glow_filter::GlowFilter; 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::displacement_map_filter::DisplacementMapFilterObject; 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_object::XmlObject; 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::TDisplayObject; use crate::html::TextFormat; @@ -32,7 +32,6 @@ use ruffle_macros::enum_trait_object; use std::fmt::Debug; pub mod array_object; -pub mod bitmap_data; pub mod convolution_filter; mod custom_object; pub mod displacement_map_filter; @@ -61,6 +60,7 @@ pub enum NativeObject<'gc> { ColorTransform(GcCell<'gc, ColorTransformObject>), TextFormat(GcCell<'gc, TextFormat>), NetStream(NetStream<'gc>), + BitmapData(BitmapDataWrapper<'gc>), } /// Represents an object that can be directly interacted with by the AVM @@ -85,7 +85,6 @@ pub enum NativeObject<'gc> { ConvolutionFilterObject(ConvolutionFilterObject<'gc>), GradientBevelFilterObject(GradientBevelFilterObject<'gc>), GradientGlowFilterObject(GradientGlowFilterObject<'gc>), - BitmapData(BitmapDataObject<'gc>), } )] pub trait TObject<'gc>: 'gc + Collect + Into> + Clone + Copy { @@ -627,11 +626,6 @@ pub trait TObject<'gc>: 'gc + Collect + Into> + Clone + Copy { None } - /// Get the underlying `BitmapDataObject`, if it exists - fn as_bitmap_data_object(&self) -> Option> { - None - } - fn as_ptr(&self) -> *const ObjectPtr; /// Check if this object is in the prototype chain of the specified test object. diff --git a/core/src/avm1/object/bitmap_data.rs b/core/src/avm1/object/bitmap_data.rs deleted file mode 100644 index 82de10070..000000000 --- a/core/src/avm1/object/bitmap_data.rs +++ /dev/null @@ -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); - }); -} diff --git a/core/src/avm2/globals/flash/display/bitmap.rs b/core/src/avm2/globals/flash/display/bitmap.rs index 7d3aa8f1f..693eeb8b7 100644 --- a/core/src/avm2/globals/flash/display/bitmap.rs +++ b/core/src/avm2/globals/flash/display/bitmap.rs @@ -55,7 +55,8 @@ pub fn init<'gc>( .character_by_id(symbol_id) .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( activation, new_bitmap_data, diff --git a/core/src/avm2/globals/flash/display/bitmap_data.rs b/core/src/avm2/globals/flash/display/bitmap_data.rs index c781ddf88..3669236d8 100644 --- a/core/src/avm2/globals/flash/display/bitmap_data.rs +++ b/core/src/avm2/globals/flash/display/bitmap_data.rs @@ -3,7 +3,9 @@ use crate::avm2::activation::Activation; use crate::avm2::error::{argument_error, make_error_2008, range_error}; 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::parameters::{null_parameter_error, ParametersExt}; use crate::avm2::value::Value; use crate::avm2::vector::VectorStorage; use crate::avm2::Error; @@ -14,34 +16,29 @@ use crate::bitmap::bitmap_data::{BitmapDataDrawError, IBitmapDrawable}; use crate::bitmap::{is_size_valid, operations}; use crate::character::Character; use crate::display_object::Bitmap; +use crate::display_object::TDisplayObject; use crate::swf::BlendMode; -use gc_arena::GcCell; +use gc_arena::{GcCell, MutationContext}; use ruffle_render::filters::Filter; use ruffle_render::transform::Transform; 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. /// /// `bd` is assumed to be an uninstantiated library symbol, associated with the /// class named by `name`. pub fn fill_bitmap_data_from_symbol<'gc>( - activation: &mut Activation<'_, 'gc>, - bd: Bitmap<'gc>, + gc_context: MutationContext<'gc, '_>, + bitmap: Bitmap<'gc>, ) -> BitmapDataWrapper<'gc> { - let new_bitmap_data = GcCell::allocate(activation.context.gc_context, BitmapData::default()); - new_bitmap_data - .write(activation.context.gc_context) - .set_pixels( - bd.width().into(), - bd.height().into(), - true, - bd.bitmap_data().read().pixels().to_vec(), - ); - BitmapDataWrapper::new(new_bitmap_data) + let transparency = true; + let bitmap_data = BitmapData::new_with_pixels( + bitmap.width().into(), + bitmap.height().into(), + transparency, + bitmap.bitmap_data().read().pixels().to_vec(), + ); + BitmapDataWrapper::new(GcCell::allocate(gc_context, bitmap_data)) } /// 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 { // 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 { - let new_bitmap_data = - GcCell::allocate(activation.context.gc_context, BitmapData::default()); - if character.is_some() { //TODO: Determine if mismatched symbols will still work as a //regular BitmapData subclass, or if this should throw @@ -92,7 +86,7 @@ pub fn init<'gc>( let width = args.get_u32(activation, 0)?; let height = args.get_u32(activation, 1)?; 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) { return Err(Error::AvmError(argument_error( @@ -102,10 +96,11 @@ pub fn init<'gc>( )?)); } - new_bitmap_data - .write(activation.context.gc_context) - .init_pixels(width, height, transparency, fill_color as i32); - BitmapDataWrapper::new(new_bitmap_data) + let new_bitmap_data = BitmapData::new(width, height, transparency, fill_color); + BitmapDataWrapper::new(GcCell::allocate( + activation.context.gc_context, + new_bitmap_data, + )) }; new_bitmap_data.init_object2(activation.context.gc_context, this); diff --git a/core/src/bitmap/bitmap_data.rs b/core/src/bitmap/bitmap_data.rs index ba78a1b0f..f0a045840 100644 --- a/core/src/bitmap/bitmap_data.rs +++ b/core/src/bitmap/bitmap_data.rs @@ -199,7 +199,7 @@ bitflags! { } } -#[derive(Clone, Collect, Default)] +#[derive(Clone, Collect)] #[collect(no_drop)] pub struct BitmapData<'gc> { /// 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, } -#[derive(Clone, Collect, Default, Debug)] +#[derive(Clone, Collect, Debug)] #[collect(require_static)] enum DirtyState { // Both the CPU and GPU pixels are up to date. We do not need to wait for any syncs to complete - #[default] Clean, + // The CPU pixels have been modified, and need to be synced to the GPU via `update_dirty_texture` CpuModified(PixelRegion), + // The GPU pixels have been modified, and need to be synced to the CPU via `BitmapDataWrapper::sync` GpuModified(Box, PixelRegion), } @@ -483,15 +484,20 @@ impl fmt::Debug for BitmapData<'_> { } impl<'gc> BitmapData<'gc> { - pub fn init_pixels(&mut self, width: u32, height: u32, transparency: bool, fill_color: i32) { - self.width = width; - self.height = height; - self.transparency = transparency; - self.pixels = vec![ - Color(fill_color).to_premultiplied_alpha(self.transparency()); - width as usize * height as usize - ]; - self.set_cpu_dirty(PixelRegion::for_whole_size(width, height)); + pub fn new(width: u32, height: u32, transparency: bool, fill_color: i32) -> Self { + Self { + pixels: vec![ + Color(fill_color).to_premultiplied_alpha(transparency); + width as usize * height as usize + ], + width, + height, + transparency, + disposed: false, + bitmap_handle: None, + avm2_object: None, + dirty_state: DirtyState::Clean, + } } pub fn new_with_pixels( @@ -568,14 +574,6 @@ impl<'gc> BitmapData<'gc> { &self.pixels } - pub fn set_pixels(&mut self, width: u32, height: u32, transparency: bool, pixels: Vec) { - 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 { // TODO: This could have been implemented as follows: // diff --git a/core/src/display_object/bitmap.rs b/core/src/display_object/bitmap.rs index c6d018dbf..dd9e84b0e 100644 --- a/core/src/display_object/bitmap.rs +++ b/core/src/display_object/bitmap.rs @@ -5,7 +5,7 @@ use crate::avm2::{ Activation as Avm2Activation, ClassObject as Avm2ClassObject, Object as Avm2Object, 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::display_object::{DisplayObjectBase, DisplayObjectPtr, TDisplayObject}; use crate::prelude::*; @@ -135,30 +135,24 @@ impl<'gc> Bitmap<'gc> { ) -> Result { let width = bitmap.width(); let height = bitmap.height(); + let transparency = match bitmap.format() { + BitmapFormat::Rgba => true, + BitmapFormat::Rgb => false, + _ => unreachable!( + "Bitmap objects can only be constructed from RGB or RGBA source bitmaps" + ), + }; let pixels: Vec<_> = bitmap .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::Rgb => false, - _ => unreachable!( - "Bitmap objects can only be constructed from RGB or RGBA source bitmaps" - ), - }, - pixels, - ); - let bitmap_data = GcCell::allocate(context.gc_context, bitmap_data); + let bitmap_data = BitmapData::new_with_pixels(width, height, transparency, pixels); let smoothing = true; Ok(Self::new_with_bitmap_data( context, id, - BitmapDataWrapper::new(bitmap_data), + BitmapDataWrapper::new(GcCell::allocate(context.gc_context, bitmap_data)), smoothing, )) } @@ -179,7 +173,7 @@ impl<'gc> Bitmap<'gc> { } /// 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() } diff --git a/core/src/loader.rs b/core/src/loader.rs index 214897e6f..07b1d7b79 100644 --- a/core/src/loader.rs +++ b/core/src/loader.rs @@ -5,16 +5,16 @@ use crate::avm1::ExecutionReason; use crate::avm1::{Activation, ActivationIdentifier}; use crate::avm1::{Object, SoundObject, TObject, Value}; use crate::avm2::bytearray::ByteArrayStorage; -use crate::avm2::object::ByteArrayObject; -use crate::avm2::object::EventObject as Avm2EventObject; -use crate::avm2::object::LoaderStream; -use crate::avm2::object::TObject as _; +use crate::avm2::object::{ + BitmapDataObject, ByteArrayObject, EventObject as Avm2EventObject, LoaderStream, TObject as _, +}; use crate::avm2::{ Activation as Avm2Activation, Avm2, Domain as Avm2Domain, Object as Avm2Object, Value as Avm2Value, }; use crate::backend::navigator::{OwnedFuture, Request}; use crate::bitmap::bitmap_data::Color; +use crate::bitmap::bitmap_data::{BitmapData, BitmapDataWrapper}; use crate::context::{ActionQueue, ActionType, UpdateContext}; use crate::display_object::{ DisplayObject, TDisplayObject, TDisplayObjectContainer, TInteractiveObject, @@ -28,7 +28,7 @@ use crate::string::AvmString; use crate::tag_utils::SwfMovie; use crate::vminterface::Instantiator; use encoding_rs::UTF_8; -use gc_arena::{Collect, CollectionContext}; +use gc_arena::{Collect, CollectionContext, GcCell}; use generational_arena::{Arena, Index}; use ruffle_render::utils::{determine_jpeg_tag_format, JpegTagFormat}; use std::fmt; @@ -1492,28 +1492,24 @@ impl<'gc> Loader<'gc> { // since Bitmap and BitmapData never have AVM1-side objects. let bitmap = ruffle_render::utils::decode_define_bits_jpeg(data, None)?; - let bitmapdata_avm2 = activation - .avm2() - .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.height(), - true, - bitmap.as_colors().map(Color::from).collect(), - ); + let transparency = true; + let bitmap_data = BitmapData::new_with_pixels( + bitmap.width(), + bitmap.height(), + transparency, + 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 .avm2()