From 3afd20063f4890a0a5f2e871bc9514ebf148f425 Mon Sep 17 00:00:00 2001 From: CUB3D Date: Tue, 6 Oct 2020 00:53:43 +0100 Subject: [PATCH] core: Implement get/set pixel(32) functions --- core/src/avm1/globals.rs | 2 +- core/src/avm1/globals/bitmap_data.rs | 255 ++++++++++++++++++-- core/src/avm1/object/bitmap_data.rs | 122 +++++++++- core/tests/regression_tests.rs | 1 + core/tests/swfs/avm1/bitmap_data/output.txt | 63 +++++ core/tests/swfs/avm1/bitmap_data/test.fla | Bin 4613 -> 5376 bytes core/tests/swfs/avm1/bitmap_data/test.swf | Bin 654 -> 1317 bytes 7 files changed, 416 insertions(+), 27 deletions(-) create mode 100644 core/tests/swfs/avm1/bitmap_data/output.txt diff --git a/core/src/avm1/globals.rs b/core/src/avm1/globals.rs index 7e2c9382d..bb7928cdb 100644 --- a/core/src/avm1/globals.rs +++ b/core/src/avm1/globals.rs @@ -589,7 +589,7 @@ pub fn create_globals<'gc>( EnumSet::empty(), ); - let bitmap_data_proto = bitmap_data::create_proto(gc_context, object_proto, Some(function_proto)); + let bitmap_data_proto = bitmap_data::create_proto(gc_context, object_proto, function_proto); let bitmap_data = bitmap_data::create_bitmap_data_object(gc_context, bitmap_data_proto, Some(function_proto)); display.define_value( diff --git a/core/src/avm1/globals/bitmap_data.rs b/core/src/avm1/globals/bitmap_data.rs index a6de6207c..1f395e9fa 100644 --- a/core/src/avm1/globals/bitmap_data.rs +++ b/core/src/avm1/globals/bitmap_data.rs @@ -2,11 +2,10 @@ use crate::avm1::activation::Activation; use crate::avm1::error::Error; -use crate::avm1::{Object, ScriptObject, TObject, Value}; +use crate::avm1::{Object, TObject, Value}; use enumset::EnumSet; use gc_arena::MutationContext; use crate::avm1::function::{FunctionObject, Executable}; -use crate::display_object::TDisplayObject; use crate::avm1::object::bitmap_data::BitmapDataObject; pub fn constructor<'gc>( @@ -14,7 +13,9 @@ pub fn constructor<'gc>( this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { - //TODO: check default for this and height + + //TODO: if either width or height is missing then the constructor should fail + let width = args.get(0) .unwrap_or(&Value::Number(0.0)) .coerce_to_u32(activation)?; @@ -23,7 +24,12 @@ pub fn constructor<'gc>( .unwrap_or(&Value::Number(0.0)) .coerce_to_u32(activation)?; - let transparent = args.get(2) + if width > 2880 || height > 2880 || width <= 0 || height <= 0{ + log::warn!("Invalid BitmapData size {}x{}", width, height); + return Ok(Value::Undefined) + } + + let transparency = args.get(2) .unwrap_or(&Value::Bool(true)) .as_bool(activation.current_swf_version()); @@ -33,25 +39,20 @@ pub fn constructor<'gc>( // 0xFFFFFFFF as f64; let fill_color = args.get(3) .unwrap_or(&Value::Number(4294967295_f64)) - .coerce_to_u32(activation)?; - - //TODO: respect size limits - - log::warn!("BitmapData constructor w: {}, h: {}, t: {}, fc:{}", width, height, transparent, fill_color); + .coerce_to_i32(activation)?; //TODO: respect transparency (can we maybe use less memory when disabled?) let bitmap_data = this.as_bitmap_data_object().unwrap(); bitmap_data.init_pixels(activation.context.gc_context, width, height, fill_color); - - + bitmap_data.set_transparency(activation.context.gc_context, transparency); Ok(Value::Undefined) } pub fn load_bitmap<'gc>( activation: &mut Activation<'_, 'gc, '_>, - this: Object<'gc>, + _this: Object<'gc>, args: &[Value<'gc>], ) -> Result, Error<'gc>> { //TODO: how does this handle no args @@ -75,14 +76,238 @@ pub fn load_bitmap<'gc>( Ok(new_bitmap.into()) } +pub fn get_height<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + Ok(this.as_bitmap_data_object().unwrap().get_height().into()) +} + +pub fn get_width<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + Ok(this.as_bitmap_data_object().unwrap().get_width().into()) +} + +pub fn get_transparent<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + Ok(this.as_bitmap_data_object().unwrap().get_transparency().into()) +} + +pub fn get_rectangle<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let bitmap_data = this.as_bitmap_data_object().unwrap(); + + let proto = activation.context.system_prototypes.rectangle_constructor; + let rect = proto.construct(activation, &[0.into(), 0.into(), bitmap_data.get_width().into(), bitmap_data.get_height().into()])?; + Ok(rect.into()) +} + +//TODO: out of bounds / missing args / neg args +pub fn get_pixel<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let x = args.get(0) + .unwrap_or(&Value::Number(0_f64)) + .coerce_to_u32(activation)?; + + let y = args.get(1) + .unwrap_or(&Value::Number(0_f64)) + .coerce_to_u32(activation)?; + + let bitmap_data = this.as_bitmap_data_object().unwrap(); + //TODO: move this unwrap to the object + Ok(bitmap_data.get_pixel(x, y).unwrap_or(0).into()) +} + +//TODO: out of bounds / missing args / neg args +pub fn set_pixel<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let x = args.get(0) + .unwrap_or(&Value::Number(0_f64)) + .coerce_to_u32(activation)?; + + let y = args.get(1) + .unwrap_or(&Value::Number(0_f64)) + .coerce_to_u32(activation)?; + + let color = args.get(2) + .unwrap_or(&Value::Number(0_f64)) + .coerce_to_i32(activation)?; + + let bitmap_data = this.as_bitmap_data_object().unwrap(); + bitmap_data.set_pixel(activation.context.gc_context, x, y, color); + + Ok(Value::Undefined) +} + +//TODO: out of bounds / missing args / neg args +pub fn set_pixel32<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let x = args.get(0) + .unwrap_or(&Value::Number(0_f64)) + .coerce_to_u32(activation)?; + + let y = args.get(1) + .unwrap_or(&Value::Number(0_f64)) + .coerce_to_u32(activation)?; + + let color = args.get(2) + .unwrap_or(&Value::Number(0_f64)) + .coerce_to_i32(activation)?; + + let bitmap_data = this.as_bitmap_data_object().unwrap(); + bitmap_data.set_pixel32(activation.context.gc_context, x, y, color); + + Ok(Value::Undefined) +} + +//TODO: out of bounds / missing args / neg args +pub fn get_pixel32<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let x = args.get(0) + .unwrap_or(&Value::Number(0_f64)) + .coerce_to_u32(activation)?; + + let y = args.get(1) + .unwrap_or(&Value::Number(0_f64)) + .coerce_to_u32(activation)?; + + let bitmap_data = this.as_bitmap_data_object().unwrap(); + //TODO: move this unwrap to the object + + let x = bitmap_data.get_pixel32(x, y).unwrap_or(0); + + + Ok(x.into()) +} + +//TODO: missing args / out of bounds +pub fn copy_channel<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let source_bitmap = args.get(0) + .unwrap_or(&Value::Undefined) + //TODO: unwrap + .coerce_to_object(activation); + + let source_rect = args.get(1) + .unwrap_or(&Value::Undefined) + //TODO: unwrap + .coerce_to_object(activation); + + let dest_point = args.get(2) + .unwrap_or(&Value::Undefined) + //TODO: unwrap + .coerce_to_object(activation); + + let source_channel = args.get(3) + .unwrap_or(&Value::Undefined) + .coerce_to_i32(activation)?; + + let dest_channel = args.get(4) + .unwrap_or(&Value::Undefined) + .coerce_to_i32(activation)?; + + if let Some(source_bitmap) = source_bitmap.as_bitmap_data_object() { + let bitmap_data = this.as_bitmap_data_object().unwrap(); + bitmap_data.copy_channel(activation.context.gc_context, source_bitmap, source_channel as u8, dest_channel as u8); + } + + Ok(Value::Undefined) +} + pub fn create_proto<'gc>( gc_context: MutationContext<'gc, '_>, proto: Object<'gc>, - fn_proto: Option>, + fn_proto: Object<'gc>, ) -> Object<'gc> { - let mut object = BitmapDataObject::empty_object(gc_context, Some(proto)); + let bitmap_data_object = BitmapDataObject::empty_object(gc_context, Some(proto)); + let mut object = bitmap_data_object.as_script_object().unwrap(); - object.into() + object.add_property( + gc_context, + "height", + FunctionObject::function( + gc_context, + Executable::Native(get_height), + Some(fn_proto), + fn_proto, + ), + None, + EnumSet::empty(), + ); + + object.add_property( + gc_context, + "width", + FunctionObject::function( + gc_context, + Executable::Native(get_width), + Some(fn_proto), + fn_proto, + ), + None, + EnumSet::empty(), + ); + + object.add_property( + gc_context, + "transparent", + FunctionObject::function( + gc_context, + Executable::Native(get_transparent), + Some(fn_proto), + fn_proto, + ), + None, + EnumSet::empty(), + ); + + object.add_property( + gc_context, + "rectangle", + FunctionObject::function( + gc_context, + Executable::Native(get_rectangle), + Some(fn_proto), + fn_proto, + ), + None, + EnumSet::empty(), + ); + + object.force_set_function("getPixel", get_pixel, gc_context, EnumSet::empty(), Some(fn_proto)); + object.force_set_function("getPixel32", get_pixel32, gc_context, EnumSet::empty(), Some(fn_proto)); + object.force_set_function("setPixel", set_pixel, gc_context, EnumSet::empty(), Some(fn_proto)); + object.force_set_function("setPixel32", set_pixel32, gc_context, EnumSet::empty(), Some(fn_proto)); + object.force_set_function("copyChannel", copy_channel, gc_context, EnumSet::empty(), Some(fn_proto)); + + + + bitmap_data_object.into() } pub fn create_bitmap_data_object<'gc>( diff --git a/core/src/avm1/object/bitmap_data.rs b/core/src/avm1/object/bitmap_data.rs index 3add1a59c..844a5c350 100644 --- a/core/src/avm1/object/bitmap_data.rs +++ b/core/src/avm1/object/bitmap_data.rs @@ -6,6 +6,7 @@ use gc_arena::{Collect, GcCell, MutationContext}; use crate::avm1::activation::Activation; use std::fmt; +use swf::Rectangle; /// A BitmapData #[derive(Clone, Copy, Collect)] @@ -19,9 +20,11 @@ pub struct BitmapDataData<'gc> { base: ScriptObject<'gc>, /// The pixels in the bitmap, stored as a array of ARGB colour values - pixels: Vec, + pixels: Vec, - //todO: track width and height + width: u32, + height: u32, + transparency: bool, } impl fmt::Debug for BitmapDataObject<'_> { @@ -29,50 +32,140 @@ impl fmt::Debug for BitmapDataObject<'_> { let this = self.0.read(); f.debug_struct("BitmapData") .field("pixels", &this.pixels) + .field("width", &this.width) + .field("height", &this.height) + .field("transparency", &this.transparency) .finish() } } impl<'gc> BitmapDataObject<'gc> { + add_field_accessors!( + [set_transparency, get_transparency, transparency, bool], + ); + pub fn empty_object(gc_context: MutationContext<'gc, '_>, proto: Option>) -> Self { BitmapDataObject(GcCell::allocate( gc_context, BitmapDataData { base: ScriptObject::object(gc_context, proto), pixels: Vec::new(), + width: 0, + height: 0, + transparency: true, }, )) } - pub fn init_pixels(&self, gc_context: MutationContext<'gc, '_>, width: u32, height: u32, fill_color: u32) { + pub fn get_pixels(&self) -> Vec { + self.0.read().pixels.clone() + } + + pub fn init_pixels(&self, gc_context: MutationContext<'gc, '_>, width: u32, height: u32, fill_color: i32) { + self.0.write(gc_context).width = width; + self.0.write(gc_context).height = height; self.0.write(gc_context).pixels = vec![fill_color; (width * height) as usize] } - pub fn get_pixel32(&self, x: u32, y: u32) -> Option { + pub fn get_pixel32(&self, x: u32, y: u32) -> Option { //TODO: look into the effects of pre-multiplication self.0.read().pixels.get((x * y) as usize).cloned() } - pub fn get_pixel(&self, x: u32, y: u32) -> Option { + pub fn get_pixel(&self, x: u32, y: u32) -> Option { self.get_pixel32(x, y).map(|p| p & 0xFFFFFF) } - pub fn set_pixel32(&self, gc_context: MutationContext<'gc, '_>, x: u32, y: u32, color: u32) { + pub fn set_pixel32(&self, gc_context: MutationContext<'gc, '_>, x: u32, y: u32, color: i32) { + let alpha = (color >> 24) & 0xFF; + + // Internally flash uses pre-multiplied values however that will cause issues with accuracy (assuming they are using fixed point like with other color types) + // so we will just fake it for now, if the alpha is 0 then it will clear out the colors + //tODO: how well does this handle less edge case values eg 0x12345678? + let adjusted_color = if alpha == 0 && self.0.read().transparency { + // Alpha = 0 and transparency is on so clear out rest of color + 0 + } else { + // If we don't have transparency then force the alpha to 0xFF + //TODO: could we do that earlier to make this cleaner + if self.0.read().transparency { + color + } else { + (color & 0xFFFFFF) | (0xFF << 24) + } + }; + //TODO: bounds check - self.0.write(gc_context).pixels[(x * y) as usize] = color + self.0.write(gc_context).pixels[(x * y) as usize] = adjusted_color; } - pub fn set_pixel(&self, gc_context: MutationContext<'gc, '_>, x: u32, y: u32, color: u32) { - let current_alpha = self.get_pixel32(x, y).unwrap_or(0); + pub fn set_pixel(&self, gc_context: MutationContext<'gc, '_>, x: u32, y: u32, color: i32) { + let current_alpha = (self.get_pixel32(x, y).unwrap_or(0) >> 24) & 0xFF; //todo: check this shift self.set_pixel32(gc_context, x, y, color | (current_alpha << 24)) } pub fn dispose(&self, gc_context: MutationContext<'gc, '_>) { - self.0.write(gc_context).pixels.clear() + self.0.write(gc_context).pixels.clear(); + self.0.write(gc_context).width = 0; + self.0.write(gc_context).height = 0; + } + + pub fn copy_channel(&self, gc_context: MutationContext<'gc, '_>, source: BitmapDataObject, /*rect: Rectangle, pnt: Point,*/ source_channel: u8, dest_channel: u8) { + let other_pixels = source.get_pixels(); + + let mut new_self_pixels = Vec::new();//(self.0.read().pixels.len()); + let width = self.get_width(); + let height = self.get_height(); + + for x in 0..width { + for y in 0..height { + // TODO: if rect.contains((x, y)) and offset by pnt + + //TODO: how does this handle out of bounds + let original_color = self.0.read().pixels[(y * width + x) as usize]; + + //TODO: does this calculation work if they are different sizes + let source_color = other_pixels[(y * width + x) as usize]; + + //TODO: should this channel be an enum? + //TODO: need to support multiple (how does this work if you copy red -> blue and green or any other multi copy) + let channel_shift = match source_channel { + 8 => 24, + 4 => 16, + 2 => 8, + 1 => 0, + //TODO: + _ => {panic!()} + }; + + let source_part = (source_color >> channel_shift) & 0xFF; + + let dest_channel_shift = match dest_channel { + 8 => 24, + 4 => 16, + 2 => 8, + 1 => 0, + //TODO: + _ => {panic!()} + }; + let original_color = if dest_channel_shift == 0 { + original_color & (4278255615_u32 as i32)//& 0xFF00FFFF + } else { + original_color + }; + + let new_dest_color = original_color | ((source_part << dest_channel_shift) & 0xFF); + + //new_self_pixels.insert((y * width + x) as usize, new_dest_color); + new_self_pixels.push(new_dest_color); + } + } + + self.0.write(gc_context).pixels = new_self_pixels; } //TODO: probably wont handle the edge cases correctly, also may have differences if we dont use premultiplied alpha in our impl (wonder if premultipliing only for functions that need it would be benificial in any way) - pub fn threshold(&self, mask: u32, threshold: u32, new_color: u32, copy_source: bool) -> Vec { + pub fn threshold(&self, mask: i32, threshold: i32, new_color: i32, copy_source: bool) -> Vec { self.0.read().pixels.iter().cloned().map(|p| { //TODO: support other operations if (p & mask) == (threshold & mask) { @@ -87,6 +180,13 @@ impl<'gc> BitmapDataObject<'gc> { }) .collect() } + + pub fn get_width(&self) -> u32 { + self.0.read().width + } + pub fn get_height(&self) -> u32 { + self.0.read().height + } } impl<'gc> TObject<'gc> for BitmapDataObject<'gc> { diff --git a/core/tests/regression_tests.rs b/core/tests/regression_tests.rs index a45074952..3b29758bc 100644 --- a/core/tests/regression_tests.rs +++ b/core/tests/regression_tests.rs @@ -289,6 +289,7 @@ swf_tests! { (date_set_year, "avm1/date/setYear", 1), (this_scoping, "avm1/this_scoping", 1), (bevel_filter, "avm1/bevel_filter", 1), + (bitmap_data, "avm1/bitmap_data", 1), (as3_hello_world, "avm2/hello_world", 1), (as3_function_call, "avm2/function_call", 1), (as3_function_call_via_call, "avm2/function_call_via_call", 1), diff --git a/core/tests/swfs/avm1/bitmap_data/output.txt b/core/tests/swfs/avm1/bitmap_data/output.txt new file mode 100644 index 000000000..cf0007e85 --- /dev/null +++ b/core/tests/swfs/avm1/bitmap_data/output.txt @@ -0,0 +1,63 @@ +// bitmap +[object Object] +// bitmap.width +10 +// bitmap.height +10 +// bitmap.rectangle +(x=0, y=0, w=10, h=10) +// bitmap.transparent +false +// bitmap.width (after set to 100) +10 +// bitmap.height (after set to 100) +10 +// get/set pixel with transparent = true +// getPixel32(0, 0) after setPixel32(0, 0, 0xffffffff) +-1 +// getPixel(0, 0) +16777215 +// getPixel32(0, 0) after setPixel32(0, 0, 0x80ffffff) +-2130706433 +// getPixel(0, 0) +16777215 +// getPixel32(0, 0) after setPixel(0, 0, 0xBBBBBB) +-2135180357 +// getPixel(0, 0) +12303291 +// getPixel32(0, 0) after setPixel32(0, 0, 0x00ffffff) +0 +// getPixel(0, 0) +0 +// get/set pixel with transparent = false +// getPixel32(0, 0) after setPixel32(0, 0, 0xffffffff) +-1 +// getPixel(0, 0) +16777215 +// getPixel32(0, 0) after setPixel32(0, 0, 0x80ffffff) +-1 +// getPixel(0, 0) +16777215 +// getPixel32(0, 0) after setPixel(0, 0, 0xBBBBBB) +-4473925 +// getPixel(0, 0) +12303291 +// getPixel32(0, 0) after setPixel32(0, 0, 0x00ffffff) +-1 +// getPixel(0, 0) +16777215 +// bitmap_clone +[object Object] +// bitmap_clone == bitmap +false +// clone2 +undefined +// dest.copyChannel(src, src.rectangle, pnt, 1, 1) +// dest.getPixel32(0, 0) +ffffff +// dest.getPixel32(1, 0) +ffff +// dest.getPixel32(0, 1) +ffff +// dest.getPixel32(1, 1) +effff diff --git a/core/tests/swfs/avm1/bitmap_data/test.fla b/core/tests/swfs/avm1/bitmap_data/test.fla index 043baa742691a075417e4ff06aef21823bb4eb79..186b9b6e677f1c90d66985d9354052975d8aaf48 100644 GIT binary patch delta 2323 zcmV+u3GDWTB!DWAihpA$mylEi008zH000jF002Z!O+;^Fb!}yCbS`*pZ0%X?Z<{z6 z|6Zy8gOB@?PC39p^U{S$-KuDlwriwRcPCBLGmG$e_u zS-QoIGK?6>J|zKhwR=GW<`JSBu*gV&sjx&5_^=#lAdL_UNEW-=pJC#Y9o4`O$tBTC z^aJ}~bpUmm!haM%OEh=1@ve|6^bxihco=xQw6@*qOG z#1Zxw_6tb|zZ@hGF?PAobS6&+;Pt|LS2H#64?cV$KKE=owkBsssqqx)^s+EzFM~*2 zftdaB^~1yc)BV>lPD#c z^_5|O6@Q|k$CJuuPqy`VuL~(7KDh%40VrEj1Nb;V+lWyC`GGR9%GrR+TW|-CBMqRb z-wuKNo%hQSnERl(#E2|I9v*07*c_!Dg;Jn0(s!ZHHZ4;rRsuFSTyIz#A>~R)##;s@ z>j<||l}cx1$+9q64{nqkT#(QdiEeDO)RZ#!>VGi700oRUI>ihy!gFcf7%nz6#e(gE8VnB1g0uj{7a4XE0-6W^X~ zrzGkjq^=u%#GVUdV)hH8!vBOYDkt9O*F_t)kVdB=L4 zf3NfJb^bNOs86QvUPSl~#VuD$`PY+GOn=?7HG&j(1AyyxEq6~1q<6x%Tw#g%pD*!) zFyeWm#S(v>f78>9Fv1QgmOkq!O{0TyAuZWz!%IP#jB35W_W|kQ!v-Y@j-($n^V)YC zYY434!+u^|FSxq=d+L*OAqzNk7+@RFCxx+Vjp>Jo{pps3zzI}T)GcMVc z&y34=@-rM^ajHd)=g%9A^1$Kj>3@ZB_V^+v$&XDGXD^20@FfS7$2ZkUkI_2nnJsw! z_zh@@8RLmO#xqN|t0<(Kk0E6wJ2c4`Qum4B7!-EP@LzH6W}D*FN{YnrTr@J_)G@iH zQas?*c3m9l7%ydp)=0%{k4L?kZzpQzTL}9ykJZf1blR8M>|*Yl4F`5GkAElqm@TO6 z4V$OAcW*dKW@j?#nP+pX?hR++I6av=dc&ERc28#5;qz$R*JraM&m*U=&z;P0+?RRU z!EE;H=jl}4&d%+)Uoxg$9y@lwTudgnYGQkI+ml#*H*%oelX%?G77&j55!>qG+0)T8 zb7W?;o{ag!w){ueyZ-=COBw?N6aWGU2mr^bL{a4Ua@WZM006HL000#Lli&~+f9+Vy za@sHy-SZV1XW814{32+uGdw3*xXmQYv|AyI;E7+gM1cHS7yYFELS^#^29orVMW%zs zF!uGm=b&?Rg~#oSMNZj0mtf5AqSVuVe*d9?DD<>1PJn{E$EVTdW5I79KaJSqJ!^&9 zh2Ll|$Rf!TE-6?faVp5d(`cBDe>qX-Wf*FpN|E=T)+uj*ufa9wWd#S0Vd)HE3|!a1 zw&S|^BQOy*AyS8{W1BcGF`HmLG{2#~+eJ7bH~rz7PF;E0RLYz{IG@jrxn*R<9mK6x ztAsI4U1jKEnM%6QQ*pKe+2HiKU`3S6C`&A@74u67|)_O1ok;@!~M!* zpuDd>zKKkvW27T{WMZNY3w=PCAhZLr%G|>Ulr15Zw2tD8Op{nmVmZ4gwcLH2EQ}`0 z-$HrH+{h32^#5C5LbkOff1ed`<$lN@k9opVDU>z1ZcWI@Br6gseVXTS#AwL^AJTB` z=_ICY_>}b4zgNi-1TU|oB!R~&g&aJ{mn+3+nR9rNSZQ! z)%R7vh@y~m+$M6YVb?KHbI`@O+w7oD%f{V7%fW8Xs(h@M9>Df9e>N*cU&Dq9wl{S9 zR^RS4hsZ5q&n?_K?{sb3?hPHM>uz9oN)KQ^7m?bJX#5ZGM<5PpE+e6OZt2&QuiU-m z-|ErLjX0{@&S@cdsl2Ccq}L7#%9_eSLRqzbSk<3l8`h%FmqmxBy*~Ye(&zE#Q=Y!G zr|e`s4L~W#0QMS_kq|Q+=NIQ33z}&k_It008^CVS)ev delta 1570 zcmV+-2Hp99DupDFihsT@=U#&X000aI000jF002Z!O+;^Fb!}yCbS`*pY?V}7PunmQ zey`O3aOS-*iN#4Mtgr;BqpF4kqf*c|3Gv`0PV3FFBio_ve*NrR3aHSrNRjMw&i9?W zqv_4{RAzNag@JX+g+5zr7Neyx)fvXC)yi8Pc~UKK==%Zwd4GMCWJ_9-k4$JoL`G4I z+9HJZNG?X5#15ar>QwT!OhqPuq zhtb#uQj%X%ZGS@vY$T2HTV1A9#i+3nbZwH13VNB_JXpb;CTs%P+*HOQ_1qq>q^x38 z5U%NtZcY|dzowiiQqoD*+E>GIHnP)C;drl?Cp=v_GsP5YfB4i|OQca>vR_Dg%WZA}?p! zwa)v(Oi8LKF=RM+JbPS{yuBVEqVvN2=)>pXU^u`SQleRAi)yz=)tQ}jCp9vcXCNdH zwa^%HT7Qt58|@$uL_ylHL2O5G1&^K%0{;-)-^|J(`0JwkT5tFF=Mny&+#~$|^@?ow z-P@t@zq^Y2w(gw~ymv?%l4dwxm~i~Uh&OMvB_M{FROCe5M?YrtbJ_nw2!(PQME7a5@sOoA!9kJ5qm`{yqWMBzaD z)(gxa8?kwG^^~)_r!N!w^g#QecJ6iD0vAb^FiAj>#Hql=KqDcaGOYfWVW@#BMLrC) zVfg}l4{pGS=M40Wu1-yafa4g*?l}(n41X*WSVw7HZShH$xD8q2Q1^LvQ;{eB5!S-Q&5#VVDg&{J`;2HE2HOwc^aWW-ZYK9eb5 z$bqJHz+T(*E6H|5sW7TO8k8rnAQ?o433tdPxXQ+(oDs?M3Foo5?)f+-Vh(O{K7V6E zC{7ZC(VYWyFkM7(2<$WD!g^&JDD&0rS*B&`$ka`HVj--quKCGC*fjS*)|oYoK-m%! zN!m~>jO=}sgWNg}52M-YuTY*dxAMc9{_piAq&r)(MIKk~hZM4yB`lRfS%VI(2`QfO zJR#B}Sr$i>lq~Qu4L6?7V&cN*q<@e8UL{8mygrhW1h!QQdHE<`uN0G2#^5aze34Uj z`KS~)sgKJbz}KmeB&F=y_f)`$q7aY9j^Fj|;py0PE-w%|>!SYI@WQt3(YV*Ua4H`U zOB=90r;Aeb4Q%)Z*uByQ?AJU}>k*0n0lo#IL30%eHRo2|f%28RckIVubbtFIjw-h^ zk_%QUKhU<)YmI`krg9JyT8$rC&Ck$<2ho?yqW75AD18}!IcMo>Ysy~M%OHZ!ZR--H;rvHLp1Yn-*Vk{n5nBuA1X$&utp z@?Vj3+}(MYrRos8RHxlPP_z077zqT6F;WVX77`~30RRO6000&MHnT<&JOK*6FXvu^ z0ssIE29t~vGaS?S$_$?Z001Ko000#L000000000000000ECZ9<6F~z01(O~WKLXhf zlkgE1Dggo%02BZK2mk=bszgzXF;WT~0000S0000J00000000000000000|G1AQBam Uv=k}=l@F8P6dDH94*&oF0Kucvw*UYD diff --git a/core/tests/swfs/avm1/bitmap_data/test.swf b/core/tests/swfs/avm1/bitmap_data/test.swf index dd797b78fe67bffccde31ee607fa8cb670318df1..8f79d2e66757f0b183ffb359d53f1d88b0c91273 100644 GIT binary patch literal 1317 zcmV+=1={*US5prQ6952sob6W2QX5AUZNT`E#0EbSI|(rzj4{MW^UwpN0%AQ-LZz^Y zj5k??YGzu}R7TTNGmRy($u^5@k_DAz{vr!6@(cL{{=oTy$o&|N1~d}FE~m2al+-=< z_Bp3--@el`Z7{h9z|(c;$pKeZ10p5Jo}~8hpLuW)d*K2C3PE9WFm@AlC!(2 zn%DKbo{N%CU;{cat=i#8+HkbLd+1`N zihXk68JNNE1qHLco!@eNiW~!Pl{c6pX<25zkSeA()s@Xmku^n4E3OpOLN2WpH**;+ zTS|%CpQLux%EUO}c?;CcbErkAU9~5kRkv8l#;BGm*>b9!F0544ELUAkX}Q%xF`Z7A zDw#|%+f|K`2xG;b^&FcOk2J+DxXAM{&(!){H&r}MBENYnCQaEDHIu3oGy0%~^-;TO z=Ibn|kJ?ouZ<3~g5-{ADUY_DhS_uxp4G2o(ola+#osaX{{Ii|;s`jn& zbv0!BIH;LD!zK;}vJLlUFV=?Z>)rYiwhn5R%_8!Hs;meLxE`T|vI7M0SHvM#3E;kG z02BKZxU5#{m^8r-v1HkBH#@A|!%_n|4$B9_E<@JzN^%)8+A`GGZwW-8A^mi|;s&%q z1FDdXXRv{zac~(NO@d2f?4ySpV>7%Rf6Utn(O#(D=H2Ac6>zcBd07D9xWgQifI9%V z{|Inu^q$FGh?9G|#=DCPLT*NMmjZ*gmmdq`O2qY^biKbI_2tzY{y^wntqRb!2yaUA zrlaaSER)fn^v!rZI%5nhh~maVY#zoqVr(5$*jc4`i6laQCq_LJfQTw;^stQ-xKY;Xzz;lz7$@NY$^O8 z_RJrThCTe9xA>+4OQYmX^1cwfhw_O%7IT7;HupiexePFSEQrUa!DGWnK#-1SL*SV+ zG3O0-MU@B7z`EsyJ}A%5Ob;l(2_A?YpA5zq#YjR*B!$FuRAPkOE(SHp+u(yk87>rw bbBKuAFET)Eh=?{=zEAAe9|KT$3&8pp z0Pe2<=k5jo;N88A0su9ReY@kh${-9PVawKTv702Z4Pn3E*ZO6R@e^oNt5v9*&@@%4 zP{lAx&_IpEX0fj11cwNZ(>S3lQnEHWW@)lpEKab4FrHx}p&`tUiRQ5o4p0mYO@|pr zitH9AC}I4Fv7p|_REirQ(N$h?<`W?#9tBG4bwyE?hH$DDNr(7XtHseUCa^;UOSwm+d~>p4+HLzF5($bta^Tcuhej#39i!ru z9jo!g)hl~V!`LYs)tyGuvaFW7z1^(j*2+c oaBKX^>Xnxl8d<$M?pix5fZHqbFN@!^`c`KPJd