diff --git a/core/src/avm1/globals/bitmap_data.rs b/core/src/avm1/globals/bitmap_data.rs index 182ded7d6..88fb417cc 100644 --- a/core/src/avm1/globals/bitmap_data.rs +++ b/core/src/avm1/globals/bitmap_data.rs @@ -1299,7 +1299,7 @@ pub fn threshold<'gc>( .unwrap_or(&Value::Undefined) .coerce_to_u32(activation)?; - let colour = args.get(5).unwrap_or(&0.into()).coerce_to_u32(activation)?; + let colour = args.get(5).unwrap_or(&0.into()).coerce_to_i32(activation)?; let mask = args .get(6) @@ -1313,32 +1313,18 @@ pub fn threshold<'gc>( if let Some(src_bitmap) = source_bitmap.as_bitmap_data_object() { if !src_bitmap.disposed() { - // dealing with object aliasing... - let src_bitmap_clone: BitmapData; // only initialized if source is the same object as self - let src_bitmap_data_cell = src_bitmap.bitmap_data(); - let src_bitmap_gc_ref; // only initialized if source is a different object than self - let source_bitmap_ref = // holds the reference to either of the ones above - if GcCell::ptr_eq(src_bitmap.bitmap_data(), bitmap_data.bitmap_data()) { - src_bitmap_clone = src_bitmap_data_cell.read().clone(); - &src_bitmap_clone - } else { - src_bitmap_gc_ref = src_bitmap_data_cell.read(); - &src_bitmap_gc_ref - }; - - let modified_count = bitmap_data - .bitmap_data() - .write(activation.context.gc_context) - .threshold( - source_bitmap_ref, - (src_min_x, src_min_y, src_width, src_height), - (dest_x, dest_y), - operation, - threshold, - colour, - mask, - copy_source, - ); + let modified_count = bitmap_data_operations::threshold( + &mut activation.context, + bitmap_data.bitmap_data_wrapper(), + src_bitmap.bitmap_data_wrapper(), + (src_min_x, src_min_y, src_width, src_height), + (dest_x, dest_y), + operation, + threshold, + colour, + mask, + copy_source, + ); return Ok(modified_count.into()); } diff --git a/core/src/avm2/globals/flash/display/bitmap_data.rs b/core/src/avm2/globals/flash/display/bitmap_data.rs index 193e47b4a..b16d97f67 100644 --- a/core/src/avm2/globals/flash/display/bitmap_data.rs +++ b/core/src/avm2/globals/flash/display/bitmap_data.rs @@ -1275,8 +1275,8 @@ pub fn threshold<'gc>( this: Option>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.and_then(|this| this.as_bitmap_data()) { - if !bitmap_data.read().disposed() { + if let Some(bitmap_data) = this.and_then(|this| this.as_bitmap_data_wrapper()) { + if !bitmap_data.disposed() { let src_bitmap = args.get_object(activation, 0, "sourceBitmapData")?; let source_rect = args.get_object(activation, 1, "sourceRect")?; let dest_point = args.get_object(activation, 2, "dstPoint")?; @@ -1290,7 +1290,7 @@ pub fn threshold<'gc>( ); let operation = args.try_get_string(activation, 3)?; let threshold = args.get_u32(activation, 4)?; - let color = args.get_u32(activation, 5)?; + let color = args.get_i32(activation, 5)?; let mask = args.get_u32(activation, 6)?; let copy_source = args.get_bool(7); @@ -1322,33 +1322,22 @@ pub fn threshold<'gc>( .get_public_property("height", activation)? .coerce_to_i32(activation)?; - if let Some(src_bitmap) = src_bitmap.as_bitmap_data() { - src_bitmap.read().check_valid(activation)?; - // dealing with object aliasing... - let src_bitmap_clone: BitmapData; // only initialized if source is the same object as self - let src_bitmap_data_cell = src_bitmap; - let src_bitmap_gc_ref; // only initialized if source is a different object than self - let source_bitmap_ref = // holds the reference to either of the ones above - if GcCell::ptr_eq(src_bitmap, bitmap_data) { - src_bitmap_clone = src_bitmap_data_cell.read().clone(); - &src_bitmap_clone - } else { - src_bitmap_gc_ref = src_bitmap_data_cell.read(); - &src_bitmap_gc_ref - }; - return Ok(bitmap_data - .write(activation.context.gc_context) - .threshold( - source_bitmap_ref, - (src_min_x, src_min_y, src_width, src_height), - dest_point, - operation, - threshold, - color, - mask, - copy_source, - ) - .into()); + if let Some(src_bitmap) = src_bitmap.as_bitmap_data_wrapper() { + src_bitmap.check_valid(activation)?; + + return Ok(bitmap_data_operations::threshold( + &mut activation.context, + bitmap_data, + src_bitmap, + (src_min_x, src_min_y, src_width, src_height), + dest_point, + operation, + threshold, + color, + mask, + copy_source, + ) + .into()); } } } diff --git a/core/src/bitmap/bitmap_data.rs b/core/src/bitmap/bitmap_data.rs index c5111eba0..c92ac0fe8 100644 --- a/core/src/bitmap/bitmap_data.rs +++ b/core/src/bitmap/bitmap_data.rs @@ -920,77 +920,6 @@ impl<'gc> BitmapData<'gc> { self.set_cpu_dirty(PixelRegion::for_whole_size(self.width, self.height)); } - /// This implements the threshold operation generically over the test operation performed for each pixel - /// Returns the number of pixels modified - #[allow(clippy::too_many_arguments)] - pub fn threshold( - &mut self, - source_bitmap: &Self, - src_rect: (i32, i32, i32, i32), - dest_point: (i32, i32), - operation: ThresholdOperation, - threshold: u32, - colour: u32, - mask: u32, - copy_source: bool, - ) -> u32 { - // Pre-compute the masked threshold - let masked_threshold = threshold & mask; - - // Extract coords - let (src_min_x, src_min_y, src_width, src_height) = src_rect; - let (dest_min_x, dest_min_y) = dest_point; - - // The number of modified pixels - // This doesn't seem to include pixels changed due to copy_source - let mut modified_count = 0; - let mut dirty_area: Option = None; - - // Check each pixel - for src_y in src_min_y..(src_min_y + src_height) { - for src_x in src_min_x..(src_min_x + src_width) { - let dest_x = src_x - src_min_x + dest_min_x; - let dest_y = src_y - src_min_y + dest_min_y; - - if !self.is_point_in_bounds(dest_x, dest_y) - || !source_bitmap.is_point_in_bounds(src_x, src_y) - { - continue; - } - - // Extract source colour - let source_color = source_bitmap - .get_pixel32_raw(src_x as u32, src_y as u32) - .to_un_multiplied_alpha(); - - // If the test, as defined by the operation pass then set to input colour - if operation.matches(source_color.0 as u32 & mask, masked_threshold) { - modified_count += 1; - self.set_pixel32_raw(dest_x as u32, dest_y as u32, Color(colour as _)); - } else { - // If the test fails, but copy_source is true then take the colour from the source - if copy_source { - let new_color = source_bitmap - .get_pixel32_raw(dest_x as u32, dest_y as u32) - .to_un_multiplied_alpha(); - - self.set_pixel32_raw(dest_x as u32, dest_y as u32, new_color); - } - } - if let Some(dirty_area) = &mut dirty_area { - dirty_area.encompass(dest_x as u32, dest_y as u32); - } else { - dirty_area = Some(PixelRegion::for_pixel(dest_x as u32, dest_y as u32)); - } - } - } - if let Some(dirty_area) = dirty_area { - self.set_cpu_dirty(dirty_area); - } - - modified_count - } - // Updates the data stored with our `BitmapHandle` if this `BitmapData` // is dirty pub fn update_dirty_texture(&mut self, renderer: &mut dyn RenderBackend) { diff --git a/core/src/bitmap/bitmap_data_operations.rs b/core/src/bitmap/bitmap_data_operations.rs index 090417b83..6835f7029 100644 --- a/core/src/bitmap/bitmap_data_operations.rs +++ b/core/src/bitmap/bitmap_data_operations.rs @@ -1,4 +1,6 @@ -use crate::bitmap::bitmap_data::{BitmapData, BitmapDataWrapper, ChannelOptions, Color, LehmerRng}; +use crate::bitmap::bitmap_data::{ + BitmapData, BitmapDataWrapper, ChannelOptions, Color, LehmerRng, ThresholdOperation, +}; use crate::bitmap::turbulence::Turbulence; use crate::context::UpdateContext; use gc_arena::GcCell; @@ -463,3 +465,92 @@ pub fn color_transform<'gc>( (x_max - 1, y_max - 1), )); } + +#[allow(clippy::too_many_arguments)] +pub fn threshold<'gc>( + context: &mut UpdateContext<'_, 'gc>, + target: BitmapDataWrapper<'gc>, + source_bitmap: BitmapDataWrapper<'gc>, + src_rect: (i32, i32, i32, i32), + dest_point: (i32, i32), + operation: ThresholdOperation, + threshold: u32, + colour: i32, + mask: u32, + copy_source: bool, +) -> u32 { + // Pre-compute the masked threshold + let masked_threshold = threshold & mask; + + // Extract coords + let (src_min_x, src_min_y, src_width, src_height) = src_rect; + let (dest_min_x, dest_min_y) = dest_point; + + // The number of modified pixels + // This doesn't seem to include pixels changed due to copy_source + let mut modified_count = 0; + let mut dirty_area: Option = None; + + let target = target.sync(); + let source_bitmap = source_bitmap.sync(); + + // dealing with object aliasing... + let src_bitmap_clone: BitmapData; // only initialized if source is the same object as self + let src_bitmap_data_cell = source_bitmap; + let src_bitmap_gc_ref; // only initialized if source is a different object than self + let source_bitmap_ref = // holds the reference to either of the ones above + if GcCell::ptr_eq(source_bitmap, target) { + src_bitmap_clone = src_bitmap_data_cell.read().clone(); + &src_bitmap_clone + } else { + src_bitmap_gc_ref = src_bitmap_data_cell.read(); + &src_bitmap_gc_ref + }; + + let mut write = target.write(context.gc_context); + + // Check each pixel + for src_y in src_min_y..(src_min_y + src_height) { + for src_x in src_min_x..(src_min_x + src_width) { + let dest_x = src_x - src_min_x + dest_min_x; + let dest_y = src_y - src_min_y + dest_min_y; + + if !write.is_point_in_bounds(dest_x, dest_y) + || !source_bitmap_ref.is_point_in_bounds(src_x, src_y) + { + continue; + } + + // Extract source colour + let source_color = source_bitmap_ref + .get_pixel32_raw(src_x as u32, src_y as u32) + .to_un_multiplied_alpha(); + + // If the test, as defined by the operation pass then set to input colour + if operation.matches(i32::from(source_color) as u32 & mask, masked_threshold) { + modified_count += 1; + write.set_pixel32_raw(dest_x as u32, dest_y as u32, Color::from(colour)); + } else { + // If the test fails, but copy_source is true then take the colour from the source + if copy_source { + let new_color = source_bitmap_ref + .get_pixel32_raw(dest_x as u32, dest_y as u32) + .to_un_multiplied_alpha(); + + write.set_pixel32_raw(dest_x as u32, dest_y as u32, new_color); + } + } + if let Some(dirty_area) = &mut dirty_area { + dirty_area.encompass(dest_x as u32, dest_y as u32); + } else { + dirty_area = Some(PixelRegion::for_pixel(dest_x as u32, dest_y as u32)); + } + } + } + + if let Some(dirty_area) = dirty_area { + write.set_cpu_dirty(dirty_area); + } + + modified_count +}