Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 118 additions & 4 deletions core/src/avm1/globals/bitmap_data.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
//! flash.display.BitmapData object

use crate::avm1::activation::Activation;
use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::object::bitmap_data::{BitmapDataObject, ChannelOptions, Color};
use crate::avm1::{activation::Activation, object::bitmap_data::BitmapData};
use crate::avm1::{Object, TObject, Value};
use crate::character::Character;
use crate::display_object::TDisplayObject;
Expand Down Expand Up @@ -613,13 +613,127 @@ pub fn hit_test<'gc>(
}

pub fn copy_pixels<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() {
if !bitmap_data.disposed() {
log::warn!("BitmapData.copyPixels - not yet implemented");
let source_bitmap = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_object(activation);

let source_rect = args
.get(1)
.unwrap_or(&Value::Undefined)
.coerce_to_object(activation);

let src_min_x = source_rect
.get("x", activation)?
.coerce_to_i32(activation)?;
let src_min_y = source_rect
.get("y", activation)?
.coerce_to_i32(activation)?;
let src_width = source_rect
.get("width", activation)?
.coerce_to_i32(activation)?;
let src_height = source_rect
.get("height", activation)?
.coerce_to_i32(activation)?;

let dest_point = args
.get(2)
.unwrap_or(&Value::Undefined)
.coerce_to_object(activation);

let dest_x = dest_point.get("x", activation)?.coerce_to_i32(activation)?;
let dest_y = dest_point.get("y", activation)?.coerce_to_i32(activation)?;

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

if args.len() >= 5 {
let alpha_point = args
.get(4)
.unwrap_or(&Value::Undefined)
.coerce_to_object(activation);

let alpha_x = alpha_point
.get("x", activation)?
.coerce_to_i32(activation)?;

let alpha_y = alpha_point
.get("y", activation)?
.coerce_to_i32(activation)?;

let alpha_bitmap = args
.get(3)
.unwrap_or(&Value::Undefined)
.coerce_to_object(activation);

if let Some(alpha_bitmap) = alpha_bitmap.as_bitmap_data_object() {
if !alpha_bitmap.disposed() {
// dealing with aliasing the same way as for the source
let alpha_bitmap_clone: BitmapData;
let alpha_bitmap_data_cell = alpha_bitmap.bitmap_data();
let alpha_bitmap_gc_ref;
let alpha_bitmap_ref = if GcCell::ptr_eq(
alpha_bitmap.bitmap_data(),
bitmap_data.bitmap_data(),
) {
alpha_bitmap_clone = alpha_bitmap_data_cell.read().clone();
&alpha_bitmap_clone
} else {
alpha_bitmap_gc_ref = alpha_bitmap_data_cell.read();
&alpha_bitmap_gc_ref
};

let merge_alpha = if args.len() >= 6 {
args.get(5)
.unwrap_or(&Value::Undefined)
.as_bool(activation.swf_version())
} else {
true
};

bitmap_data
.bitmap_data()
.write(activation.context.gc_context)
.copy_pixels(
source_bitmap_ref,
(src_min_x, src_min_y, src_width, src_height),
(dest_x, dest_y),
Some((alpha_bitmap_ref, (alpha_x, alpha_y), merge_alpha)),
);
}
}
} else {
bitmap_data
.bitmap_data()
.write(activation.context.gc_context)
.copy_pixels(
source_bitmap_ref,
(src_min_x, src_min_y, src_width, src_height),
(dest_x, dest_y),
None,
);
}
}
}

return Ok(Value::Undefined);
}
}
Expand Down
95 changes: 95 additions & 0 deletions core/src/avm1/object/bitmap_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,16 @@ impl Color {
pub fn with_alpha(&self, alpha: u8) -> Color {
Color::argb(alpha, self.red(), self.green(), self.blue())
}

pub fn blend_over(&self, source: &Self) -> Self {
let sa = source.alpha();

let r = source.red() + ((self.red() as u16 * (255 - sa as u16)) >> 8) as u8;
let g = source.green() + ((self.green() as u16 * (255 - sa as u16)) >> 8) as u8;
let b = source.blue() + ((self.blue() as u16 * (255 - sa as u16)) >> 8) as u8;
let a = source.alpha() + ((self.alpha() as u16 * (255 - sa as u16)) >> 8) as u8;
Color::argb(a, r, g, b)
}
}

impl std::fmt::Display for Color {
Expand Down Expand Up @@ -482,6 +492,91 @@ impl BitmapData {
(x, y, w, h)
}

pub fn copy_pixels(
&mut self,
source_bitmap: &Self,
src_rect: (i32, i32, i32, i32),
dest_point: (i32, i32),
alpha_source: Option<(&Self, (i32, i32), bool)>,
) {
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 !source_bitmap.is_point_in_bounds(src_x, src_y)
|| !self.is_point_in_bounds(dest_x, dest_y)
{
continue;
}

let source_color = source_bitmap
.get_pixel_raw(src_x as u32, src_y as u32)
.unwrap();

let mut dest_color = self.get_pixel_raw(dest_x as u32, dest_y as u32).unwrap();

if let Some((alpha_bitmap, (alpha_min_x, alpha_min_y), merge_alpha)) = alpha_source
{
let alpha_x = src_x - src_min_x + alpha_min_x;
let alpha_y = src_y - src_min_y + alpha_min_y;

if alpha_bitmap.transparency
&& !alpha_bitmap.is_point_in_bounds(alpha_x, alpha_y)
{
continue;
}

let final_alpha = if alpha_bitmap.transparency {
let a = alpha_bitmap
.get_pixel_raw(alpha_x as u32, alpha_y as u32)
.unwrap()
.alpha();

if source_bitmap.transparency {
((a as u16 * source_color.alpha() as u16) >> 8) as u8
} else {
a
}
} else if source_bitmap.transparency {
source_color.alpha()
} else {
255
};

// there could be a faster or more accurate way to do this,
// (without converting to floats and back, twice),
// but for now this should suffice
let intermediate_color = source_color
.to_un_multiplied_alpha()
.with_alpha(final_alpha)
.to_premultiplied_alpha(true);

// there are some interesting conditions in the following
// lines, these are a result of comparing the output in
// many parameter combinations with that of Adobe's player,
// and finding patterns in the differences.
dest_color = if merge_alpha || !self.transparency {
dest_color.blend_over(&intermediate_color)
} else {
intermediate_color
};
} else {
dest_color = if source_bitmap.transparency && !self.transparency {
dest_color.blend_over(&source_color)
} else {
source_color
};
}

self.set_pixel32_raw(dest_x as u32, dest_y as u32, dest_color);
}
}
}

pub fn scroll(&mut self, x: i32, y: i32) {
let width = self.width() as i32;
let height = self.height() as i32;
Expand Down