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_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,

View File

@ -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<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>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, 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<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() {
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<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() {
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<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() {
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<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() {
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<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 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<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 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<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 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<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 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<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() {
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<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() {
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<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 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<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() {
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<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() {
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<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 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<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() {
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<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() {
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<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() {
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<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() {
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<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() {
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<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() {
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<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() {
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<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() {
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<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() {
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<Value<'gc>, 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 = <DisplayObject as crate::display_object::TDisplayObject>::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
}

View File

@ -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<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 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));
}
}
}

View File

@ -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<Value<'gc>, 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<Value<'gc>, 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)
}

View File

@ -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<Object<'gc>> + Clone + Copy {
@ -627,11 +626,6 @@ pub trait TObject<'gc>: 'gc + Collect + Into<Object<'gc>> + Clone + Copy {
None
}
/// Get the underlying `BitmapDataObject`, if it exists
fn as_bitmap_data_object(&self) -> Option<BitmapDataObject<'gc>> {
None
}
fn as_ptr(&self) -> *const ObjectPtr;
/// 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)
.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,

View File

@ -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);

View File

@ -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<dyn SyncHandle>, 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<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> {
// 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,
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<Self, ruffle_render::error::Error> {
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()
}

View File

@ -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()