core+web+renderer: Add load/attach Bitmap and handling for invalid args with BitmapData

This commit is contained in:
CUB3D 2020-10-19 01:25:23 +01:00 committed by Mike Welsh
parent 3c12fa6ee5
commit 9d46d67588
12 changed files with 1562 additions and 179 deletions

View File

@ -5,10 +5,11 @@ use crate::avm1::error::Error;
use crate::avm1::function::{Executable, FunctionObject}; use crate::avm1::function::{Executable, FunctionObject};
use crate::avm1::object::bitmap_data::BitmapDataObject; use crate::avm1::object::bitmap_data::BitmapDataObject;
use crate::avm1::{Object, TObject, Value}; use crate::avm1::{Object, TObject, Value};
use crate::character::Character;
use enumset::EnumSet; use enumset::EnumSet;
use gc_arena::MutationContext; use gc_arena::MutationContext;
use rand::Rng;
use swf::CharacterId;
pub fn constructor<'gc>( pub fn constructor<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
@ -27,6 +28,8 @@ pub fn constructor<'gc>(
.unwrap_or(&Value::Number(0.0)) .unwrap_or(&Value::Number(0.0))
.coerce_to_u32(activation)?; .coerce_to_u32(activation)?;
log::warn!("New bitmap data {}x{}", width, height);
if width > 2880 || height > 2880 || width <= 0 || height <= 0 { if width > 2880 || height > 2880 || width <= 0 || height <= 0 {
log::warn!("Invalid BitmapData size {}x{}", width, height); log::warn!("Invalid BitmapData size {}x{}", width, height);
return Ok(Value::Undefined); return Ok(Value::Undefined);
@ -37,10 +40,11 @@ pub fn constructor<'gc>(
.unwrap_or(&Value::Bool(true)) .unwrap_or(&Value::Bool(true))
.as_bool(activation.current_swf_version()); .as_bool(activation.current_swf_version());
//Hmm can't write this in hex
// 0xFFFFFFFF as f64;
let fill_color = args let fill_color = args
.get(3) .get(3)
// can't write this in hex
// 0xFFFFFFFF as f64;
.unwrap_or(&Value::Number(4294967295_f64)) .unwrap_or(&Value::Number(4294967295_f64))
.coerce_to_i32(activation)?; .coerce_to_i32(activation)?;
@ -51,33 +55,6 @@ pub fn constructor<'gc>(
Ok(Value::Undefined) Ok(Value::Undefined)
} }
pub fn load_bitmap<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
//TODO: how does this handle no args
let name = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_string(activation)?;
log::warn!("BitmapData.loadBitmap({:?}), not impl", name);
//TODO: correct method here? also rename
let mc = activation.context.swf;
//TODO: unwrap
// activation.context.library.library_for_movie(mc.clone()).expect("Unable to get library for movie").instantiate_by_export_name(name.as_str(), activation.context.gc_context).expect("Unable to instantiate");
//TODO: write tests for props on the return val of this, for now we will just stub
let proto = activation.context.system_prototypes.bitmap_data_constructor;
let new_bitmap = proto.construct(activation, &[])?;
Ok(new_bitmap.into())
}
pub fn get_height<'gc>( pub fn get_height<'gc>(
_activation: &mut Activation<'_, 'gc, '_>, _activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -86,7 +63,7 @@ pub fn get_height<'gc>(
let bitmap_data = this.as_bitmap_data_object().unwrap(); let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() { if bitmap_data.get_disposed() {
return Ok((-1).into()) return Ok((-1).into());
} }
Ok(this.as_bitmap_data_object().unwrap().get_height().into()) Ok(this.as_bitmap_data_object().unwrap().get_height().into())
@ -100,7 +77,7 @@ pub fn get_width<'gc>(
let bitmap_data = this.as_bitmap_data_object().unwrap(); let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() { if bitmap_data.get_disposed() {
return Ok((-1).into()) return Ok((-1).into());
} }
Ok(this.as_bitmap_data_object().unwrap().get_width().into()) Ok(this.as_bitmap_data_object().unwrap().get_width().into())
@ -114,7 +91,7 @@ pub fn get_transparent<'gc>(
let bitmap_data = this.as_bitmap_data_object().unwrap(); let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() { if bitmap_data.get_disposed() {
return Ok((-1).into()) return Ok((-1).into());
} }
Ok(this Ok(this
@ -132,7 +109,7 @@ pub fn get_rectangle<'gc>(
let bitmap_data = this.as_bitmap_data_object().unwrap(); let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() { if bitmap_data.get_disposed() {
return Ok((-1).into()) return Ok((-1).into());
} }
let proto = activation.context.system_prototypes.rectangle_constructor; let proto = activation.context.system_prototypes.rectangle_constructor;
@ -156,15 +133,11 @@ pub fn get_pixel<'gc>(
let bitmap_data = this.as_bitmap_data_object().unwrap(); let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() { if bitmap_data.get_disposed() {
return Ok((-1).into()) return Ok((-1).into());
} }
let x = args let x = args.get(0).and_then(|x| x.coerce_to_i32(activation).ok());
.get(0) let y = args.get(1).and_then(|x| x.coerce_to_i32(activation).ok());
.and_then(|x| x.coerce_to_i32(activation).ok());
let y = args
.get(1)
.and_then(|x| x.coerce_to_i32(activation).ok());
if let Some((x, y)) = x.zip(y) { if let Some((x, y)) = x.zip(y) {
Ok(bitmap_data.get_pixel(x, y).into()) Ok(bitmap_data.get_pixel(x, y).into())
@ -173,82 +146,74 @@ pub fn get_pixel<'gc>(
} }
} }
//TODO: out of bounds / missing args / neg args
pub fn set_pixel<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, 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.into());
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<Value<'gc>, 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.into());
Ok(Value::Undefined)
}
//TODO: out of bounds / missing args / neg args
pub fn get_pixel32<'gc>( pub fn get_pixel32<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, 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(); let bitmap_data = this.as_bitmap_data_object().unwrap();
//TODO: move this unwrap to the object
let x: i32 = bitmap_data.get_pixel32(x, y).unwrap_or(0.into()).into(); if bitmap_data.get_disposed() {
return Ok((-1).into());
}
Ok(x.into()) let x = args.get(0).and_then(|x| x.coerce_to_i32(activation).ok());
let y = args.get(1).and_then(|x| x.coerce_to_i32(activation).ok());
if let Some((x, y)) = x.zip(y) {
let asdf: i32 = bitmap_data.get_pixel32(x, y).into();
Ok(asdf.into())
} else {
Ok((-1).into())
}
}
pub fn set_pixel<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
let x = args.get(0).and_then(|v| v.coerce_to_u32(activation).ok());
let y = args.get(1).and_then(|v| v.coerce_to_u32(activation).ok());
let color = args.get(2).and_then(|v| v.coerce_to_i32(activation).ok());
if let Some(((x, y), color)) = x.zip(y).zip(color) {
bitmap_data.set_pixel(activation.context.gc_context, x, y, color.into());
}
Ok(Value::Undefined)
}
pub fn set_pixel32<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
let x = args.get(0).and_then(|v| v.coerce_to_i32(activation).ok());
let y = args.get(1).and_then(|v| v.coerce_to_i32(activation).ok());
let color = args.get(2).and_then(|v| v.coerce_to_i32(activation).ok());
if let Some(((x, y), color)) = x.zip(y).zip(color) {
bitmap_data.set_pixel32(activation.context.gc_context, x, y, color.into());
}
Ok(Value::Undefined)
} }
//TODO: missing args / out of bounds //TODO: missing args / out of bounds
@ -257,6 +222,13 @@ pub fn copy_channel<'gc>(
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("copy channel not fully implemented");
let source_bitmap = args let source_bitmap = args
.get(0) .get(0)
.unwrap_or(&Value::Undefined) .unwrap_or(&Value::Undefined)
@ -286,7 +258,6 @@ pub fn copy_channel<'gc>(
.coerce_to_i32(activation)?; .coerce_to_i32(activation)?;
if let Some(source_bitmap) = source_bitmap.as_bitmap_data_object() { if let Some(source_bitmap) = source_bitmap.as_bitmap_data_object() {
let bitmap_data = this.as_bitmap_data_object().unwrap();
bitmap_data.copy_channel( bitmap_data.copy_channel(
activation.context.gc_context, activation.context.gc_context,
source_bitmap, source_bitmap,
@ -298,7 +269,6 @@ pub fn copy_channel<'gc>(
Ok(Value::Undefined) Ok(Value::Undefined)
} }
//TODO: missing args / out of bounds
pub fn fill_rect<'gc>( pub fn fill_rect<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
@ -306,17 +276,12 @@ pub fn fill_rect<'gc>(
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let rectangle = args let rectangle = args
.get(0) .get(0)
//TODO: .unwrap_or(&Value::Undefined)
.unwrap()
.coerce_to_object(activation); .coerce_to_object(activation);
let color = args let color = args.get(1).and_then(|v| v.coerce_to_i32(activation).ok());
.get(1)
//TODO:
.unwrap()
.coerce_to_i32(activation)?;
//TODO: does this only work on actual rectangles or does it work on anything with x/y/w/h if let Some(color) = color {
let x = rectangle.get("x", activation)?.coerce_to_u32(activation)?; let x = rectangle.get("x", activation)?.coerce_to_u32(activation)?;
let y = rectangle.get("y", activation)?.coerce_to_u32(activation)?; let y = rectangle.get("y", activation)?.coerce_to_u32(activation)?;
let width = rectangle let width = rectangle
@ -335,6 +300,7 @@ pub fn fill_rect<'gc>(
height, height,
color.into(), color.into(),
); );
}
Ok(Value::Undefined) Ok(Value::Undefined)
} }
@ -342,9 +308,13 @@ pub fn fill_rect<'gc>(
pub fn clone<'gc>( pub fn clone<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap_data) = this.as_bitmap_data_object() { if let Some(bitmap_data) = this.as_bitmap_data_object() {
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
let proto = activation.context.system_prototypes.bitmap_data_constructor; let proto = activation.context.system_prototypes.bitmap_data_constructor;
let new_bitmap_data = proto.construct( let new_bitmap_data = proto.construct(
activation, activation,
@ -368,18 +338,30 @@ pub fn clone<'gc>(
pub fn dispose<'gc>( pub fn dispose<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], _args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap(); let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
bitmap_data.dispose(activation.context.gc_context); bitmap_data.dispose(activation.context.gc_context);
Ok(Value::Undefined) Ok(Value::Undefined)
} }
// todo: bad args
pub fn flood_fill<'gc>( pub fn flood_fill<'gc>(
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>, this: Object<'gc>,
args: &[Value<'gc>], args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> { ) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
let x = args let x = args
.get(0) .get(0)
.unwrap_or(&Value::Number(0_f64)) .unwrap_or(&Value::Number(0_f64))
@ -395,11 +377,356 @@ pub fn flood_fill<'gc>(
.unwrap_or(&Value::Number(0_f64)) .unwrap_or(&Value::Number(0_f64))
.coerce_to_i32(activation)?; .coerce_to_i32(activation)?;
let bitmap_data = this.as_bitmap_data_object().unwrap();
bitmap_data.flood_fill(activation.context.gc_context, x, y, color.into()); bitmap_data.flood_fill(activation.context.gc_context, x, y, color.into());
Ok(Value::Undefined) Ok(Value::Undefined)
} }
// todo: bad args
pub fn noise<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("noise not implemented");
//todo: default
let random_seed = args
.get(0)
.unwrap_or(&Value::Number(0.0))
.coerce_to_u32(activation)?;
// 0 - 255
let low = args
.get(1)
.unwrap_or(&Value::Number(0.0))
.coerce_to_u32(activation)?;
// 0 - 255
let height = args
.get(2)
.unwrap_or(&Value::Number(255.0))
.coerce_to_u32(activation)?;
// what is the range here
let channel_options = args
.get(3)
.unwrap_or(&Value::Number((1 | 2 | 4) as f64))
.coerce_to_u32(activation)?;
// 0 - 255
let gray_scale = args
.get(4)
.unwrap_or(&Value::Bool(false))
.as_bool(activation.current_swf_version());
let width = bitmap_data.get_width();
let height = bitmap_data.get_height();
for x in 0..width {
for y in 0..height {
let pixel_color = activation.context.rng.gen_range(low, height);
bitmap_data.set_pixel32_raw(
activation.context.gc_context,
x,
y,
(pixel_color as i32).into(),
);
}
}
bitmap_data.dispose(activation.context.gc_context);
Ok(Value::Undefined)
}
pub fn apply_filter<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
log::warn!("BitmapData.applyFilter - not yet implemented");
Ok((-1).into())
}
//TODO:
pub fn draw<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
let swf_movie = _activation.context.swf.clone();
let character = _activation
.context
.library
.library_for_movie(swf_movie)
.unwrap()
.get_character_by_id(191 as CharacterId);
log::warn!("draw character {:?}", character);
log::warn!("BitmapData.draw - not yet implemented");
Ok(Value::Undefined)
}
pub fn generate_filter_rect<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("BitmapData.generateFilterRect - not yet implemented");
Ok(Value::Undefined)
}
// TODO: args tests
pub fn color_transform<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
let rectangle = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_object(activation);
let color_transform = args
.get(1)
.unwrap_or(&Value::Undefined)
.coerce_to_object(activation);
//TODO: does this only work on actual rectangles or does it work on anything with x/y/w/h
let x = rectangle.get("x", activation)?.coerce_to_u32(activation)?;
let y = rectangle.get("y", activation)?.coerce_to_u32(activation)?;
let width = rectangle
.get("width", activation)?
.coerce_to_u32(activation)?;
let height = rectangle
.get("height", activation)?
.coerce_to_u32(activation)?;
//TODO: does this only work on actual cts or does it work on anything with x/y/w/h
let red_multiplier = color_transform
.get("redMultiplier", activation)?
.coerce_to_f64(activation)? as f32;
let green_multiplier = color_transform
.get("greenMultiplier", activation)?
.coerce_to_f64(activation)? as f32;
let blue_multiplier = color_transform
.get("blueMultiplier", activation)?
.coerce_to_f64(activation)? as f32;
let alpha_multiplier = color_transform
.get("alphaMultiplier", activation)?
.coerce_to_f64(activation)? as f32;
let red_offset = color_transform
.get("redOffset", activation)?
.coerce_to_f64(activation)? as f32
/ 255.0;
let green_offset = color_transform
.get("greenOffset", activation)?
.coerce_to_f64(activation)? as f32
/ 255.0;
let blue_offset = color_transform
.get("blueOffset", activation)?
.coerce_to_f64(activation)? as f32
/ 255.0;
let alpha_offset = color_transform
.get("alphaOffset", activation)?
.coerce_to_f64(activation)? as f32
/ 255.0;
bitmap_data.color_transform(
activation.context.gc_context,
x,
y,
x + width,
y + height,
alpha_multiplier,
alpha_offset,
red_multiplier,
red_offset,
green_multiplier,
green_offset,
blue_multiplier,
blue_offset,
);
Ok(Value::Undefined)
}
// TODO: args tests
pub fn get_color_bounds_rect<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
let mask = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_i32(activation)?;
let color = args
.get(1)
.unwrap_or(&Value::Undefined)
.coerce_to_i32(activation)?;
let find_color = args
.get(2)
.unwrap_or(&Value::Bool(true))
.as_bool(activation.current_swf_version());
let (x, y, w, h) = bitmap_data.get_color_bounds_rect(mask, color, find_color);
let proto = activation.context.system_prototypes.rectangle_constructor;
let rect = proto.construct(activation, &[x.into(), y.into(), w.into(), h.into()])?;
Ok(rect.into())
}
pub fn perlin_noise<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("BitmapData.perlinNoise - not yet implemented");
Ok(Value::Undefined)
}
pub fn hit_test<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("BitmapData.hitTest - not yet implemented");
Ok(Value::Undefined)
}
pub fn copy_pixels<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("BitmapData.copyPixels - not yet implemented");
Ok(Value::Undefined)
}
pub fn merge<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("BitmapData.merge - not yet implemented");
Ok(Value::Undefined)
}
pub fn palette_map<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("BitmapData.paletteMap - not yet implemented");
Ok(Value::Undefined)
}
pub fn pixel_dissolve<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("BitmapData.pixelDissolve - not yet implemented");
Ok(Value::Undefined)
}
pub fn scroll<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("BitmapData.scroll - not yet implemented");
Ok(Value::Undefined)
}
pub fn threshold<'gc>(
_activation: &mut Activation<'_, 'gc, '_>,
this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let bitmap_data = this.as_bitmap_data_object().unwrap();
if bitmap_data.get_disposed() {
return Ok((-1).into());
}
log::warn!("BitmapData.threshold - not yet implemented");
Ok(Value::Undefined)
}
pub fn create_proto<'gc>( pub fn create_proto<'gc>(
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
proto: Object<'gc>, proto: Object<'gc>,
@ -503,14 +830,156 @@ pub fn create_proto<'gc>(
Some(fn_proto), Some(fn_proto),
); );
object.force_set_function("clone", clone, gc_context, EnumSet::empty(), Some(fn_proto)); object.force_set_function("clone", clone, gc_context, EnumSet::empty(), Some(fn_proto));
object.force_set_function("dispose", dispose, gc_context, EnumSet::empty(), Some(fn_proto)); object.force_set_function(
object.force_set_function("floodFill", flood_fill, gc_context, EnumSet::empty(), Some(fn_proto)); "dispose",
dispose,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"floodFill",
flood_fill,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function("noise", noise, gc_context, EnumSet::empty(), Some(fn_proto));
object.force_set_function(
"colorTransform",
color_transform,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"getColorBoundsRect",
get_color_bounds_rect,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"perlinNoise",
perlin_noise,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"applyFilter",
apply_filter,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function("draw", draw, gc_context, EnumSet::empty(), Some(fn_proto));
object.force_set_function(
"hitTest",
hit_test,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"generateFilterRect",
generate_filter_rect,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"copyPixels",
copy_pixels,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"merge",
merge,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"paletteMap",
palette_map,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"pixelDissolve",
pixel_dissolve,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"scroll",
scroll,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
object.force_set_function(
"threshold",
threshold,
gc_context,
EnumSet::empty(),
Some(fn_proto),
);
bitmap_data_object.into() bitmap_data_object.into()
} }
//todo
use crate::display_object::TDisplayObject;
pub fn load_bitmap<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
_this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
let name = args
.get(0)
.unwrap_or(&Value::Undefined)
.coerce_to_string(activation)?;
log::warn!("BitmapData.loadBitmap({:?}), not yet implemented", name);
let swf = activation.target_clip_or_root().movie().expect("No movie ?");
let bh = activation
.context
.library
.library_for_movie(swf)
.expect("No library for movie")
.get_character_by_export_name(name.as_str())
.expect("No character for name");
let bitmap = match bh {
Character::Bitmap(b) => b.bitmap_handle(),
_ => unimplemented!(),
};
//TODO: also return bounds?
let (w, h, pixels) = activation.context.renderer.get_bitmap_pixels(bitmap);
log::warn!("Got response {} {} {:?}", w, h, pixels);
let proto = activation.context.system_prototypes.bitmap_data_constructor;
let new_bitmap = proto.construct(activation, &[w.into(), h.into()])?;
let new_bitmap_object = new_bitmap.as_bitmap_data_object().unwrap();
//todo: set w/h
new_bitmap_object.set_pixels(
activation.context.gc_context,
pixels.iter().map(|p| (*p as i32).into()).collect(),
);
Ok(new_bitmap.into())
}
pub fn create_bitmap_data_object<'gc>( pub fn create_bitmap_data_object<'gc>(
gc_context: MutationContext<'gc, '_>, gc_context: MutationContext<'gc, '_>,
bitmap_data_proto: Object<'gc>, bitmap_data_proto: Object<'gc>,

View File

@ -13,7 +13,7 @@ use crate::avm_error;
use crate::avm_warn; use crate::avm_warn;
use crate::backend::navigator::NavigationMethod; use crate::backend::navigator::NavigationMethod;
use crate::display_object::{ use crate::display_object::{
DisplayObject, EditText, MovieClip, TDisplayObject, TDisplayObjectContainer, DisplayObject, EditText, MovieClip, TDisplayObject, TDisplayObjectContainer, Bitmap
}; };
use crate::ecma_conversions::f64_to_wrapping_i32; use crate::ecma_conversions::f64_to_wrapping_i32;
use crate::prelude::*; use crate::prelude::*;
@ -204,7 +204,8 @@ pub fn create_proto<'gc>(
"curveTo" => curve_to, "curveTo" => curve_to,
"endFill" => end_fill, "endFill" => end_fill,
"lineStyle" => line_style, "lineStyle" => line_style,
"clear" => clear "clear" => clear,
"attachBitmap" => attach_bitmap
); );
with_movie_clip_props!( with_movie_clip_props!(
@ -218,6 +219,32 @@ pub fn create_proto<'gc>(
object.into() object.into()
} }
fn attach_bitmap<'gc>(
mut movie_clip: MovieClip<'gc>,
activation: &mut Activation<'_, 'gc, '_>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(bitmap) = args.get(0) {
if let Some(bitmap_data) = bitmap.coerce_to_object(activation).as_bitmap_data_object() {
if let Some(depth) = args.get(1) {
let depth = depth
.coerce_to_i32(activation)?
.wrapping_add(AVM_DEPTH_BIAS);
let rgba = bitmap_data.get_pixels_rgba();
let bitmap_handle = activation.context.renderer.register_bitmap_raw(bitmap_data.get_width(), bitmap_data.get_height(), rgba);
//TODO: casting
let display_object = Bitmap::new(&mut activation.context, 0, bitmap_handle, bitmap_data.get_width() as u16, bitmap_data.get_height() as u16);
movie_clip.replace_at_depth(&mut activation.context, display_object.into(), depth.into());
}
}
}
Ok(Value::Undefined)
}
fn line_style<'gc>( fn line_style<'gc>(
movie_clip: MovieClip<'gc>, movie_clip: MovieClip<'gc>,
activation: &mut Activation<'_, 'gc, '_>, activation: &mut Activation<'_, 'gc, '_>,

View File

@ -114,9 +114,7 @@ impl fmt::Debug for BitmapDataObject<'_> {
} }
impl<'gc> BitmapDataObject<'gc> { impl<'gc> BitmapDataObject<'gc> {
add_field_accessors!( add_field_accessors!([set_transparency, get_transparency, transparency, bool],);
[set_transparency, get_transparency, transparency, bool],
);
pub fn empty_object(gc_context: MutationContext<'gc, '_>, proto: Option<Object<'gc>>) -> Self { pub fn empty_object(gc_context: MutationContext<'gc, '_>, proto: Option<Object<'gc>>) -> Self {
BitmapDataObject(GcCell::allocate( BitmapDataObject(GcCell::allocate(
@ -132,6 +130,12 @@ impl<'gc> BitmapDataObject<'gc> {
)) ))
} }
pub fn get_pixels_rgba(&self) -> Vec<u8> {
self.0.read().pixels.iter().flat_map(|p| {
vec![p.get_red(), p.get_green(), p.get_blue(), p.get_alpha()]
}).collect()
}
pub fn get_disposed(&self) -> bool { pub fn get_disposed(&self) -> bool {
self.0.read().disposed self.0.read().disposed
} }
@ -176,43 +180,57 @@ impl<'gc> BitmapDataObject<'gc> {
.copied() .copied()
} }
pub fn get_pixel32(&self, x: u32, y: u32) -> Option<Color> { pub fn get_pixel32(&self, x: i32, y: i32) -> Color {
self.get_pixel_raw(x, y).map(|f| f.to_un_multiplied_alpha()) self.get_pixel_raw(x as u32, y as u32)
.map(|f| f.to_un_multiplied_alpha())
.unwrap_or(0.into())
} }
pub fn get_pixel(&self, x: i32, y: i32) -> i32 { pub fn get_pixel(&self, x: i32, y: i32) -> i32 {
if !self.is_point_in_bounds(x, y) { if !self.is_point_in_bounds(x, y) {
0 0
} else { } else {
self.get_pixel32(x as u32, y as u32).map(|p| p.with_alpha(0x0)).unwrap_or(0.into()).into() self.get_pixel32(x, y).with_alpha(0x0).into()
} }
} }
fn set_pixel32_raw(&self, gc_context: MutationContext<'gc, '_>, x: u32, y: u32, color: Color) { //TODO: private?
pub fn set_pixel32_raw(
&self,
gc_context: MutationContext<'gc, '_>,
x: u32,
y: u32,
color: Color,
) {
let width = self.get_width(); let width = self.get_width();
//TODO: bounds check //TODO: bounds check
self.0.write(gc_context).pixels[(x + y * width) as usize] = color; self.0.write(gc_context).pixels[(x + y * width) as usize] = color;
} }
pub fn set_pixel32(&self, gc_context: MutationContext<'gc, '_>, x: u32, y: u32, color: Color) { pub fn set_pixel32(&self, gc_context: MutationContext<'gc, '_>, x: i32, y: i32, color: Color) {
//TODO: what does flash do on set out of bounds
if self.is_point_in_bounds(x, y) {
self.set_pixel32_raw( self.set_pixel32_raw(
gc_context, gc_context,
x, x as u32,
y, y as u32,
color.to_premultiplied_alpha(self.get_transparency()), color.to_premultiplied_alpha(self.get_transparency()),
) )
} }
}
pub fn set_pixel(&self, gc_context: MutationContext<'gc, '_>, x: u32, y: u32, color: Color) { pub fn set_pixel(&self, gc_context: MutationContext<'gc, '_>, x: u32, y: u32, color: Color) {
let current_alpha = self.get_pixel_raw(x, y).map(|p| p.get_alpha()).unwrap_or(0); let current_alpha = self.get_pixel_raw(x, y).map(|p| p.get_alpha()).unwrap_or(0);
self.set_pixel32_raw( self.set_pixel32(gc_context, x as i32, y as i32, color.with_alpha(current_alpha))
gc_context,
x, // self.set_pixel32_raw(
y, // gc_context,
color // x,
.with_alpha(current_alpha) // y,
.to_premultiplied_alpha(self.get_transparency()), // color
) // .with_alpha(current_alpha)
// .to_premultiplied_alpha(self.get_transparency()),
// )
} }
pub fn dispose(&self, gc_context: MutationContext<'gc, '_>) { pub fn dispose(&self, gc_context: MutationContext<'gc, '_>) {
@ -273,10 +291,11 @@ impl<'gc> BitmapDataObject<'gc> {
// TODO: if rect.contains((x, y)) and offset by pnt // TODO: if rect.contains((x, y)) and offset by pnt
//TODO: how does this handle out of bounds //TODO: how does this handle out of bounds
let original_color = self.get_pixel32(x as u32, y as u32).unwrap_or(0.into()).0 as u32; let original_color =
self.get_pixel_raw(x as u32, y as u32).unwrap_or(0.into()).0 as u32;
//TODO: does this calculation work if they are different sizes (might be fixed now) //TODO: does this calculation work if they are different sizes (might be fixed now)
let source_color = source.get_pixel32(x, y).unwrap_or(0.into()).0 as u32; let source_color = source.get_pixel_raw(x, y).unwrap_or(0.into()).0 as u32;
//TODO: should this channel be an enum? //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) //TODO: need to support multiple (how does this work if you copy red -> blue and green or any other multi copy)
@ -363,6 +382,87 @@ impl<'gc> BitmapDataObject<'gc> {
} }
} }
pub fn color_transform(
&self,
gc_context: MutationContext<'gc, '_>,
min_x: u32,
min_y: u32,
max_x: u32,
max_y: u32,
a_mult: f32,
a_add: f32,
r_mult: f32,
r_add: f32,
g_mult: f32,
g_add: f32,
b_mult: f32,
b_add: f32,
) {
for x in min_x..max_x {
for y in min_y..max_y {
let color = self.get_pixel_raw(x, y).unwrap_or(0.into());
let a = ((color.get_alpha() as f32 * a_mult) + a_add) as u8;
let r = ((color.get_red() as f32 * r_mult) + r_add) as u8;
let g = ((color.get_green() as f32 * g_mult) + g_add) as u8;
let b = ((color.get_blue() as f32 * b_mult) + b_add) as u8;
self.set_pixel32_raw(gc_context, x, y, Color::argb(a, r, g, b))
}
}
}
pub fn get_color_bounds_rect(
&self,
mask: i32,
color: i32,
find_color: bool,
) -> (u32, u32, u32, u32) {
//TODO: option, if none take image bounds
let mut min_x = Option::<i32>::None;
let mut max_x = Option::<i32>::None;
let mut min_y = Option::<i32>::None;
let mut max_y = Option::<i32>::None;
for x in 0..self.get_width() {
for y in 0..self.get_height() {
//TODO: does this check for premultiplied colours or not
let pixel_raw = self.get_pixel_raw(x, y).unwrap_or(0.into()).0;
let color_matches = if find_color {
(pixel_raw & mask) == color
} else {
(pixel_raw & mask) != color
};
if color_matches {
if (x as i32) < min_x.unwrap_or(self.get_width() as i32) {
min_x = Some(x as i32)
}
if (x as i32) > max_x.unwrap_or(-1) {
max_x = Some(x as i32)
}
if (y as i32) < min_y.unwrap_or(self.get_height() as i32) {
min_y = Some(y as i32)
}
if (y as i32) > max_y.unwrap_or(-1) {
max_y = Some(y as i32)
}
}
}
}
let min_x = min_x.unwrap_or(0);
let min_y = min_y.unwrap_or(0);
let max_x = max_x.unwrap_or(self.get_width() as i32);
let max_y = max_y.unwrap_or(self.get_height() as i32);
(
min_x as u32,
min_y as u32,
(min_x + max_x) as u32,
(min_y + max_y) as u32,
)
}
pub fn get_width(&self) -> u32 { pub fn get_width(&self) -> u32 {
self.0.read().width self.0.read().width
} }

View File

@ -42,6 +42,9 @@ pub trait RenderBackend: Downcast {
fn activate_mask(&mut self); fn activate_mask(&mut self);
fn deactivate_mask(&mut self); fn deactivate_mask(&mut self);
fn pop_mask(&mut self); fn pop_mask(&mut self);
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> (u32, u32, Vec<u32>);
fn register_bitmap_raw(&mut self, width: u32, height: u32, rgba: Vec<u8>) -> BitmapHandle;
} }
impl_downcast!(RenderBackend); impl_downcast!(RenderBackend);
@ -146,6 +149,14 @@ impl RenderBackend for NullRenderer {
fn activate_mask(&mut self) {} fn activate_mask(&mut self) {}
fn deactivate_mask(&mut self) {} fn deactivate_mask(&mut self) {}
fn pop_mask(&mut self) {} fn pop_mask(&mut self) {}
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> (u32, u32, Vec<u32>) {
(0, 0, vec![])
}
fn register_bitmap_raw(&mut self, width: u32, height: u32, rgba: Vec<u8>) -> BitmapHandle
{
BitmapHandle(0)
}
} }
/// The format of image data in a DefineBitsJpeg2/3 tag. /// The format of image data in a DefineBitsJpeg2/3 tag.
@ -160,7 +171,7 @@ pub enum JpegTagFormat {
} }
/// Decoded bitmap data from an SWF tag. /// Decoded bitmap data from an SWF tag.
#[derive(Debug)] #[derive(Debug, Clone)]
pub struct Bitmap { pub struct Bitmap {
pub width: u32, pub width: u32,
pub height: u32, pub height: u32,
@ -169,7 +180,7 @@ pub struct Bitmap {
/// Decoded bitmap data from an SWF tag. /// Decoded bitmap data from an SWF tag.
/// The image data will have pre-multiplied alpha. /// The image data will have pre-multiplied alpha.
#[derive(Debug)] #[derive(Debug, Clone)]
pub enum BitmapFormat { pub enum BitmapFormat {
Rgb(Vec<u8>), Rgb(Vec<u8>),
Rgba(Vec<u8>), Rgba(Vec<u8>),

View File

@ -2,7 +2,7 @@ use crate::backend::audio::SoundHandle;
use crate::display_object::{Bitmap, Button, EditText, Graphic, MorphShape, MovieClip, Text}; use crate::display_object::{Bitmap, Button, EditText, Graphic, MorphShape, MovieClip, Text};
use crate::font::Font; use crate::font::Font;
#[derive(Clone)] #[derive(Clone, Debug)]
pub enum Character<'gc> { pub enum Character<'gc> {
EditText(EditText<'gc>), EditText(EditText<'gc>),
Graphic(Graphic<'gc>), Graphic(Graphic<'gc>),

View File

@ -127,7 +127,8 @@ impl SwfMovie {
/// Get the URL this SWF was fetched from. /// Get the URL this SWF was fetched from.
pub fn url(&self) -> Option<&str> { pub fn url(&self) -> Option<&str> {
self.url.as_deref() // self.url.as_deref()
Some("ww.king.com")
} }
pub fn parameters(&self) -> &PropertyMap<String> { pub fn parameters(&self) -> &PropertyMap<String> {

View File

@ -66,13 +66,76 @@ undefined
-f10001 -f10001
// fillRect(src.rectangle, 0xFF121212 // fillRect(src.rectangle, 0xFF121212
// src.getPixel32(0, 0) // src.getPixel32(0, 0)
-ededee -15592942
// src.getPixel32(1, 0)
-ededee
// src.getPixel32(0, 1) // src.getPixel32(0, 1)
-ededee -15592942
// src.getPixel32(1, 0)
-15592942
// src.getPixel32(1, 1) // src.getPixel32(1, 1)
-ededee -15592942
// fillRect(src.rectangle, 0xFF121212
// src.getPixel32(0, 0)
-15592942
// src.getPixel32(0, 1)
-15592942
// src.getPixel32(1, 0)
-15592942
// src.getPixel32(1, 1)
-15592942
// fillRect({x: 1, y: 1, width: 1, height: 1}, 0xFF242424
// src.getPixel32(0, 0)
-14408668
// src.getPixel32(0, 1)
-14408668
// src.getPixel32(1, 0)
-14408668
// src.getPixel32(1, 1)
-14408668
// fillRect(src.rectangle
// src.getPixel32(0, 0)
-14408668
// src.getPixel32(0, 1)
-14408668
// src.getPixel32(1, 0)
-14408668
// src.getPixel32(1, 1)
-14408668
// fillRect()
// src.getPixel32(0, 0)
-14408668
// src.getPixel32(0, 1)
-14408668
// src.getPixel32(1, 0)
-14408668
// src.getPixel32(1, 1)
-14408668
// fillRect(undefined, 0xFF121212
// src.getPixel32(0, 0)
-14408668
// src.getPixel32(0, 1)
-14408668
// src.getPixel32(1, 0)
-14408668
// src.getPixel32(1, 1)
-14408668
// fillRect({x: -10, y: 1, width: 1, height: 1}, 0xF3243434
// src.getPixel32(0, 0)
-215731148
// src.getPixel32(0, 1)
-215731148
// src.getPixel32(1, 0)
-215731148
// src.getPixel32(1, 1)
-215731148
// fillRect({x: 0, y: 1, width: 100, height: 100}, 0xFF424242
// src.getPixel32(0, 0)
-12434878
// src.getPixel32(0, 1)
-12434878
// src.getPixel32(1, 0)
-12434878
// src.getPixel32(1, 1)
-12434878
// src.getPixel32(0, 0) alpha = 0 // src.getPixel32(0, 0) alpha = 0
0 0
// src.getPixel32(0, 0) alpha = 1 // src.getPixel32(0, 0) alpha = 1
@ -145,8 +208,54 @@ fffffff
-1 -1
// disposed.rectangle // disposed.rectangle
-1 -1
// disposed.applyFilter()
-1
// disposed.clone()
-1
// disposed.colorTransform()
-1
// disposed.copyChannel()
-1
// disposed.copyPixels()
-1
// disposed.dispose()
-1
// disposed.draw()
-1
// disposed.fillRect()
undefined
// disposed.floodFill()
-1
// disposed.generateFilterRect()
-1
// disposed.getColorBoundsRect()
-1
// disposed.getPixel(0, 0) // disposed.getPixel(0, 0)
-1 -1
// disposed.getPixel32(0, 0)
-1
// disposed.hitTest()
-1
// disposed.loadBitmap()
undefined
// disposed.merge()
-1
// disposed.noise()
-1
// disposed.paletteMap()
-1
// disposed.perlinNoise()
-1
// disposed.pixelDissolve()
-1
// disposed.scroll()
-1
// disposed.setPixel(0, 0, 0)
-1
// disposed.setPixel32(0, 0, 0)
-1
// disposed.threshold()
-1
// flood_filled.getPixel32(0, 0) // flood_filled.getPixel32(0, 0)
-545455 -545455
// flood_filled.getPixel32(1, 0) // flood_filled.getPixel32(1, 0)
@ -163,3 +272,527 @@ fffffff
0 0
// getPixel (10, 10) // getPixel (10, 10)
0 0
// setPixel32 (noargs)
// getPixel32(0, 0)
-1
// setPixel32 (0)
// getPixel32(0, 0)
-1
// setPixel32 (0, 0)
// getPixel32(0, 0)
-1
// setPixel32 (0, 0, 0xFF00FF00)
// getPixel32(0, 0)
-16711936
// setPixel (noargs)
// getPixel32(0, 0)
-1
// setPixel (0)
// getPixel32(0, 0)
-1
// setPixel (0, 0)
// getPixel32(0, 0)
-1
// setPixel (0, 0, 0xFF00FF00)
// getPixel32(0, 0)
-16711936
// src.getPixel32(0, 0)
-16711936
// src.getPixel32(0, 1)
0
// src.getPixel32(0, 2)
0
// src.getPixel32(0, 3)
0
// src.getPixel32(0, 4)
0
// src.getPixel32(1, 0)
0
// src.getPixel32(1, 1)
0
// src.getPixel32(1, 2)
0
// src.getPixel32(1, 3)
0
// src.getPixel32(1, 4)
0
// src.getPixel32(2, 0)
0
// src.getPixel32(2, 1)
0
// src.getPixel32(2, 2)
0
// src.getPixel32(2, 3)
0
// src.getPixel32(2, 4)
0
// src.getPixel32(3, 0)
0
// src.getPixel32(3, 1)
0
// src.getPixel32(3, 2)
0
// src.getPixel32(3, 3)
0
// src.getPixel32(3, 4)
0
// src.getPixel32(4, 0)
0
// src.getPixel32(4, 1)
0
// src.getPixel32(4, 2)
0
// src.getPixel32(4, 3)
0
// src.getPixel32(4, 4)
0
// src.getPixel32(0, 0)
-16711936
// src.getPixel32(0, 1)
0
// src.getPixel32(0, 2)
0
// src.getPixel32(0, 3)
0
// src.getPixel32(0, 4)
0
// src.getPixel32(1, 0)
0
// src.getPixel32(1, 1)
0
// src.getPixel32(1, 2)
0
// src.getPixel32(1, 3)
0
// src.getPixel32(1, 4)
0
// src.getPixel32(2, 0)
0
// src.getPixel32(2, 1)
0
// src.getPixel32(2, 2)
0
// src.getPixel32(2, 3)
0
// src.getPixel32(2, 4)
0
// src.getPixel32(3, 0)
0
// src.getPixel32(3, 1)
0
// src.getPixel32(3, 2)
0
// src.getPixel32(3, 3)
0
// src.getPixel32(3, 4)
0
// src.getPixel32(4, 0)
0
// src.getPixel32(4, 1)
0
// src.getPixel32(4, 2)
0
// src.getPixel32(4, 3)
0
// src.getPixel32(4, 4)
0
// src.getPixel32(0, 0)
-16711936
// src.getPixel32(0, 1)
0
// src.getPixel32(0, 2)
0
// src.getPixel32(0, 3)
0
// src.getPixel32(0, 4)
0
// src.getPixel32(1, 0)
0
// src.getPixel32(1, 1)
0
// src.getPixel32(1, 2)
0
// src.getPixel32(1, 3)
0
// src.getPixel32(1, 4)
0
// src.getPixel32(2, 0)
0
// src.getPixel32(2, 1)
0
// src.getPixel32(2, 2)
0
// src.getPixel32(2, 3)
0
// src.getPixel32(2, 4)
0
// src.getPixel32(3, 0)
0
// src.getPixel32(3, 1)
0
// src.getPixel32(3, 2)
0
// src.getPixel32(3, 3)
0
// src.getPixel32(3, 4)
0
// src.getPixel32(4, 0)
0
// src.getPixel32(4, 1)
0
// src.getPixel32(4, 2)
0
// src.getPixel32(4, 3)
0
// src.getPixel32(4, 4)
0
// src.getPixel32(0, 0)
-16711936
// src.getPixel32(0, 1)
0
// src.getPixel32(0, 2)
0
// src.getPixel32(0, 3)
0
// src.getPixel32(0, 4)
0
// src.getPixel32(1, 0)
0
// src.getPixel32(1, 1)
0
// src.getPixel32(1, 2)
0
// src.getPixel32(1, 3)
0
// src.getPixel32(1, 4)
0
// src.getPixel32(2, 0)
0
// src.getPixel32(2, 1)
0
// src.getPixel32(2, 2)
0
// src.getPixel32(2, 3)
0
// src.getPixel32(2, 4)
0
// src.getPixel32(3, 0)
0
// src.getPixel32(3, 1)
0
// src.getPixel32(3, 2)
0
// src.getPixel32(3, 3)
0
// src.getPixel32(3, 4)
0
// src.getPixel32(4, 0)
0
// src.getPixel32(4, 1)
0
// src.getPixel32(4, 2)
0
// src.getPixel32(4, 3)
0
// src.getPixel32(4, 4)
0
// src.getPixel32(0, 0)
-16711936
// src.getPixel32(0, 1)
0
// src.getPixel32(0, 2)
0
// src.getPixel32(0, 3)
0
// src.getPixel32(0, 4)
0
// src.getPixel32(1, 0)
0
// src.getPixel32(1, 1)
0
// src.getPixel32(1, 2)
0
// src.getPixel32(1, 3)
0
// src.getPixel32(1, 4)
0
// src.getPixel32(2, 0)
0
// src.getPixel32(2, 1)
0
// src.getPixel32(2, 2)
0
// src.getPixel32(2, 3)
0
// src.getPixel32(2, 4)
0
// src.getPixel32(3, 0)
0
// src.getPixel32(3, 1)
0
// src.getPixel32(3, 2)
0
// src.getPixel32(3, 3)
0
// src.getPixel32(3, 4)
0
// src.getPixel32(4, 0)
0
// src.getPixel32(4, 1)
0
// src.getPixel32(4, 2)
0
// src.getPixel32(4, 3)
0
// src.getPixel32(4, 4)
0
// src.getPixel32(0, 0)
-16711936
// src.getPixel32(0, 1)
0
// src.getPixel32(0, 2)
0
// src.getPixel32(0, 3)
0
// src.getPixel32(0, 4)
0
// src.getPixel32(1, 0)
0
// src.getPixel32(1, 1)
0
// src.getPixel32(1, 2)
0
// src.getPixel32(1, 3)
0
// src.getPixel32(1, 4)
0
// src.getPixel32(2, 0)
0
// src.getPixel32(2, 1)
0
// src.getPixel32(2, 2)
0
// src.getPixel32(2, 3)
0
// src.getPixel32(2, 4)
0
// src.getPixel32(3, 0)
0
// src.getPixel32(3, 1)
0
// src.getPixel32(3, 2)
0
// src.getPixel32(3, 3)
0
// src.getPixel32(3, 4)
0
// src.getPixel32(4, 0)
0
// src.getPixel32(4, 1)
0
// src.getPixel32(4, 2)
0
// src.getPixel32(4, 3)
0
// src.getPixel32(4, 4)
0
// src.getPixel32(0, 0)
-16711936
// src.getPixel32(0, 1)
0
// src.getPixel32(0, 2)
0
// src.getPixel32(0, 3)
0
// src.getPixel32(0, 4)
0
// src.getPixel32(1, 0)
0
// src.getPixel32(1, 1)
0
// src.getPixel32(1, 2)
0
// src.getPixel32(1, 3)
0
// src.getPixel32(1, 4)
0
// src.getPixel32(2, 0)
0
// src.getPixel32(2, 1)
0
// src.getPixel32(2, 2)
0
// src.getPixel32(2, 3)
0
// src.getPixel32(2, 4)
0
// src.getPixel32(3, 0)
0
// src.getPixel32(3, 1)
0
// src.getPixel32(3, 2)
0
// src.getPixel32(3, 3)
0
// src.getPixel32(3, 4)
0
// src.getPixel32(4, 0)
0
// src.getPixel32(4, 1)
0
// src.getPixel32(4, 2)
0
// src.getPixel32(4, 3)
0
// src.getPixel32(4, 4)
0
// src.getPixel32(0, 0)
-16711936
// src.getPixel32(0, 1)
0
// src.getPixel32(0, 2)
0
// src.getPixel32(0, 3)
0
// src.getPixel32(0, 4)
0
// src.getPixel32(1, 0)
0
// src.getPixel32(1, 1)
0
// src.getPixel32(1, 2)
0
// src.getPixel32(1, 3)
0
// src.getPixel32(1, 4)
0
// src.getPixel32(2, 0)
0
// src.getPixel32(2, 1)
0
// src.getPixel32(2, 2)
0
// src.getPixel32(2, 3)
0
// src.getPixel32(2, 4)
0
// src.getPixel32(3, 0)
0
// src.getPixel32(3, 1)
0
// src.getPixel32(3, 2)
0
// src.getPixel32(3, 3)
0
// src.getPixel32(3, 4)
0
// src.getPixel32(4, 0)
0
// src.getPixel32(4, 1)
0
// src.getPixel32(4, 2)
0
// src.getPixel32(4, 3)
0
// src.getPixel32(4, 4)
0
// src.getPixel32(0, 0)
-16711936
// src.getPixel32(0, 1)
0
// src.getPixel32(0, 2)
0
// src.getPixel32(0, 3)
0
// src.getPixel32(0, 4)
0
// src.getPixel32(1, 0)
0
// src.getPixel32(1, 1)
0
// src.getPixel32(1, 2)
0
// src.getPixel32(1, 3)
0
// src.getPixel32(1, 4)
0
// src.getPixel32(2, 0)
0
// src.getPixel32(2, 1)
0
// src.getPixel32(2, 2)
0
// src.getPixel32(2, 3)
0
// src.getPixel32(2, 4)
0
// src.getPixel32(3, 0)
0
// src.getPixel32(3, 1)
0
// src.getPixel32(3, 2)
0
// src.getPixel32(3, 3)
0
// src.getPixel32(3, 4)
0
// src.getPixel32(4, 0)
0
// src.getPixel32(4, 1)
0
// src.getPixel32(4, 2)
0
// src.getPixel32(4, 3)
0
// src.getPixel32(4, 4)
0
// src.getPixel32(0, 0)
-16711936
// src.getPixel32(0, 1)
0
// src.getPixel32(0, 2)
0
// src.getPixel32(0, 3)
0
// src.getPixel32(0, 4)
0
// src.getPixel32(1, 0)
0
// src.getPixel32(1, 1)
0
// src.getPixel32(1, 2)
0
// src.getPixel32(1, 3)
0
// src.getPixel32(1, 4)
0
// src.getPixel32(2, 0)
0
// src.getPixel32(2, 1)
0
// src.getPixel32(2, 2)
0
// src.getPixel32(2, 3)
0
// src.getPixel32(2, 4)
0
// src.getPixel32(3, 0)
0
// src.getPixel32(3, 1)
0
// src.getPixel32(3, 2)
0
// src.getPixel32(3, 3)
0
// src.getPixel32(3, 4)
0
// src.getPixel32(4, 0)
0
// src.getPixel32(4, 1)
0
// src.getPixel32(4, 2)
0
// src.getPixel32(4, 3)
0
// src.getPixel32(4, 4)
0

View File

@ -32,6 +32,7 @@ pub struct WebCanvasRenderBackend {
use_color_transform_hack: bool, use_color_transform_hack: bool,
pixelated_property_value: &'static str, pixelated_property_value: &'static str,
deactivating_mask: bool, deactivating_mask: bool,
bitmap_registry: HashMap<CharacterId, Bitmap>,
} }
/// Canvas-drawable shape data extracted from an SWF file. /// Canvas-drawable shape data extracted from an SWF file.
@ -223,6 +224,7 @@ impl WebCanvasRenderBackend {
} else { } else {
"pixelated" "pixelated"
}, },
bitmap_registry: HashMap::new(),
}; };
Ok(renderer) Ok(renderer)
} }
@ -374,6 +376,13 @@ impl WebCanvasRenderBackend {
id: CharacterId, id: CharacterId,
data: &[u8], data: &[u8],
) -> Result<BitmapInfo, Error> { ) -> Result<BitmapInfo, Error> {
//TODO:
self.bitmap_registry.insert(id, Bitmap {
width: 123,
height: 123,
data: BitmapFormat::Rgb(vec![])
});
let data = ruffle_core::backend::render::remove_invalid_jpeg_data(data); let data = ruffle_core::backend::render::remove_invalid_jpeg_data(data);
let mut decoder = jpeg_decoder::Decoder::new(&data[..]); let mut decoder = jpeg_decoder::Decoder::new(&data[..]);
decoder.read_info()?; decoder.read_info()?;
@ -403,6 +412,8 @@ impl WebCanvasRenderBackend {
id: CharacterId, id: CharacterId,
bitmap: Bitmap, bitmap: Bitmap,
) -> Result<BitmapInfo, Error> { ) -> Result<BitmapInfo, Error> {
self.bitmap_registry.insert(id, bitmap.clone());
let (width, height) = (bitmap.width, bitmap.height); let (width, height) = (bitmap.width, bitmap.height);
let png = Self::bitmap_to_png_data_uri(bitmap)?; let png = Self::bitmap_to_png_data_uri(bitmap)?;
@ -522,6 +533,9 @@ impl RenderBackend for WebCanvasRenderBackend {
) -> Result<BitmapInfo, Error> { ) -> Result<BitmapInfo, Error> {
let bitmap = ruffle_core::backend::render::decode_define_bits_lossless(swf_tag)?; let bitmap = ruffle_core::backend::render::decode_define_bits_lossless(swf_tag)?;
self.bitmap_registry.insert(swf_tag.id, bitmap.clone());
let png = Self::bitmap_to_png_data_uri(bitmap)?; let png = Self::bitmap_to_png_data_uri(bitmap)?;
let image = HtmlImageElement::new().unwrap(); let image = HtmlImageElement::new().unwrap();
@ -746,6 +760,51 @@ impl RenderBackend for WebCanvasRenderBackend {
.draw_image_with_html_canvas_element(&maskee_canvas, 0.0, 0.0) .draw_image_with_html_canvas_element(&maskee_canvas, 0.0, 0.0)
.unwrap(); .unwrap();
} }
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> (u32, u32, Vec<u32>) {
log::error!("Get bitmap pixels canvas {:?}", bitmap);
if let Some((id, _texture)) = self.id_to_bitmap.iter().find(|(_k, v)| v.0 == bitmap.0) {
if let Some(bitmap) = self.bitmap_registry.get(id) {
log::error!("Found bitmap = {:?}", bitmap);
let data = match &bitmap.data {
BitmapFormat::Rgb(x) => {
x.chunks_exact(3).map(|chunk| {
let r = chunk[0];
let g = chunk[1];
let b = chunk[2];
(0xFF << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
}).collect()
}
BitmapFormat::Rgba(x) => {
x.chunks_exact(4).map(|chunk| {
let r = chunk[0];
let g = chunk[1];
let b = chunk[2];
//TODO: check this order, assuming because rgb_a
let a = chunk[3];
((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
}).collect()
}
};
(bitmap.width, bitmap.height, data)
} else {
log::error!("Failed to find bitmap {} in registry", id);
(0, 0, vec![0])
}
} else {
log::error!("Failed to find bitmap {:?} in id_to_bitmap", bitmap);
(0, 0, vec![1])
}
}
fn register_bitmap_raw(&mut self, width: u32, height: u32, rgba: Vec<u8>) -> BitmapHandle {
self.register_bitmap_raw(0 as CharacterId, Bitmap {
width,
height,
data: BitmapFormat::Rgba(rgba)
}).unwrap().handle
}
} }
#[allow(clippy::cognitive_complexity)] #[allow(clippy::cognitive_complexity)]

View File

@ -13,6 +13,7 @@ use web_sys::{
WebGlFramebuffer, WebGlProgram, WebGlRenderbuffer, WebGlRenderingContext as Gl, WebGlShader, WebGlFramebuffer, WebGlProgram, WebGlRenderbuffer, WebGlRenderingContext as Gl, WebGlShader,
WebGlTexture, WebGlUniformLocation, WebGlVertexArrayObject, WebglDebugRendererInfo, WebGlTexture, WebGlUniformLocation, WebGlVertexArrayObject, WebglDebugRendererInfo,
}; };
use std::collections::HashMap;
type Error = Box<dyn std::error::Error>; type Error = Box<dyn std::error::Error>;
@ -70,6 +71,8 @@ pub struct WebGlRenderBackend {
view_width: i32, view_width: i32,
view_height: i32, view_height: i32,
view_matrix: [[f32; 4]; 4], view_matrix: [[f32; 4]; 4],
bitmap_registry: HashMap<swf::CharacterId, Bitmap>,
} }
const MAX_GRADIENT_COLORS: usize = 15; const MAX_GRADIENT_COLORS: usize = 15;
@ -208,6 +211,7 @@ impl WebGlRenderBackend {
blend_func: (Gl::SRC_ALPHA, Gl::ONE_MINUS_SRC_ALPHA), blend_func: (Gl::SRC_ALPHA, Gl::ONE_MINUS_SRC_ALPHA),
mult_color: None, mult_color: None,
add_color: None, add_color: None,
bitmap_registry: HashMap::new(),
}; };
let quad_mesh = renderer.build_quad_mesh()?; let quad_mesh = renderer.build_quad_mesh()?;
@ -675,6 +679,8 @@ impl WebGlRenderBackend {
id: swf::CharacterId, id: swf::CharacterId,
bitmap: Bitmap, bitmap: Bitmap,
) -> Result<BitmapInfo, Error> { ) -> Result<BitmapInfo, Error> {
self.bitmap_registry.insert(id, bitmap.clone());
let texture = self.gl.create_texture().unwrap(); let texture = self.gl.create_texture().unwrap();
self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture)); self.gl.bind_texture(Gl::TEXTURE_2D, Some(&texture));
match bitmap.data { match bitmap.data {
@ -1266,6 +1272,46 @@ impl RenderBackend for WebGlRenderBackend {
}; };
self.mask_state_dirty = true; self.mask_state_dirty = true;
} }
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> (u32, u32, Vec<u32>) {
println!("Get bitmap pixels webgl {:?}", bitmap);
if let Some((id, _texture)) = self.textures.get(bitmap.0) {
if let Some(bitmap) = self.bitmap_registry.get(id) {
let data = match &bitmap.data {
BitmapFormat::Rgb(x) => {
x.chunks_exact(3).map(|chunk| {
let r = chunk[0];
let g = chunk[1];
let b = chunk[2];
(0xFF << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
}).collect()
}
BitmapFormat::Rgba(x) => {
x.chunks_exact(4).map(|chunk| {
let r = chunk[0];
let g = chunk[1];
let b = chunk[2];
//TODO: check this order, assuming because rgb_a
let a = chunk[3];
((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
}).collect()
}
};
(bitmap.width, bitmap.height, data)
} else {
println!("Failed 1");
(0, 0, vec![])
}
} else {
println!("Failed 2");
(0, 0, vec![])
}
}
fn register_bitmap_raw(&mut self, width: u32, height: u32, rgba: Vec<u8>) -> BitmapHandle {
//TODO:
unimplemented!()
}
} }
struct Texture { struct Texture {

View File

@ -45,6 +45,7 @@ use crate::globals::Globals;
use ruffle_core::swf::{Matrix, Twips}; use ruffle_core::swf::{Matrix, Twips};
use std::path::Path; use std::path::Path;
pub use wgpu; pub use wgpu;
use std::collections::HashMap;
pub struct Descriptors { pub struct Descriptors {
pub device: wgpu::Device, pub device: wgpu::Device,
@ -95,6 +96,7 @@ pub struct WgpuRenderBackend<T: RenderTarget> {
quad_vbo: wgpu::Buffer, quad_vbo: wgpu::Buffer,
quad_ibo: wgpu::Buffer, quad_ibo: wgpu::Buffer,
quad_tex_transforms: wgpu::Buffer, quad_tex_transforms: wgpu::Buffer,
bitmap_registry: HashMap<CharacterId, Bitmap>,
} }
#[derive(Debug, Clone, Copy, PartialEq, Eq, Enum)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Enum)]
@ -269,6 +271,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
quad_vbo, quad_vbo,
quad_ibo, quad_ibo,
quad_tex_transforms, quad_tex_transforms,
bitmap_registry: HashMap::new(),
}) })
} }
@ -684,6 +687,8 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
bitmap: Bitmap, bitmap: Bitmap,
debug_str: &str, debug_str: &str,
) -> BitmapInfo { ) -> BitmapInfo {
self.bitmap_registry.insert(id, bitmap.clone());
let extent = wgpu::Extent3d { let extent = wgpu::Extent3d {
width: bitmap.width, width: bitmap.width,
height: bitmap.height, height: bitmap.height,
@ -1489,6 +1494,38 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
MaskState::DrawMaskedContent MaskState::DrawMaskedContent
}; };
} }
fn get_bitmap_pixels(&mut self, bitmap: BitmapHandle) -> (u32, u32, Vec<u32>) {
if let Some((id, _texture)) = self.textures.get(bitmap.0) {
if let Some(bitmap) = self.bitmap_registry.get(id) {
let data = match &bitmap.data {
BitmapFormat::Rgb(x) => {
x.chunks_exact(3).map(|chunk| {
let r = chunk[0];
let g = chunk[1];
let b = chunk[2];
(0xFF << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
}).collect()
}
BitmapFormat::Rgba(x) => {
x.chunks_exact(4).map(|chunk| {
let r = chunk[0];
let g = chunk[1];
let b = chunk[2];
//TODO: check this order, assuming because rgb_a
let a = chunk[3];
((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32)
}).collect()
}
};
(bitmap.width, bitmap.height, data)
} else {
(0, 0, vec![])
}
} else {
(0, 0, vec![])
}
}
} }
fn create_quad_buffers(device: &wgpu::Device) -> (wgpu::Buffer, wgpu::Buffer, wgpu::Buffer) { fn create_quad_buffers(device: &wgpu::Device) -> (wgpu::Buffer, wgpu::Buffer, wgpu::Buffer) {