avm1: Refactor and fix `BitmapData::compare`
This commit is contained in:
parent
687069ba7d
commit
56b5183262
|
@ -1069,62 +1069,61 @@ pub fn compare<'gc>(
|
|||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<Value<'gc>, Error<'gc>> {
|
||||
if let Some(bitmap_data) = this.as_bitmap_data_object() {
|
||||
if bitmap_data.disposed() {
|
||||
return Ok((-2).into());
|
||||
}
|
||||
const EQUIVALENT: i32 = 0;
|
||||
const NOT_BITMAP: i32 = -1;
|
||||
const BITMAP_DISPOSED: i32 = -2;
|
||||
const DIFFERENT_WIDTHS: i32 = -3;
|
||||
const DIFFERENT_HEIGHTS: i32 = -4;
|
||||
|
||||
let other_bitmap = args
|
||||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.coerce_to_object(activation);
|
||||
let this_bitmap_data = if let Some(bitmap_data) = this.as_bitmap_data_object() {
|
||||
bitmap_data
|
||||
} else {
|
||||
return Ok(NOT_BITMAP.into());
|
||||
};
|
||||
|
||||
if let Some(other_bitmap_data) = other_bitmap.as_bitmap_data_object() {
|
||||
if other_bitmap_data.disposed() {
|
||||
return Ok((-2).into());
|
||||
}
|
||||
|
||||
if bitmap_data.bitmap_data().read().width()
|
||||
!= other_bitmap_data.bitmap_data().read().width()
|
||||
{
|
||||
return Ok((-3).into());
|
||||
}
|
||||
if bitmap_data.bitmap_data().read().height()
|
||||
!= other_bitmap_data.bitmap_data().read().height()
|
||||
{
|
||||
return Ok((-4).into());
|
||||
}
|
||||
if bitmap_data
|
||||
.bitmap_data()
|
||||
.read()
|
||||
.pixels()
|
||||
.eq(other_bitmap_data.bitmap_data().read().pixels())
|
||||
{
|
||||
return Ok((0).into());
|
||||
}
|
||||
|
||||
let new_bitmap_data = BitmapDataObject::empty_object(
|
||||
activation.context.gc_context,
|
||||
Some(activation.context.avm1.prototypes.bitmap_data),
|
||||
);
|
||||
|
||||
new_bitmap_data
|
||||
.as_bitmap_data_object()
|
||||
.unwrap()
|
||||
.bitmap_data()
|
||||
.write(activation.context.gc_context)
|
||||
.compare(
|
||||
&bitmap_data.bitmap_data().read(),
|
||||
&other_bitmap_data.bitmap_data().read(),
|
||||
);
|
||||
|
||||
return Ok(new_bitmap_data.into());
|
||||
}
|
||||
|
||||
return Ok(Value::Undefined);
|
||||
if this_bitmap_data.disposed() {
|
||||
// The documentation says that -2 should be returned here, but -1 is actually returned.
|
||||
return Ok(NOT_BITMAP.into());
|
||||
}
|
||||
|
||||
Ok((-1).into())
|
||||
let other = args
|
||||
.get(0)
|
||||
.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 {
|
||||
// The documentation says that -1 should be returned here, but -2 is actually returned.
|
||||
return Ok(BITMAP_DISPOSED.into());
|
||||
};
|
||||
|
||||
if other_bitmap_data.disposed() {
|
||||
return Ok(BITMAP_DISPOSED.into());
|
||||
}
|
||||
|
||||
let this_bitmap_data = this_bitmap_data.bitmap_data();
|
||||
let this_bitmap_data = this_bitmap_data.read();
|
||||
let other_bitmap_data = other_bitmap_data.bitmap_data();
|
||||
let other_bitmap_data = other_bitmap_data.read();
|
||||
|
||||
if this_bitmap_data.width() != other_bitmap_data.width() {
|
||||
return Ok(DIFFERENT_WIDTHS.into());
|
||||
}
|
||||
|
||||
if this_bitmap_data.height() != other_bitmap_data.height() {
|
||||
return Ok(DIFFERENT_HEIGHTS.into());
|
||||
}
|
||||
|
||||
match BitmapData::compare(&this_bitmap_data, &other_bitmap_data) {
|
||||
Some(bitmap_data) => Ok(BitmapDataObject::with_bitmap_data(
|
||||
activation.context.gc_context,
|
||||
Some(activation.context.avm1.prototypes.bitmap_data),
|
||||
bitmap_data,
|
||||
)
|
||||
.into()),
|
||||
None => Ok(EQUIVALENT.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_proto<'gc>(
|
||||
|
|
|
@ -36,12 +36,20 @@ impl<'gc> BitmapDataObject<'gc> {
|
|||
);
|
||||
|
||||
pub fn empty_object(gc_context: MutationContext<'gc, '_>, proto: Option<Object<'gc>>) -> Self {
|
||||
BitmapDataObject(GcCell::allocate(
|
||||
Self::with_bitmap_data(gc_context, proto, Default::default())
|
||||
}
|
||||
|
||||
pub fn with_bitmap_data(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
proto: Option<Object<'gc>>,
|
||||
bitmap_data: BitmapData<'gc>,
|
||||
) -> Self {
|
||||
Self(GcCell::allocate(
|
||||
gc_context,
|
||||
BitmapDataData {
|
||||
base: ScriptObject::object(gc_context, proto),
|
||||
disposed: false,
|
||||
data: GcCell::allocate(gc_context, BitmapData::default()),
|
||||
data: GcCell::allocate(gc_context, bitmap_data),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
|
|
@ -133,7 +133,7 @@ bitflags! {
|
|||
#[collect(no_drop)]
|
||||
pub struct BitmapData<'gc> {
|
||||
/// The pixels in the bitmap, stored as a array of pre-multiplied ARGB colour values
|
||||
pub pixels: Vec<Color>,
|
||||
pixels: Vec<Color>,
|
||||
dirty: bool,
|
||||
width: u32,
|
||||
height: u32,
|
||||
|
@ -854,32 +854,56 @@ impl<'gc> BitmapData<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn compare(&mut self, bitmap: &Self, other: &Self) {
|
||||
// Should be replaced with i32::abs_diff once stabilized (https://github.com/rust-lang/rust/issues/89492)
|
||||
fn abs_diff(a: i32, b: i32) -> i32 {
|
||||
if a > b {
|
||||
a.wrapping_sub(b)
|
||||
} else {
|
||||
b.wrapping_sub(a)
|
||||
}
|
||||
}
|
||||
/// Compare two BitmapData objects.
|
||||
/// Returns `None` if the bitmaps are equivalent.
|
||||
pub fn compare(bitmap: &Self, other: &Self) -> Option<Self> {
|
||||
// This function expects that the two bitmaps have the same dimensions.
|
||||
// TODO: Relax this assumption and return a special value instead?
|
||||
debug_assert_eq!(bitmap.width, other.width);
|
||||
debug_assert_eq!(bitmap.height, other.height);
|
||||
|
||||
for i in 0..self.pixels().len() {
|
||||
let bitmap_pixel = bitmap.pixels()[i];
|
||||
let other_pixel = other.pixels()[i];
|
||||
self.pixels[i] = Color(
|
||||
if bitmap_pixel.with_alpha(0xffu8) != other_pixel.with_alpha(0xffu8) {
|
||||
(0xff << 24)
|
||||
+ (abs_diff(bitmap_pixel.red() as i32, other_pixel.red() as i32) << 16)
|
||||
+ (abs_diff(bitmap_pixel.green() as i32, other_pixel.green() as i32) << 8)
|
||||
+ abs_diff(bitmap_pixel.blue() as i32, other_pixel.blue() as i32)
|
||||
} else if bitmap_pixel.alpha() != other_pixel.alpha() {
|
||||
(abs_diff(bitmap_pixel.alpha() as i32, other_pixel.alpha() as i32) << 24)
|
||||
+ 0xffffff
|
||||
let mut different = false;
|
||||
let pixels = bitmap
|
||||
.pixels
|
||||
.iter()
|
||||
.zip(&other.pixels)
|
||||
.map(|(bitmap_pixel, other_pixel)| {
|
||||
let bitmap_pixel = bitmap_pixel.to_un_multiplied_alpha();
|
||||
let other_pixel = other_pixel.to_un_multiplied_alpha();
|
||||
if bitmap_pixel == other_pixel {
|
||||
Color::argb(0, 0, 0, 0)
|
||||
} else if bitmap_pixel.with_alpha(0) != other_pixel.with_alpha(0) {
|
||||
different = true;
|
||||
Color::argb(
|
||||
0xff,
|
||||
bitmap_pixel.red().wrapping_sub(other_pixel.red()),
|
||||
bitmap_pixel.green().wrapping_sub(other_pixel.green()),
|
||||
bitmap_pixel.blue().wrapping_sub(other_pixel.blue()),
|
||||
)
|
||||
} else {
|
||||
0
|
||||
},
|
||||
)
|
||||
different = true;
|
||||
Color::argb(
|
||||
bitmap_pixel.alpha().wrapping_sub(other_pixel.alpha()),
|
||||
0xff,
|
||||
0xff,
|
||||
0xff,
|
||||
)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if different {
|
||||
Some(Self {
|
||||
pixels,
|
||||
dirty: false,
|
||||
width: bitmap.width,
|
||||
height: bitmap.height,
|
||||
transparency: true,
|
||||
bitmap_handle: None,
|
||||
avm2_object: None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue