diff --git a/core/src/avm1/globals/bitmap_data.rs b/core/src/avm1/globals/bitmap_data.rs index 3aead4283..5897dc7bb 100644 --- a/core/src/avm1/globals/bitmap_data.rs +++ b/core/src/avm1/globals/bitmap_data.rs @@ -1173,26 +1173,14 @@ pub fn palette_map<'gc>( if let Some(src_bitmap) = source_bitmap.as_bitmap_data_object() { if !src_bitmap.disposed() { - // dealing with object aliasing... - let src_bitmap_data_cell = src_bitmap.bitmap_data(); - let read; - let source: Option<&BitmapData> = - if GcCell::ptr_eq(src_bitmap_data_cell, bitmap_data.bitmap_data()) { - None - } else { - read = src_bitmap_data_cell.read(); - Some(&read) - }; - - bitmap_data - .bitmap_data() - .write(activation.context.gc_context) - .palette_map( - source, - (src_min_x, src_min_y, src_width, src_height), - (dest_x, dest_y), - (red_array, green_array, blue_array, alpha_array), - ); + bitmap_data_operations::palette_map( + &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), + (red_array, green_array, blue_array, alpha_array), + ); } } diff --git a/core/src/avm2/globals/flash/display/bitmap_data.rs b/core/src/avm2/globals/flash/display/bitmap_data.rs index 5838fe728..887dff68f 100644 --- a/core/src/avm2/globals/flash/display/bitmap_data.rs +++ b/core/src/avm2/globals/flash/display/bitmap_data.rs @@ -1135,11 +1135,11 @@ pub fn palette_map<'gc>( this: Option>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - if let Some(bitmap_data) = this.and_then(|this| this.as_bitmap_data()) { - bitmap_data.read().check_valid(activation)?; + if let Some(bitmap_data) = this.and_then(|this| this.as_bitmap_data_wrapper()) { + bitmap_data.check_valid(activation)?; let source_bitmap = args .get_object(activation, 0, "sourceBitmapData")? - .as_bitmap_data() + .as_bitmap_data_wrapper() .unwrap(); let source_rect = args.get_object(activation, 1, "sourceRect")?; @@ -1183,23 +1183,14 @@ pub fn palette_map<'gc>( let blue_array = get_channel(5, 0)?; let alpha_array = get_channel(6, 24)?; - let read; - let source: Option<&BitmapData> = if GcCell::ptr_eq(source_bitmap, bitmap_data) { - None - } else { - read = source_bitmap.read(); - read.check_valid(activation)?; - Some(&read) - }; - - bitmap_data - .write(activation.context.gc_context) - .palette_map( - source, - (source_point.0, source_point.1, source_size.0, source_size.1), - dest_point, - (red_array, green_array, blue_array, alpha_array), - ); + bitmap_data_operations::palette_map( + &mut activation.context, + bitmap_data, + source_bitmap, + (source_point.0, source_point.1, source_size.0, source_size.1), + dest_point, + (red_array, green_array, blue_array, alpha_array), + ); } Ok(Value::Undefined) diff --git a/core/src/bitmap/bitmap_data.rs b/core/src/bitmap/bitmap_data.rs index 4b75b8c41..1f9ac6c20 100644 --- a/core/src/bitmap/bitmap_data.rs +++ b/core/src/bitmap/bitmap_data.rs @@ -824,58 +824,6 @@ impl<'gc> BitmapData<'gc> { self.set_cpu_dirty(dirty_region); } - // Unlike `copy_channel` and `copy_pixels`, this function seems to - // operate "in-place" if the source bitmap is the same object as `self`. - // This means that we can't resolve this aliasing issue in Rust by a - // simple clone in the caller. Instead, if the `source_bitmap` parameter - // is `None`, it means that `self` should be used as source as well. - pub fn palette_map( - &mut self, - source_bitmap: Option<&Self>, - src_rect: (i32, i32, i32, i32), - dest_point: (i32, i32), - channel_arrays: ([u32; 256], [u32; 256], [u32; 256], [u32; 256]), - ) { - let (src_min_x, src_min_y, src_width, src_height) = src_rect; - let (dest_min_x, dest_min_y) = dest_point; - - 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 - .unwrap_or(self) - .is_point_in_bounds(src_x, src_y) - { - continue; - } - - let source_color = source_bitmap - .unwrap_or(self) - .get_pixel32_raw(src_x as u32, src_y as u32) - .to_un_multiplied_alpha(); - - let r = channel_arrays.0[source_color.red() as usize]; - let g = channel_arrays.1[source_color.green() as usize]; - let b = channel_arrays.2[source_color.blue() as usize]; - let a = channel_arrays.3[source_color.alpha() as usize]; - - let sum = u32::wrapping_add(u32::wrapping_add(r, g), u32::wrapping_add(b, a)); - let mix_color = Color(sum as i32).to_premultiplied_alpha(true); - - self.set_pixel32_raw(dest_x as u32, dest_y as u32, mix_color); - } - } - let mut dirty_region = PixelRegion::encompassing_pixels_i32( - ((dest_min_x), (dest_min_y)), - ((dest_min_x + src_width), (dest_min_y + src_height)), - ); - dirty_region.clamp(self.width, self.height); - self.set_cpu_dirty(dirty_region); - } - // 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 01e8d738c..1791f1196 100644 --- a/core/src/bitmap/bitmap_data_operations.rs +++ b/core/src/bitmap/bitmap_data_operations.rs @@ -607,3 +607,68 @@ pub fn scroll<'gc>( let region = PixelRegion::for_whole_size(write.width(), write.height()); write.set_cpu_dirty(region); } + +pub fn palette_map<'gc>( + context: &mut UpdateContext<'_, 'gc>, + target: BitmapDataWrapper<'gc>, + source_bitmap: BitmapDataWrapper<'gc>, + src_rect: (i32, i32, i32, i32), + dest_point: (i32, i32), + channel_arrays: ([u32; 256], [u32; 256], [u32; 256], [u32; 256]), +) { + let (src_min_x, src_min_y, src_width, src_height) = src_rect; + let (dest_min_x, dest_min_y) = dest_point; + + let target = target.sync(); + let source_bitmap = source_bitmap.sync(); + + // Unlike `copy_channel` and `copy_pixels`, this function seems to + // operate "in-place" if the source bitmap is the same object as the destination. + let source_bitmap = if GcCell::ptr_eq(source_bitmap, target) { + None + } else { + Some(source_bitmap.read()) + }; + + let mut write = target.write(context.gc_context); + + 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) { + continue; + } + + let source_color = if let Some(source) = &source_bitmap { + if !source.is_point_in_bounds(src_x, src_y) { + continue; + } + source + .get_pixel32_raw(src_x as u32, src_y as u32) + .to_un_multiplied_alpha() + } else { + write + .get_pixel32_raw(src_x as u32, src_y as u32) + .to_un_multiplied_alpha() + }; + + let r = channel_arrays.0[source_color.red() as usize]; + let g = channel_arrays.1[source_color.green() as usize]; + let b = channel_arrays.2[source_color.blue() as usize]; + let a = channel_arrays.3[source_color.alpha() as usize]; + + let sum = u32::wrapping_add(u32::wrapping_add(r, g), u32::wrapping_add(b, a)); + let mix_color = Color::from(sum as i32).to_premultiplied_alpha(true); + + write.set_pixel32_raw(dest_x as u32, dest_y as u32, mix_color); + } + } + let mut dirty_region = PixelRegion::encompassing_pixels_i32( + ((dest_min_x), (dest_min_y)), + ((dest_min_x + src_width), (dest_min_y + src_height)), + ); + dirty_region.clamp(write.width(), write.height()); + write.set_cpu_dirty(dirty_region); +}