core: Store data in `BitmapData` instead of `Bitmap`
This makes `Bitmap` delegate to `BitmapData` for all of the bitmap-related information (handle, width, and height). As a result, we now unconditionally store a `BitmapData` in `Bitmap`. As a result, swapping the underling `BitmapData` instance will automatically change the properties (and rendered image) of a `Bitmap`. This required some refactoring in the render backends in order to get access to a `BitmapHandle` through `BitmapData`.
This commit is contained in:
parent
41df7fdc59
commit
10491a1be9
|
@ -253,10 +253,6 @@ fn attach_bitmap<'gc>(
|
|||
.coerce_to_i32(activation)?
|
||||
.wrapping_add(AVM_DEPTH_BIAS);
|
||||
|
||||
let bitmap_handle = bitmap_data
|
||||
.write(activation.context.gc_context)
|
||||
.bitmap_handle(activation.context.renderer);
|
||||
|
||||
// TODO: Implement pixel snapping
|
||||
let _pixel_snapping = args
|
||||
.get(2)
|
||||
|
@ -268,29 +264,20 @@ fn attach_bitmap<'gc>(
|
|||
.unwrap_or(&Value::Undefined)
|
||||
.as_bool(activation.swf_version());
|
||||
|
||||
if let Some(bitmap_handle) = bitmap_handle {
|
||||
//TODO: do attached BitmapDatas have character ids?
|
||||
let display_object = Bitmap::new_with_bitmap_data(
|
||||
&mut activation.context,
|
||||
0,
|
||||
Some(bitmap_handle),
|
||||
bitmap_data.read().width() as u16,
|
||||
bitmap_data.read().height() as u16,
|
||||
Some(bitmap_data),
|
||||
smoothing,
|
||||
);
|
||||
movie_clip.replace_at_depth(
|
||||
&mut activation.context,
|
||||
display_object.into(),
|
||||
depth,
|
||||
);
|
||||
display_object.post_instantiation(
|
||||
&mut activation.context,
|
||||
None,
|
||||
Instantiator::Avm1,
|
||||
true,
|
||||
);
|
||||
}
|
||||
//TODO: do attached BitmapDatas have character ids?
|
||||
let display_object = Bitmap::new_with_bitmap_data(
|
||||
&mut activation.context,
|
||||
0,
|
||||
bitmap_data,
|
||||
smoothing,
|
||||
);
|
||||
movie_clip.replace_at_depth(&mut activation.context, display_object.into(), depth);
|
||||
display_object.post_instantiation(
|
||||
&mut activation.context,
|
||||
None,
|
||||
Instantiator::Avm1,
|
||||
true,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::avm2::Error;
|
|||
use crate::avm2::Multiname;
|
||||
use crate::avm2::Namespace;
|
||||
use crate::avm2::QName;
|
||||
|
||||
use crate::bitmap::bitmap_data::BitmapData;
|
||||
use crate::character::Character;
|
||||
use crate::display_object::{Bitmap, TDisplayObject};
|
||||
|
@ -43,96 +44,80 @@ pub fn instance_init<'gc>(
|
|||
.coerce_to_boolean();
|
||||
|
||||
if let Some(bitmap) = this.as_display_object().and_then(|dobj| dobj.as_bitmap()) {
|
||||
if bitmap.bitmap_data().is_none() {
|
||||
//We are being initialized by the movie. This means that we
|
||||
//need to create bitmap data right away, since all AVM2 bitmaps
|
||||
//hold bitmap data.
|
||||
//We are being initialized by the movie. This means that we
|
||||
//need to create bitmap data right away, since all AVM2 bitmaps
|
||||
//hold bitmap data.
|
||||
|
||||
let bd_object = if let Some(bd_class) = bitmap.avm2_bitmapdata_class() {
|
||||
bd_class.construct(activation, &[])?
|
||||
} else if let Some(b_class) = bitmap.avm2_bitmap_class() {
|
||||
// Instantiating Bitmap from a Flex-style bitmap asset.
|
||||
// Contrary to the above comment, this code path DOES
|
||||
// trigger from AVM2, since the DisplayObject instantiation
|
||||
// logic does its job in this case.
|
||||
if let Some((movie, symbol_id)) = activation
|
||||
let bd_object = if let Some(bd_class) = bitmap.avm2_bitmapdata_class() {
|
||||
bd_class.construct(activation, &[])?
|
||||
} else if let Some(b_class) = bitmap.avm2_bitmap_class() {
|
||||
// Instantiating Bitmap from a Flex-style bitmap asset.
|
||||
// Contrary to the above comment, this code path DOES
|
||||
// trigger from AVM2, since the DisplayObject instantiation
|
||||
// logic does its job in this case.
|
||||
if let Some((movie, symbol_id)) = activation
|
||||
.context
|
||||
.library
|
||||
.avm2_class_registry()
|
||||
.class_symbol(b_class)
|
||||
{
|
||||
if let Some(Character::Bitmap {
|
||||
bitmap,
|
||||
initial_data,
|
||||
}) = activation
|
||||
.context
|
||||
.library
|
||||
.avm2_class_registry()
|
||||
.class_symbol(b_class)
|
||||
.library_for_movie_mut(movie)
|
||||
.character_by_id(symbol_id)
|
||||
.cloned()
|
||||
{
|
||||
if let Some(Character::Bitmap {
|
||||
bitmap,
|
||||
initial_data,
|
||||
}) = activation
|
||||
.context
|
||||
.library
|
||||
.library_for_movie_mut(movie)
|
||||
.character_by_id(symbol_id)
|
||||
.cloned()
|
||||
{
|
||||
let new_bitmap_data = GcCell::allocate(
|
||||
activation.context.gc_context,
|
||||
BitmapData::default(),
|
||||
);
|
||||
let new_bitmap_data =
|
||||
GcCell::allocate(activation.context.gc_context, BitmapData::default());
|
||||
|
||||
fill_bitmap_data_from_symbol(
|
||||
activation,
|
||||
bitmap,
|
||||
new_bitmap_data,
|
||||
initial_data,
|
||||
);
|
||||
BitmapDataObject::from_bitmap_data(
|
||||
activation,
|
||||
new_bitmap_data,
|
||||
activation.context.avm2.classes().bitmapdata,
|
||||
)?
|
||||
} else {
|
||||
//Class association not to a Bitmap
|
||||
return Err("Attempted to instantiate Bitmap from timeline with symbol class associated to non-Bitmap!".into());
|
||||
}
|
||||
fill_bitmap_data_from_symbol(
|
||||
activation,
|
||||
bitmap,
|
||||
new_bitmap_data,
|
||||
initial_data,
|
||||
);
|
||||
BitmapDataObject::from_bitmap_data(
|
||||
activation,
|
||||
new_bitmap_data,
|
||||
activation.context.avm2.classes().bitmapdata,
|
||||
)?
|
||||
} else {
|
||||
//Class association not bidirectional
|
||||
return Err("Cannot instantiate Bitmap from timeline without bidirectional symbol class association".into());
|
||||
//Class association not to a Bitmap
|
||||
return Err("Attempted to instantiate Bitmap from timeline with symbol class associated to non-Bitmap!".into());
|
||||
}
|
||||
} else {
|
||||
// No class association
|
||||
return Err(
|
||||
"Cannot instantiate Bitmap from timeline without associated symbol class"
|
||||
.into(),
|
||||
);
|
||||
};
|
||||
//Class association not bidirectional
|
||||
return Err("Cannot instantiate Bitmap from timeline without bidirectional symbol class association".into());
|
||||
}
|
||||
} else {
|
||||
// No class association
|
||||
return Err(
|
||||
"Cannot instantiate Bitmap from timeline without associated symbol class"
|
||||
.into(),
|
||||
);
|
||||
};
|
||||
|
||||
this.set_property(
|
||||
&Multiname::public("bitmapData"),
|
||||
bd_object.into(),
|
||||
activation,
|
||||
)?;
|
||||
}
|
||||
this.set_property(
|
||||
&Multiname::public("bitmapData"),
|
||||
bd_object.into(),
|
||||
activation,
|
||||
)?;
|
||||
|
||||
bitmap.set_smoothing(activation.context.gc_context, smoothing);
|
||||
} else {
|
||||
//We are being initialized by AVM2 (and aren't associated with a
|
||||
//Bitmap subclass).
|
||||
let bitmap_handle = if let Some(bd) = bitmap_data {
|
||||
bd.write(activation.context.gc_context)
|
||||
.bitmap_handle(activation.context.renderer)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let width = bitmap_data.map(|bd| bd.read().width()).unwrap_or(0) as u16;
|
||||
let height = bitmap_data.map(|bd| bd.read().height()).unwrap_or(0) as u16;
|
||||
let bitmap_data = bitmap_data.unwrap_or_else(|| {
|
||||
GcCell::allocate(activation.context.gc_context, BitmapData::dummy())
|
||||
});
|
||||
|
||||
let bitmap = Bitmap::new_with_bitmap_data(
|
||||
&mut activation.context,
|
||||
0,
|
||||
bitmap_handle,
|
||||
width,
|
||||
height,
|
||||
bitmap_data,
|
||||
smoothing,
|
||||
);
|
||||
let bitmap =
|
||||
Bitmap::new_with_bitmap_data(&mut activation.context, 0, bitmap_data, smoothing);
|
||||
|
||||
this.init_display_object(activation.context.gc_context, bitmap.into());
|
||||
}
|
||||
|
@ -160,10 +145,13 @@ pub fn bitmap_data<'gc>(
|
|||
.and_then(|this| this.as_display_object())
|
||||
.and_then(|dobj| dobj.as_bitmap())
|
||||
{
|
||||
return Ok(bitmap
|
||||
.bitmap_data()
|
||||
.map(|bd| bd.read().object2())
|
||||
.unwrap_or(Value::Null));
|
||||
let mut value = bitmap.bitmap_data().read().object2();
|
||||
|
||||
// AS3 expects an unset BitmapData to be null, not 'undefined'
|
||||
if matches!(value, Value::Undefined) {
|
||||
value = Value::Null;
|
||||
}
|
||||
return Ok(value);
|
||||
}
|
||||
|
||||
Ok(Value::Undefined)
|
||||
|
@ -179,13 +167,15 @@ pub fn set_bitmap_data<'gc>(
|
|||
.and_then(|this| this.as_display_object())
|
||||
.and_then(|dobj| dobj.as_bitmap())
|
||||
{
|
||||
let bitmap_data = args
|
||||
.get(0)
|
||||
.cloned()
|
||||
.unwrap_or(Value::Null)
|
||||
.as_object()
|
||||
.and_then(|bd| bd.as_bitmap_data());
|
||||
|
||||
let bitmap_data = args.get(0).unwrap_or(&Value::Null);
|
||||
let bitmap_data = if matches!(bitmap_data, Value::Null) {
|
||||
GcCell::allocate(activation.context.gc_context, BitmapData::dummy())
|
||||
} else {
|
||||
bitmap_data
|
||||
.coerce_to_object(activation)?
|
||||
.as_bitmap_data()
|
||||
.ok_or_else(|| Error::RustError("Argument was not a BitmapData".into()))?
|
||||
};
|
||||
bitmap.set_bitmap_data(&mut activation.context, bitmap_data);
|
||||
}
|
||||
|
||||
|
|
|
@ -200,6 +200,23 @@ impl fmt::Debug for BitmapData<'_> {
|
|||
}
|
||||
|
||||
impl<'gc> BitmapData<'gc> {
|
||||
// Creates a dummy BitmapData with no pixels or handle, marked as disposed.
|
||||
// This is used for AS3 `Bitmap` instances without a corresponding AS3 `BitmapData` instance.
|
||||
// Marking it as disposed skips rendering, and the unset `avm2_object` will cause this to
|
||||
// be inaccessible to AS3 code.
|
||||
pub fn dummy() -> Self {
|
||||
BitmapData {
|
||||
pixels: Vec::new(),
|
||||
dirty: false,
|
||||
width: 0,
|
||||
height: 0,
|
||||
transparency: false,
|
||||
disposed: true,
|
||||
bitmap_handle: None,
|
||||
avm2_object: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_pixels(&mut self, width: u32, height: u32, transparency: bool, fill_color: i32) {
|
||||
self.width = width;
|
||||
self.height = height;
|
||||
|
|
|
@ -10,8 +10,8 @@ use crate::display_object::{DisplayObjectBase, DisplayObjectPtr, TDisplayObject}
|
|||
use crate::prelude::*;
|
||||
use crate::vminterface::Instantiator;
|
||||
use core::fmt;
|
||||
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
||||
use ruffle_render::bitmap::BitmapHandle;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use ruffle_render::bitmap::BitmapFormat;
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use std::cell::{Ref, RefMut};
|
||||
|
||||
|
@ -64,19 +64,10 @@ impl fmt::Debug for Bitmap<'_> {
|
|||
#[collect(no_drop)]
|
||||
pub struct BitmapData<'gc> {
|
||||
base: DisplayObjectBase<'gc>,
|
||||
static_data: Gc<'gc, BitmapStatic>,
|
||||
id: CharacterId,
|
||||
|
||||
/// The current bitmap data object.
|
||||
bitmap_data: Option<GcCell<'gc, crate::bitmap::bitmap_data::BitmapData<'gc>>>,
|
||||
|
||||
/// The current bitmap handle.
|
||||
///
|
||||
/// This needs to be cached separately from the associated bitmap data so
|
||||
/// that it can be accessed without a mutation context.
|
||||
///
|
||||
/// If this is `None`, then the bitmap does not render anything.
|
||||
#[collect(require_static)]
|
||||
bitmap_handle: Option<BitmapHandle>,
|
||||
bitmap_data: GcCell<'gc, crate::bitmap::bitmap_data::BitmapData<'gc>>,
|
||||
|
||||
/// Whether or not bitmap smoothing is enabled.
|
||||
smoothing: bool,
|
||||
|
@ -102,10 +93,7 @@ impl<'gc> Bitmap<'gc> {
|
|||
pub fn new_with_bitmap_data(
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
id: CharacterId,
|
||||
bitmap_handle: Option<BitmapHandle>,
|
||||
width: u16,
|
||||
height: u16,
|
||||
bitmap_data: Option<GcCell<'gc, crate::bitmap::bitmap_data::BitmapData<'gc>>>,
|
||||
bitmap_data: GcCell<'gc, crate::bitmap::bitmap_data::BitmapData<'gc>>,
|
||||
smoothing: bool,
|
||||
) -> Self {
|
||||
//NOTE: We do *not* solicit a handle from the `bitmap_data` at this
|
||||
|
@ -115,9 +103,8 @@ impl<'gc> Bitmap<'gc> {
|
|||
context.gc_context,
|
||||
BitmapData {
|
||||
base: Default::default(),
|
||||
static_data: Gc::allocate(context.gc_context, BitmapStatic { id, width, height }),
|
||||
id,
|
||||
bitmap_data,
|
||||
bitmap_handle,
|
||||
smoothing,
|
||||
avm2_object: None,
|
||||
avm2_bitmap_class: BitmapClass::NoSubclass,
|
||||
|
@ -131,45 +118,43 @@ impl<'gc> Bitmap<'gc> {
|
|||
id: CharacterId,
|
||||
bitmap: ruffle_render::bitmap::Bitmap,
|
||||
) -> Result<Self, ruffle_render::error::Error> {
|
||||
let width = bitmap.width() as u16;
|
||||
let height = bitmap.height() as u16;
|
||||
let bitmap_handle = context.renderer.register_bitmap(bitmap)?;
|
||||
let bitmap_data = None;
|
||||
let width = bitmap.width();
|
||||
let height = bitmap.height();
|
||||
let pixels: Vec<_> = bitmap
|
||||
.as_colors()
|
||||
.map(crate::bitmap::bitmap_data::Color::from)
|
||||
.collect();
|
||||
let mut bitmap_data = crate::bitmap::bitmap_data::BitmapData::default();
|
||||
bitmap_data.set_pixels(
|
||||
width,
|
||||
height,
|
||||
match bitmap.format() {
|
||||
BitmapFormat::Rgba => true,
|
||||
BitmapFormat::Rgb => false,
|
||||
},
|
||||
pixels,
|
||||
);
|
||||
let bitmap_data = GcCell::allocate(context.gc_context, bitmap_data);
|
||||
|
||||
let smoothing = true;
|
||||
Ok(Self::new_with_bitmap_data(
|
||||
context,
|
||||
id,
|
||||
Some(bitmap_handle),
|
||||
width,
|
||||
height,
|
||||
bitmap_data,
|
||||
smoothing,
|
||||
))
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn bitmap_handle(self) -> Option<BitmapHandle> {
|
||||
self.0.read().bitmap_handle.clone()
|
||||
}
|
||||
|
||||
pub fn width(self) -> u16 {
|
||||
let read = self.0.read();
|
||||
|
||||
read.bitmap_data
|
||||
.map(|bd| bd.read().width() as u16)
|
||||
.unwrap_or_else(|| read.static_data.width)
|
||||
self.0.read().bitmap_data.read().width() as u16
|
||||
}
|
||||
|
||||
pub fn height(self) -> u16 {
|
||||
let read = self.0.read();
|
||||
|
||||
read.bitmap_data
|
||||
.map(|bd| bd.read().height() as u16)
|
||||
.unwrap_or_else(|| read.static_data.height)
|
||||
self.0.read().bitmap_data.read().height() as u16
|
||||
}
|
||||
|
||||
/// Retrieve the bitmap data associated with this `Bitmap`.
|
||||
pub fn bitmap_data(self) -> Option<GcCell<'gc, crate::bitmap::bitmap_data::BitmapData<'gc>>> {
|
||||
pub fn bitmap_data(self) -> GcCell<'gc, crate::bitmap::bitmap_data::BitmapData<'gc>> {
|
||||
self.0.read().bitmap_data
|
||||
}
|
||||
|
||||
|
@ -184,25 +169,9 @@ impl<'gc> Bitmap<'gc> {
|
|||
pub fn set_bitmap_data(
|
||||
self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
bitmap_data: Option<GcCell<'gc, crate::bitmap::bitmap_data::BitmapData<'gc>>>,
|
||||
bitmap_data: GcCell<'gc, crate::bitmap::bitmap_data::BitmapData<'gc>>,
|
||||
) {
|
||||
if let Some(bitmap_data) = bitmap_data {
|
||||
let bitmap_handle = bitmap_data
|
||||
.write(context.gc_context)
|
||||
.bitmap_handle(context.renderer);
|
||||
|
||||
let mut write = self.0.write(context.gc_context);
|
||||
|
||||
write.bitmap_data = Some(bitmap_data);
|
||||
if let Some(bitmap_handle) = bitmap_handle {
|
||||
write.bitmap_handle = Some(bitmap_handle);
|
||||
}
|
||||
} else {
|
||||
let mut write = self.0.write(context.gc_context);
|
||||
|
||||
write.bitmap_data = None;
|
||||
write.bitmap_handle = None;
|
||||
}
|
||||
self.0.write(context.gc_context).bitmap_data = bitmap_data;
|
||||
}
|
||||
|
||||
pub fn avm2_bitmapdata_class(self) -> Option<Avm2ClassObject<'gc>> {
|
||||
|
@ -262,7 +231,7 @@ impl<'gc> TDisplayObject<'gc> for Bitmap<'gc> {
|
|||
}
|
||||
|
||||
fn id(&self) -> CharacterId {
|
||||
self.0.read().static_data.id
|
||||
self.0.read().id
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
|
@ -317,20 +286,24 @@ impl<'gc> TDisplayObject<'gc> for Bitmap<'gc> {
|
|||
}
|
||||
|
||||
let bitmap_data = self.0.read();
|
||||
if let Some(bitmap_handle) = &bitmap_data.bitmap_handle {
|
||||
if let Some(inner_bitmap_data) = bitmap_data.bitmap_data {
|
||||
if let Ok(mut bd) = inner_bitmap_data.try_write(context.gc_context) {
|
||||
bd.update_dirty_texture(context);
|
||||
} else {
|
||||
return; // bail, this is caused by recursive render attempt. TODO: support this.
|
||||
};
|
||||
let inner_bitmap_data = bitmap_data.bitmap_data.try_write(context.gc_context);
|
||||
if let Ok(mut inner_bitmap_data) = inner_bitmap_data {
|
||||
if inner_bitmap_data.disposed() {
|
||||
return;
|
||||
}
|
||||
|
||||
inner_bitmap_data.update_dirty_texture(context);
|
||||
let handle = inner_bitmap_data
|
||||
.bitmap_handle(context.renderer)
|
||||
.expect("Missing bitmap handle");
|
||||
|
||||
context.commands.render_bitmap(
|
||||
&bitmap_handle,
|
||||
&handle,
|
||||
context.transform_stack.transform(),
|
||||
bitmap_data.smoothing,
|
||||
);
|
||||
} else {
|
||||
//this is caused by recursive render attempt. TODO: support this.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -350,12 +323,3 @@ impl<'gc> TDisplayObject<'gc> for Bitmap<'gc> {
|
|||
Some(self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Static data shared between all instances of a bitmap.
|
||||
#[derive(Clone, Collect)]
|
||||
#[collect(no_drop)]
|
||||
struct BitmapStatic {
|
||||
id: CharacterId,
|
||||
width: u16,
|
||||
height: u16,
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ use crate::avm2::{
|
|||
use crate::context::{RenderContext, UpdateContext};
|
||||
use crate::display_object::{DisplayObjectBase, DisplayObjectPtr, TDisplayObject};
|
||||
use crate::drawing::Drawing;
|
||||
use crate::library::MovieLibrarySource;
|
||||
use crate::prelude::*;
|
||||
use crate::tag_utils::SwfMovie;
|
||||
use crate::vminterface::Instantiator;
|
||||
|
@ -47,11 +48,13 @@ impl<'gc> Graphic<'gc> {
|
|||
let static_data = GraphicStatic {
|
||||
id: swf_shape.id,
|
||||
bounds: (&swf_shape.shape_bounds).into(),
|
||||
render_handle: Some(
|
||||
context
|
||||
.renderer
|
||||
.register_shape((&swf_shape).into(), library),
|
||||
),
|
||||
render_handle: Some(context.renderer.register_shape(
|
||||
(&swf_shape).into(),
|
||||
&MovieLibrarySource {
|
||||
library,
|
||||
gc_context: context.gc_context,
|
||||
},
|
||||
)),
|
||||
shape: swf_shape,
|
||||
movie: Some(movie),
|
||||
};
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
use crate::context::{RenderContext, UpdateContext};
|
||||
use crate::display_object::{DisplayObjectBase, DisplayObjectPtr, TDisplayObject};
|
||||
use crate::library::Library;
|
||||
use crate::library::{Library, MovieLibrarySource};
|
||||
use crate::prelude::*;
|
||||
use crate::tag_utils::SwfMovie;
|
||||
use core::fmt;
|
||||
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
||||
use ruffle_render::backend::{RenderBackend, ShapeHandle};
|
||||
use ruffle_render::backend::ShapeHandle;
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::sync::Arc;
|
||||
|
@ -102,7 +102,7 @@ impl<'gc> TDisplayObject<'gc> for MorphShape<'gc> {
|
|||
let this = self.0.read();
|
||||
let ratio = this.ratio;
|
||||
let static_data = this.static_data;
|
||||
let shape_handle = static_data.get_shape(context.renderer, context.library, ratio);
|
||||
let shape_handle = static_data.get_shape(context, context.library, ratio);
|
||||
context
|
||||
.commands
|
||||
.render_shape(shape_handle, context.transform_stack.transform());
|
||||
|
@ -183,10 +183,10 @@ impl MorphShapeStatic {
|
|||
|
||||
/// Retrieves the `ShapeHandle` for the given ratio.
|
||||
/// Lazily intializes and tessellates the shape if it does not yet exist.
|
||||
fn get_shape(
|
||||
fn get_shape<'gc>(
|
||||
&self,
|
||||
renderer: &'_ mut dyn RenderBackend,
|
||||
library: &Library<'_>,
|
||||
context: &mut RenderContext<'_, 'gc, '_>,
|
||||
library: &Library<'gc>,
|
||||
ratio: u16,
|
||||
) -> ShapeHandle {
|
||||
let mut frame = self.get_frame(ratio);
|
||||
|
@ -194,7 +194,13 @@ impl MorphShapeStatic {
|
|||
handle
|
||||
} else {
|
||||
let library = library.library_for_movie(self.movie.clone()).unwrap();
|
||||
let handle = renderer.register_shape((&frame.shape).into(), library);
|
||||
let handle = context.renderer.register_shape(
|
||||
(&frame.shape).into(),
|
||||
&MovieLibrarySource {
|
||||
library,
|
||||
gc_context: context.gc_context,
|
||||
},
|
||||
);
|
||||
frame.shape_handle = Some(handle);
|
||||
handle
|
||||
}
|
||||
|
|
|
@ -2979,7 +2979,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
|
|||
) -> Result<(), Error> {
|
||||
let define_bits_lossless = reader.read_define_bits_lossless(version)?;
|
||||
let bitmap = ruffle_render::utils::decode_define_bits_lossless(&define_bits_lossless)?;
|
||||
let initial_data: Vec<i32> = bitmap.clone().into();
|
||||
let initial_data: Vec<i32> = bitmap.as_colors().collect();
|
||||
let bitmap = Bitmap::new(context, define_bits_lossless.id, bitmap)?;
|
||||
context
|
||||
.library
|
||||
|
@ -3103,7 +3103,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
|
|||
.jpeg_tables();
|
||||
let jpeg_data = ruffle_render::utils::glue_tables_to_jpeg(jpeg_data, jpeg_tables);
|
||||
let bitmap = ruffle_render::utils::decode_define_bits_jpeg(&jpeg_data, None)?;
|
||||
let initial_data: Vec<i32> = bitmap.clone().into();
|
||||
let initial_data: Vec<i32> = bitmap.as_colors().collect();
|
||||
let bitmap = Bitmap::new(context, id, bitmap)?;
|
||||
context
|
||||
.library
|
||||
|
@ -3127,7 +3127,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
|
|||
let id = reader.read_u16()?;
|
||||
let jpeg_data = reader.read_slice_to_end();
|
||||
let bitmap = ruffle_render::utils::decode_define_bits_jpeg(jpeg_data, None)?;
|
||||
let initial_data: Vec<i32> = bitmap.clone().into();
|
||||
let initial_data: Vec<i32> = bitmap.as_colors().collect();
|
||||
let bitmap = Bitmap::new(context, id, bitmap)?;
|
||||
context
|
||||
.library
|
||||
|
@ -3157,7 +3157,7 @@ impl<'gc, 'a> MovieClipData<'gc> {
|
|||
let jpeg_data = reader.read_slice(jpeg_len)?;
|
||||
let alpha_data = reader.read_slice_to_end();
|
||||
let bitmap = ruffle_render::utils::decode_define_bits_jpeg(jpeg_data, Some(alpha_data))?;
|
||||
let initial_data: Vec<i32> = bitmap.clone().into();
|
||||
let initial_data: Vec<i32> = bitmap.as_colors().collect();
|
||||
let bitmap = Bitmap::new(context, id, bitmap)?;
|
||||
context
|
||||
.library
|
||||
|
|
|
@ -2,6 +2,7 @@ use crate::avm1::PropertyMap as Avm1PropertyMap;
|
|||
use crate::avm2::{ClassObject as Avm2ClassObject, Domain as Avm2Domain};
|
||||
use crate::backend::audio::SoundHandle;
|
||||
use crate::character::Character;
|
||||
|
||||
use crate::display_object::{Bitmap, Graphic, MorphShape, TDisplayObject, Text};
|
||||
use crate::font::{Font, FontDescriptor};
|
||||
use crate::prelude::*;
|
||||
|
@ -11,6 +12,7 @@ use gc_arena::{Collect, MutationContext};
|
|||
use ruffle_render::backend::RenderBackend;
|
||||
use ruffle_render::bitmap::BitmapHandle;
|
||||
use ruffle_render::utils::remove_invalid_jpeg_data;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Weak};
|
||||
use swf::CharacterId;
|
||||
|
@ -319,17 +321,30 @@ impl<'gc> MovieLibrary<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'gc> ruffle_render::bitmap::BitmapSource for MovieLibrary<'gc> {
|
||||
pub struct MovieLibrarySource<'a, 'gc, 'gc_context> {
|
||||
pub library: &'a MovieLibrary<'gc>,
|
||||
pub gc_context: MutationContext<'gc, 'gc_context>,
|
||||
}
|
||||
|
||||
impl<'a, 'gc, 'gc_context> ruffle_render::bitmap::BitmapSource
|
||||
for MovieLibrarySource<'a, 'gc, 'gc_context>
|
||||
{
|
||||
fn bitmap_size(&self, id: u16) -> Option<ruffle_render::bitmap::BitmapSize> {
|
||||
self.get_bitmap(id)
|
||||
self.library
|
||||
.get_bitmap(id)
|
||||
.map(|bitmap| ruffle_render::bitmap::BitmapSize {
|
||||
width: bitmap.width(),
|
||||
height: bitmap.height(),
|
||||
})
|
||||
}
|
||||
fn bitmap_handle(&self, id: u16, _backend: &mut dyn RenderBackend) -> Option<BitmapHandle> {
|
||||
self.get_bitmap(id)
|
||||
.and_then(|bitmap| bitmap.bitmap_handle())
|
||||
|
||||
fn bitmap_handle(&self, id: u16, backend: &mut dyn RenderBackend) -> Option<BitmapHandle> {
|
||||
self.library.get_bitmap(id).and_then(|bitmap| {
|
||||
bitmap
|
||||
.bitmap_data()
|
||||
.write(self.gc_context)
|
||||
.bitmap_handle(backend)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -102,33 +102,19 @@ impl Bitmap {
|
|||
pub fn data_mut(&mut self) -> &mut [u8] {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Bitmap> for Vec<i32> {
|
||||
fn from(bitmap: Bitmap) -> Self {
|
||||
match bitmap.format {
|
||||
BitmapFormat::Rgb => bitmap
|
||||
.data
|
||||
.chunks_exact(3)
|
||||
.map(|chunk| {
|
||||
let red = chunk[0];
|
||||
let green = chunk[1];
|
||||
let blue = chunk[2];
|
||||
i32::from_le_bytes([blue, green, red, 0xFF])
|
||||
})
|
||||
.collect(),
|
||||
BitmapFormat::Rgba => bitmap
|
||||
.data
|
||||
.chunks_exact(4)
|
||||
.map(|chunk| {
|
||||
let red = chunk[0];
|
||||
let green = chunk[1];
|
||||
let blue = chunk[2];
|
||||
let alpha = chunk[3];
|
||||
i32::from_le_bytes([blue, green, red, alpha])
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
pub fn as_colors(&self) -> impl Iterator<Item = i32> + '_ {
|
||||
let chunks = match self.format {
|
||||
BitmapFormat::Rgb => self.data.chunks_exact(3),
|
||||
BitmapFormat::Rgba => self.data.chunks_exact(4),
|
||||
};
|
||||
chunks.map(|chunk| {
|
||||
let red = chunk[0];
|
||||
let green = chunk[1];
|
||||
let blue = chunk[2];
|
||||
let alpha = chunk.get(3).copied().unwrap_or(0xFF);
|
||||
i32::from_le_bytes([blue, green, red, alpha])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue