render: Replace `BoundingBox` with `swf::Rectangle`
This commit is contained in:
parent
5756c847cd
commit
83c15b8033
|
@ -18,14 +18,13 @@ use gc_arena::{Gc, GcCell, MutationContext};
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use instant::Instant;
|
use instant::Instant;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use ruffle_render::bounding_box::BoundingBox;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use swf::avm1::read::Reader;
|
use swf::avm1::read::Reader;
|
||||||
use swf::avm1::types::*;
|
use swf::avm1::types::*;
|
||||||
use swf::Twips;
|
use swf::{Rectangle, Twips};
|
||||||
use url::form_urlencoded;
|
use url::form_urlencoded;
|
||||||
|
|
||||||
use super::object_reference::MovieClipReference;
|
use super::object_reference::MovieClipReference;
|
||||||
|
@ -3067,8 +3066,7 @@ pub fn start_drag<'gc>(
|
||||||
if y_max.get() < y_min.get() {
|
if y_max.get() < y_min.get() {
|
||||||
std::mem::swap(&mut y_min, &mut y_max);
|
std::mem::swap(&mut y_min, &mut y_max);
|
||||||
}
|
}
|
||||||
BoundingBox {
|
Rectangle {
|
||||||
valid: true,
|
|
||||||
x_min,
|
x_min,
|
||||||
y_min,
|
y_min,
|
||||||
x_max,
|
x_max,
|
||||||
|
|
|
@ -1205,8 +1205,7 @@ fn get_bounds<'gc>(
|
||||||
// the final matrix, but this matches Flash's behavior.
|
// the final matrix, but this matches Flash's behavior.
|
||||||
let to_global_matrix = movie_clip.local_to_global_matrix();
|
let to_global_matrix = movie_clip.local_to_global_matrix();
|
||||||
let to_target_matrix = target.global_to_local_matrix();
|
let to_target_matrix = target.global_to_local_matrix();
|
||||||
let bounds_transform = to_target_matrix * to_global_matrix;
|
to_target_matrix * to_global_matrix * bounds
|
||||||
bounds.transform(&bounds_transform)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let out = ScriptObject::new(
|
let out = ScriptObject::new(
|
||||||
|
|
|
@ -18,8 +18,7 @@ use crate::vminterface::Instantiator;
|
||||||
use crate::{avm2_stub_getter, avm2_stub_setter};
|
use crate::{avm2_stub_getter, avm2_stub_setter};
|
||||||
use ruffle_render::filters::Filter;
|
use ruffle_render::filters::Filter;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use swf::Twips;
|
use swf::BlendMode;
|
||||||
use swf::{BlendMode, Rectangle};
|
|
||||||
|
|
||||||
pub use crate::avm2::object::stage_allocator as display_object_allocator;
|
pub use crate::avm2::object::stage_allocator as display_object_allocator;
|
||||||
|
|
||||||
|
@ -827,7 +826,7 @@ pub fn set_blend_mode<'gc>(
|
||||||
|
|
||||||
fn new_rectangle<'gc>(
|
fn new_rectangle<'gc>(
|
||||||
activation: &mut Activation<'_, 'gc>,
|
activation: &mut Activation<'_, 'gc>,
|
||||||
rectangle: BoundingBox,
|
rectangle: Rectangle<Twips>,
|
||||||
) -> Result<Object<'gc>, Error<'gc>> {
|
) -> Result<Object<'gc>, Error<'gc>> {
|
||||||
let x = rectangle.x_min.to_pixels();
|
let x = rectangle.x_min.to_pixels();
|
||||||
let y = rectangle.y_min.to_pixels();
|
let y = rectangle.y_min.to_pixels();
|
||||||
|
@ -848,7 +847,7 @@ pub fn get_scroll_rect<'gc>(
|
||||||
) -> Result<Value<'gc>, Error<'gc>> {
|
) -> Result<Value<'gc>, Error<'gc>> {
|
||||||
if let Some(dobj) = this.and_then(|this| this.as_display_object()) {
|
if let Some(dobj) = this.and_then(|this| this.as_display_object()) {
|
||||||
if dobj.has_scroll_rect() {
|
if dobj.has_scroll_rect() {
|
||||||
return Ok(new_rectangle(activation, dobj.next_scroll_rect().into())?.into());
|
return Ok(new_rectangle(activation, dobj.next_scroll_rect())?.into());
|
||||||
} else {
|
} else {
|
||||||
return Ok(Value::Null);
|
return Ok(Value::Null);
|
||||||
}
|
}
|
||||||
|
@ -993,8 +992,7 @@ pub fn get_bounds<'gc>(
|
||||||
// the final matrix, but this matches Flash's behavior.
|
// the final matrix, but this matches Flash's behavior.
|
||||||
let to_global_matrix = dobj.local_to_global_matrix();
|
let to_global_matrix = dobj.local_to_global_matrix();
|
||||||
let to_target_matrix = target.global_to_local_matrix();
|
let to_target_matrix = target.global_to_local_matrix();
|
||||||
let bounds_transform = to_target_matrix * to_global_matrix;
|
to_target_matrix * to_global_matrix * bounds
|
||||||
bounds.transform(&bounds_transform)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return Ok(new_rectangle(activation, out_bounds)?.into());
|
return Ok(new_rectangle(activation, out_bounds)?.into());
|
||||||
|
|
|
@ -7,9 +7,8 @@ use crate::avm2::Error;
|
||||||
use crate::avm2::Multiname;
|
use crate::avm2::Multiname;
|
||||||
use crate::display_object::{MovieClip, SoundTransform, TDisplayObject};
|
use crate::display_object::{MovieClip, SoundTransform, TDisplayObject};
|
||||||
use crate::tag_utils::SwfMovie;
|
use crate::tag_utils::SwfMovie;
|
||||||
use ruffle_render::bounding_box::BoundingBox;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use swf::Twips;
|
use swf::{Rectangle, Twips};
|
||||||
|
|
||||||
/// Implements `flash.display.Sprite`'s `init` method, which is called from the constructor
|
/// Implements `flash.display.Sprite`'s `init` method, which is called from the constructor
|
||||||
pub fn init<'gc>(
|
pub fn init<'gc>(
|
||||||
|
@ -215,8 +214,7 @@ pub fn start_drag<'gc>(
|
||||||
std::mem::swap(&mut y_min, &mut y_max);
|
std::mem::swap(&mut y_min, &mut y_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
BoundingBox {
|
Rectangle {
|
||||||
valid: true,
|
|
||||||
x_min,
|
x_min,
|
||||||
y_min,
|
y_min,
|
||||||
x_max,
|
x_max,
|
||||||
|
|
|
@ -18,7 +18,7 @@ use ruffle_render::transform::Transform;
|
||||||
use std::cell::{Ref, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use swf::{BlendMode, Fixed8, Rectangle};
|
use swf::{BlendMode, Fixed8};
|
||||||
|
|
||||||
mod avm1_button;
|
mod avm1_button;
|
||||||
mod avm2_button;
|
mod avm2_button;
|
||||||
|
@ -648,20 +648,20 @@ pub trait TDisplayObject<'gc>:
|
||||||
/// Implementors must override this method.
|
/// Implementors must override this method.
|
||||||
/// Leaf DisplayObjects should return their bounds.
|
/// Leaf DisplayObjects should return their bounds.
|
||||||
/// Composite DisplayObjects that only contain children should return `&Default::default()`
|
/// Composite DisplayObjects that only contain children should return `&Default::default()`
|
||||||
fn self_bounds(&self) -> BoundingBox;
|
fn self_bounds(&self) -> Rectangle<Twips>;
|
||||||
|
|
||||||
/// The untransformed bounding box of this object including children.
|
/// The untransformed bounding box of this object including children.
|
||||||
fn bounds(&self) -> BoundingBox {
|
fn bounds(&self) -> Rectangle<Twips> {
|
||||||
self.bounds_with_transform(&Matrix::default())
|
self.bounds_with_transform(&Matrix::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The local bounding box of this object including children, in its parent's coordinate system.
|
/// The local bounding box of this object including children, in its parent's coordinate system.
|
||||||
fn local_bounds(&self) -> BoundingBox {
|
fn local_bounds(&self) -> Rectangle<Twips> {
|
||||||
self.bounds_with_transform(self.base().matrix())
|
self.bounds_with_transform(self.base().matrix())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The world bounding box of this object including children, relative to the stage.
|
/// The world bounding box of this object including children, relative to the stage.
|
||||||
fn world_bounds(&self) -> BoundingBox {
|
fn world_bounds(&self) -> Rectangle<Twips> {
|
||||||
self.bounds_with_transform(&self.local_to_global_matrix())
|
self.bounds_with_transform(&self.local_to_global_matrix())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -669,26 +669,25 @@ pub trait TDisplayObject<'gc>:
|
||||||
/// This function recurses down and transforms the AABB each child before adding
|
/// This function recurses down and transforms the AABB each child before adding
|
||||||
/// it to the bounding box. This gives a tighter AABB then if we simply transformed
|
/// it to the bounding box. This gives a tighter AABB then if we simply transformed
|
||||||
/// the overall AABB.
|
/// the overall AABB.
|
||||||
fn bounds_with_transform(&self, matrix: &Matrix) -> BoundingBox {
|
fn bounds_with_transform(&self, matrix: &Matrix) -> Rectangle<Twips> {
|
||||||
// A scroll rect completely overrides an object's bounds,
|
// A scroll rect completely overrides an object's bounds,
|
||||||
// and can even the bounding box to be larger than the actual content
|
// and can even the bounding box to be larger than the actual content
|
||||||
if let Some(scroll_rect) = self.scroll_rect() {
|
if let Some(scroll_rect) = self.scroll_rect() {
|
||||||
return BoundingBox {
|
return *matrix
|
||||||
x_min: Twips::from_pixels(0.0),
|
* Rectangle {
|
||||||
y_min: Twips::from_pixels(0.0),
|
x_min: Twips::ZERO,
|
||||||
x_max: scroll_rect.width(),
|
y_min: Twips::ZERO,
|
||||||
y_max: scroll_rect.height(),
|
x_max: scroll_rect.width(),
|
||||||
valid: true,
|
y_max: scroll_rect.height(),
|
||||||
}
|
};
|
||||||
.transform(matrix);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bounds = self.self_bounds().transform(matrix);
|
let mut bounds = *matrix * self.self_bounds();
|
||||||
|
|
||||||
if let Some(ctr) = self.as_container() {
|
if let Some(ctr) = self.as_container() {
|
||||||
for child in ctr.iter_render_list() {
|
for child in ctr.iter_render_list() {
|
||||||
let matrix = *matrix * *child.base().matrix();
|
let matrix = *matrix * *child.base().matrix();
|
||||||
bounds.union(&child.bounds_with_transform(&matrix));
|
bounds = bounds.union(&child.bounds_with_transform(&matrix));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -336,9 +336,9 @@ impl<'gc> TDisplayObject<'gc> for Avm1Button<'gc> {
|
||||||
self.render_children(context);
|
self.render_children(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
// No inherent bounds; contains child DisplayObjects.
|
// No inherent bounds; contains child DisplayObjects.
|
||||||
BoundingBox::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hit_test_shape(
|
fn hit_test_shape(
|
||||||
|
|
|
@ -644,20 +644,20 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
// No inherent bounds; contains child DisplayObjects.
|
// No inherent bounds; contains child DisplayObjects.
|
||||||
BoundingBox::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bounds_with_transform(&self, matrix: &Matrix) -> BoundingBox {
|
fn bounds_with_transform(&self, matrix: &Matrix) -> Rectangle<Twips> {
|
||||||
// Get self bounds
|
// Get self bounds
|
||||||
let mut bounds = self.self_bounds().transform(matrix);
|
let mut bounds = *matrix * self.self_bounds();
|
||||||
|
|
||||||
// Add the bounds of the child, dictated by current state
|
// Add the bounds of the child, dictated by current state
|
||||||
let state = self.0.read().state;
|
let state = self.0.read().state;
|
||||||
if let Some(child) = self.get_state_child(state.into()) {
|
if let Some(child) = self.get_state_child(state.into()) {
|
||||||
let child_bounds = child.bounds_with_transform(matrix);
|
let child_bounds = child.bounds_with_transform(matrix);
|
||||||
bounds.union(&child_bounds);
|
bounds = bounds.union(&child_bounds);
|
||||||
}
|
}
|
||||||
|
|
||||||
bounds
|
bounds
|
||||||
|
|
|
@ -242,13 +242,12 @@ impl<'gc> TDisplayObject<'gc> for Bitmap<'gc> {
|
||||||
self.0.read().id
|
self.0.read().id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
BoundingBox {
|
Rectangle {
|
||||||
x_min: Twips::ZERO,
|
x_min: Twips::ZERO,
|
||||||
y_min: Twips::ZERO,
|
y_min: Twips::ZERO,
|
||||||
x_max: Twips::from_pixels(Bitmap::width(*self).into()),
|
x_max: Twips::from_pixels(Bitmap::width(*self).into()),
|
||||||
y_max: Twips::from_pixels(Bitmap::height(*self).into()),
|
y_max: Twips::from_pixels(Bitmap::height(*self).into()),
|
||||||
valid: true,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,7 @@ pub struct EditTextData<'gc> {
|
||||||
|
|
||||||
/// The current intrinsic bounds of the text field.
|
/// The current intrinsic bounds of the text field.
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
bounds: BoundingBox,
|
bounds: Rectangle<Twips>,
|
||||||
|
|
||||||
/// The AVM1 object handle
|
/// The AVM1 object handle
|
||||||
object: Option<AvmObject<'gc>>,
|
object: Option<AvmObject<'gc>>,
|
||||||
|
@ -219,13 +219,11 @@ impl<'gc> EditText<'gc> {
|
||||||
AutoSizeMode::None
|
AutoSizeMode::None
|
||||||
};
|
};
|
||||||
|
|
||||||
let bounds: BoundingBox = swf_tag.bounds().into();
|
|
||||||
|
|
||||||
let (layout, intrinsic_bounds) = LayoutBox::lower_from_text_spans(
|
let (layout, intrinsic_bounds) = LayoutBox::lower_from_text_spans(
|
||||||
&text_spans,
|
&text_spans,
|
||||||
context,
|
context,
|
||||||
swf_movie.clone(),
|
swf_movie.clone(),
|
||||||
bounds.width() - Twips::from_pixels(Self::INTERNAL_PADDING * 2.0),
|
swf_tag.bounds().width() - Twips::from_pixels(Self::INTERNAL_PADDING * 2.0),
|
||||||
swf_tag.is_word_wrap(),
|
swf_tag.is_word_wrap(),
|
||||||
!swf_tag.use_outlines(),
|
!swf_tag.use_outlines(),
|
||||||
);
|
);
|
||||||
|
@ -233,8 +231,8 @@ impl<'gc> EditText<'gc> {
|
||||||
|
|
||||||
let mut base = InteractiveObjectBase::default();
|
let mut base = InteractiveObjectBase::default();
|
||||||
|
|
||||||
base.base.matrix_mut().tx = bounds.x_min;
|
base.base.matrix_mut().tx = swf_tag.bounds().x_min;
|
||||||
base.base.matrix_mut().ty = bounds.y_min;
|
base.base.matrix_mut().ty = swf_tag.bounds().y_min;
|
||||||
|
|
||||||
let variable = if !swf_tag.variable_name().is_empty() {
|
let variable = if !swf_tag.variable_name().is_empty() {
|
||||||
Some(swf_tag.variable_name())
|
Some(swf_tag.variable_name())
|
||||||
|
@ -275,7 +273,7 @@ impl<'gc> EditText<'gc> {
|
||||||
object: None,
|
object: None,
|
||||||
layout,
|
layout,
|
||||||
intrinsic_bounds,
|
intrinsic_bounds,
|
||||||
bounds,
|
bounds: swf_tag.bounds().clone(),
|
||||||
autosize,
|
autosize,
|
||||||
variable: variable.map(|s| s.to_string_lossy(encoding)),
|
variable: variable.map(|s| s.to_string_lossy(encoding)),
|
||||||
bound_stage_object: None,
|
bound_stage_object: None,
|
||||||
|
@ -735,7 +733,7 @@ impl<'gc> EditText<'gc> {
|
||||||
AutoSizeMode::Right => edit_text.bounds.x_max - width,
|
AutoSizeMode::Right => edit_text.bounds.x_max - width,
|
||||||
AutoSizeMode::None => unreachable!(),
|
AutoSizeMode::None => unreachable!(),
|
||||||
};
|
};
|
||||||
edit_text.bounds.set_x(new_x);
|
edit_text.bounds.x_min = new_x;
|
||||||
edit_text.bounds.set_width(width);
|
edit_text.bounds.set_width(width);
|
||||||
} else {
|
} else {
|
||||||
let width = edit_text.static_data.bounds.width();
|
let width = edit_text.static_data.bounds.width();
|
||||||
|
@ -1570,7 +1568,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
||||||
self.0.write(mc).object = Some(to.into());
|
self.0.write(mc).object = Some(to.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
self.0.read().bounds.clone()
|
self.0.read().bounds.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1607,9 +1605,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
||||||
|
|
||||||
fn width(&self) -> f64 {
|
fn width(&self) -> f64 {
|
||||||
let edit_text = self.0.read();
|
let edit_text = self.0.read();
|
||||||
edit_text
|
(edit_text.base.base.transform.matrix * edit_text.bounds.clone())
|
||||||
.bounds
|
|
||||||
.transform(&edit_text.base.base.transform.matrix)
|
|
||||||
.width()
|
.width()
|
||||||
.to_pixels()
|
.to_pixels()
|
||||||
}
|
}
|
||||||
|
@ -1626,9 +1622,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
||||||
|
|
||||||
fn height(&self) -> f64 {
|
fn height(&self) -> f64 {
|
||||||
let edit_text = self.0.read();
|
let edit_text = self.0.read();
|
||||||
edit_text
|
(edit_text.base.base.transform.matrix * edit_text.bounds.clone())
|
||||||
.bounds
|
|
||||||
.transform(&edit_text.base.base.transform.matrix)
|
|
||||||
.height()
|
.height()
|
||||||
.to_pixels()
|
.to_pixels()
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ impl<'gc> Graphic<'gc> {
|
||||||
let library = context.library.library_for_movie(movie.clone()).unwrap();
|
let library = context.library.library_for_movie(movie.clone()).unwrap();
|
||||||
let static_data = GraphicStatic {
|
let static_data = GraphicStatic {
|
||||||
id: swf_shape.id,
|
id: swf_shape.id,
|
||||||
bounds: (&swf_shape.shape_bounds).into(),
|
bounds: swf_shape.shape_bounds.clone(),
|
||||||
render_handle: Some(context.renderer.register_shape(
|
render_handle: Some(context.renderer.register_shape(
|
||||||
(&swf_shape).into(),
|
(&swf_shape).into(),
|
||||||
&MovieLibrarySource {
|
&MovieLibrarySource {
|
||||||
|
@ -134,9 +134,9 @@ impl<'gc> TDisplayObject<'gc> for Graphic<'gc> {
|
||||||
self.0.read().static_data.id
|
self.0.read().static_data.id
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
if let Some(drawing) = &self.0.read().drawing {
|
if let Some(drawing) = &self.0.read().drawing {
|
||||||
drawing.self_bounds()
|
drawing.self_bounds().clone()
|
||||||
} else {
|
} else {
|
||||||
self.0.read().static_data.bounds.clone()
|
self.0.read().static_data.bounds.clone()
|
||||||
}
|
}
|
||||||
|
@ -269,6 +269,6 @@ struct GraphicStatic {
|
||||||
id: CharacterId,
|
id: CharacterId,
|
||||||
shape: swf::Shape,
|
shape: swf::Shape,
|
||||||
render_handle: Option<ShapeHandle>,
|
render_handle: Option<ShapeHandle>,
|
||||||
bounds: BoundingBox,
|
bounds: Rectangle<Twips>,
|
||||||
movie: Arc<SwfMovie>,
|
movie: Arc<SwfMovie>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,7 +81,7 @@ impl<'gc> TDisplayObject<'gc> for LoaderDisplay<'gc> {
|
||||||
self.render_children(context);
|
self.render_children(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,7 +108,7 @@ impl<'gc> TDisplayObject<'gc> for MorphShape<'gc> {
|
||||||
.render_shape(shape_handle, context.transform_stack.transform());
|
.render_shape(shape_handle, context.transform_stack.transform());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
let this = self.0.read();
|
let this = self.0.read();
|
||||||
let ratio = this.ratio;
|
let ratio = this.ratio;
|
||||||
let static_data = this.static_data;
|
let static_data = this.static_data;
|
||||||
|
@ -148,7 +148,7 @@ impl<'gc> TDisplayObject<'gc> for MorphShape<'gc> {
|
||||||
struct Frame {
|
struct Frame {
|
||||||
shape_handle: Option<ShapeHandle>,
|
shape_handle: Option<ShapeHandle>,
|
||||||
shape: swf::Shape,
|
shape: swf::Shape,
|
||||||
bounds: BoundingBox,
|
bounds: Rectangle<Twips>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Static data shared between all instances of a morph shape.
|
/// Static data shared between all instances of a morph shape.
|
||||||
|
@ -331,7 +331,7 @@ impl MorphShapeStatic {
|
||||||
Frame {
|
Frame {
|
||||||
shape_handle: None,
|
shape_handle: None,
|
||||||
shape,
|
shape,
|
||||||
bounds: bounds.into(),
|
bounds,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2483,8 +2483,8 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||||
self.render_children(context);
|
self.render_children(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
self.0.read().drawing.self_bounds()
|
self.0.read().drawing.self_bounds().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hit_test_shape(
|
fn hit_test_shape(
|
||||||
|
|
|
@ -106,7 +106,7 @@ pub struct StageData<'gc> {
|
||||||
|
|
||||||
/// The bounds of the current viewport in twips, used for culling.
|
/// The bounds of the current viewport in twips, used for culling.
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
view_bounds: BoundingBox,
|
view_bounds: Rectangle<Twips>,
|
||||||
|
|
||||||
/// The window mode of the viewport.
|
/// The window mode of the viewport.
|
||||||
///
|
///
|
||||||
|
@ -398,7 +398,7 @@ impl<'gc> Stage<'gc> {
|
||||||
self.0.write(context.gc_context).window_mode = window_mode;
|
self.0.write(context.gc_context).window_mode = window_mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn view_bounds(self) -> BoundingBox {
|
pub fn view_bounds(self) -> Rectangle<Twips> {
|
||||||
self.0.read().view_bounds.clone()
|
self.0.read().view_bounds.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,12 +514,11 @@ impl<'gc> Stage<'gc> {
|
||||||
|
|
||||||
self.0.write(context.gc_context).view_bounds = if self.should_letterbox() {
|
self.0.write(context.gc_context).view_bounds = if self.should_letterbox() {
|
||||||
// Letterbox: movie area
|
// Letterbox: movie area
|
||||||
BoundingBox {
|
Rectangle {
|
||||||
x_min: Twips::ZERO,
|
x_min: Twips::ZERO,
|
||||||
y_min: Twips::ZERO,
|
y_min: Twips::ZERO,
|
||||||
x_max: Twips::from_pixels(movie_width),
|
x_max: Twips::from_pixels(movie_width),
|
||||||
y_max: Twips::from_pixels(movie_height),
|
y_max: Twips::from_pixels(movie_height),
|
||||||
valid: true,
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No letterbox: full visible stage area
|
// No letterbox: full visible stage area
|
||||||
|
@ -527,12 +526,11 @@ impl<'gc> Stage<'gc> {
|
||||||
let margin_right = (width_delta - tx) / scale_x;
|
let margin_right = (width_delta - tx) / scale_x;
|
||||||
let margin_top = ty / scale_y;
|
let margin_top = ty / scale_y;
|
||||||
let margin_bottom = (height_delta - ty) / scale_y;
|
let margin_bottom = (height_delta - ty) / scale_y;
|
||||||
BoundingBox {
|
Rectangle {
|
||||||
x_min: Twips::from_pixels(-margin_left),
|
x_min: Twips::from_pixels(-margin_left),
|
||||||
y_min: Twips::from_pixels(-margin_top),
|
y_min: Twips::from_pixels(-margin_top),
|
||||||
x_max: Twips::from_pixels(movie_width + margin_right),
|
x_max: Twips::from_pixels(movie_width + margin_right),
|
||||||
y_max: Twips::from_pixels(movie_height + margin_bottom),
|
y_max: Twips::from_pixels(movie_height + margin_bottom),
|
||||||
valid: true,
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -763,7 +761,7 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> {
|
||||||
u16::MAX
|
u16::MAX
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
Default::default()
|
Default::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ impl<'gc> Text<'gc> {
|
||||||
TextStatic {
|
TextStatic {
|
||||||
swf,
|
swf,
|
||||||
id: tag.id,
|
id: tag.id,
|
||||||
bounds: (&tag.bounds).into(),
|
bounds: tag.bounds.clone(),
|
||||||
text_transform: tag.matrix.into(),
|
text_transform: tag.matrix.into(),
|
||||||
text_blocks: tag.records.clone(),
|
text_blocks: tag.records.clone(),
|
||||||
},
|
},
|
||||||
|
@ -163,7 +163,7 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
|
||||||
context.transform_stack.pop();
|
context.transform_stack.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
self.0.read().static_data.bounds.clone()
|
self.0.read().static_data.bounds.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -215,8 +215,7 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
|
||||||
matrix.invert();
|
matrix.invert();
|
||||||
let point = matrix * point;
|
let point = matrix * point;
|
||||||
let glyph_shape = glyph.as_shape();
|
let glyph_shape = glyph.as_shape();
|
||||||
let glyph_bounds: BoundingBox = (&glyph_shape.shape_bounds).into();
|
if glyph_shape.shape_bounds.contains(point)
|
||||||
if glyph_bounds.contains(point)
|
|
||||||
&& ruffle_render::shape_utils::shape_hit_test(
|
&& ruffle_render::shape_utils::shape_hit_test(
|
||||||
&glyph_shape,
|
&glyph_shape,
|
||||||
point,
|
point,
|
||||||
|
@ -281,7 +280,7 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
|
||||||
struct TextStatic {
|
struct TextStatic {
|
||||||
swf: Arc<SwfMovie>,
|
swf: Arc<SwfMovie>,
|
||||||
id: CharacterId,
|
id: CharacterId,
|
||||||
bounds: BoundingBox,
|
bounds: Rectangle<Twips>,
|
||||||
text_transform: Matrix,
|
text_transform: Matrix,
|
||||||
text_blocks: Vec<swf::TextRecord>,
|
text_blocks: Vec<swf::TextRecord>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,6 @@ use crate::vminterface::{AvmObject, Instantiator};
|
||||||
use core::fmt;
|
use core::fmt;
|
||||||
use gc_arena::{Collect, GcCell, MutationContext};
|
use gc_arena::{Collect, GcCell, MutationContext};
|
||||||
use ruffle_render::bitmap::BitmapInfo;
|
use ruffle_render::bitmap::BitmapInfo;
|
||||||
use ruffle_render::bounding_box::BoundingBox;
|
|
||||||
use ruffle_render::commands::CommandHandler;
|
use ruffle_render::commands::CommandHandler;
|
||||||
use ruffle_render::quality::StageQuality;
|
use ruffle_render::quality::StageQuality;
|
||||||
use ruffle_video::error::Error;
|
use ruffle_video::error::Error;
|
||||||
|
@ -425,17 +424,15 @@ impl<'gc> TDisplayObject<'gc> for Video<'gc> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn self_bounds(&self) -> BoundingBox {
|
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||||
let mut bounding_box = BoundingBox::default();
|
|
||||||
|
|
||||||
match (*self.0.read().source.read()).borrow() {
|
match (*self.0.read().source.read()).borrow() {
|
||||||
VideoSource::Swf { streamdef, .. } => {
|
VideoSource::Swf { streamdef, .. } => Rectangle {
|
||||||
bounding_box.set_width(Twips::from_pixels(streamdef.width as f64));
|
x_min: Twips::ZERO,
|
||||||
bounding_box.set_height(Twips::from_pixels(streamdef.height as f64));
|
y_min: Twips::ZERO,
|
||||||
}
|
x_max: Twips::from_pixels_i32(streamdef.width.into()),
|
||||||
|
y_max: Twips::from_pixels_i32(streamdef.height.into()),
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
bounding_box
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render(&self, context: &mut RenderContext) {
|
fn render(&self, context: &mut RenderContext) {
|
||||||
|
|
|
@ -2,18 +2,17 @@ use crate::context::RenderContext;
|
||||||
use gc_arena::Collect;
|
use gc_arena::Collect;
|
||||||
use ruffle_render::backend::{RenderBackend, ShapeHandle};
|
use ruffle_render::backend::{RenderBackend, ShapeHandle};
|
||||||
use ruffle_render::bitmap::{BitmapHandle, BitmapInfo, BitmapSize, BitmapSource};
|
use ruffle_render::bitmap::{BitmapHandle, BitmapInfo, BitmapSize, BitmapSource};
|
||||||
use ruffle_render::bounding_box::BoundingBox;
|
|
||||||
use ruffle_render::commands::CommandHandler;
|
use ruffle_render::commands::CommandHandler;
|
||||||
use ruffle_render::shape_utils::{DistilledShape, DrawCommand, DrawPath};
|
use ruffle_render::shape_utils::{DistilledShape, DrawCommand, DrawPath};
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use swf::{FillStyle, LineStyle, Twips};
|
use swf::{FillStyle, LineStyle, Rectangle, Twips};
|
||||||
|
|
||||||
#[derive(Clone, Debug, Collect)]
|
#[derive(Clone, Debug, Collect)]
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
pub struct Drawing {
|
pub struct Drawing {
|
||||||
render_handle: Cell<Option<ShapeHandle>>,
|
render_handle: Cell<Option<ShapeHandle>>,
|
||||||
shape_bounds: BoundingBox,
|
shape_bounds: Rectangle<Twips>,
|
||||||
edge_bounds: BoundingBox,
|
edge_bounds: Rectangle<Twips>,
|
||||||
dirty: Cell<bool>,
|
dirty: Cell<bool>,
|
||||||
paths: Vec<DrawingPath>,
|
paths: Vec<DrawingPath>,
|
||||||
bitmaps: Vec<BitmapInfo>,
|
bitmaps: Vec<BitmapInfo>,
|
||||||
|
@ -34,8 +33,8 @@ impl Drawing {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
render_handle: Cell::new(None),
|
render_handle: Cell::new(None),
|
||||||
shape_bounds: BoundingBox::default(),
|
shape_bounds: Default::default(),
|
||||||
edge_bounds: BoundingBox::default(),
|
edge_bounds: Default::default(),
|
||||||
dirty: Cell::new(false),
|
dirty: Cell::new(false),
|
||||||
paths: Vec::new(),
|
paths: Vec::new(),
|
||||||
bitmaps: Vec::new(),
|
bitmaps: Vec::new(),
|
||||||
|
@ -50,8 +49,8 @@ impl Drawing {
|
||||||
pub fn from_swf_shape(shape: &swf::Shape) -> Self {
|
pub fn from_swf_shape(shape: &swf::Shape) -> Self {
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
render_handle: Cell::new(None),
|
render_handle: Cell::new(None),
|
||||||
shape_bounds: (&shape.shape_bounds).into(),
|
shape_bounds: shape.shape_bounds.clone(),
|
||||||
edge_bounds: (&shape.edge_bounds).into(),
|
edge_bounds: shape.edge_bounds.clone(),
|
||||||
dirty: Cell::new(true),
|
dirty: Cell::new(true),
|
||||||
paths: Vec::new(),
|
paths: Vec::new(),
|
||||||
bitmaps: Vec::new(),
|
bitmaps: Vec::new(),
|
||||||
|
@ -132,8 +131,8 @@ impl Drawing {
|
||||||
self.pending_lines.clear();
|
self.pending_lines.clear();
|
||||||
self.paths.clear();
|
self.paths.clear();
|
||||||
self.bitmaps.clear();
|
self.bitmaps.clear();
|
||||||
self.edge_bounds = BoundingBox::default();
|
self.edge_bounds = Default::default();
|
||||||
self.shape_bounds = BoundingBox::default();
|
self.shape_bounds = Default::default();
|
||||||
self.dirty.set(true);
|
self.dirty.set(true);
|
||||||
self.cursor = (Twips::ZERO, Twips::ZERO);
|
self.cursor = (Twips::ZERO, Twips::ZERO);
|
||||||
self.fill_start = (Twips::ZERO, Twips::ZERO);
|
self.fill_start = (Twips::ZERO, Twips::ZERO);
|
||||||
|
@ -192,11 +191,11 @@ impl Drawing {
|
||||||
x: self.cursor.0,
|
x: self.cursor.0,
|
||||||
y: self.cursor.1,
|
y: self.cursor.1,
|
||||||
};
|
};
|
||||||
stretch_bounding_box(&mut self.shape_bounds, &command, stroke_width);
|
self.shape_bounds = stretch_bounds(&self.shape_bounds, &command, stroke_width);
|
||||||
stretch_bounding_box(&mut self.edge_bounds, &command, Twips::ZERO);
|
self.edge_bounds = stretch_bounds(&self.edge_bounds, &command, Twips::ZERO);
|
||||||
}
|
}
|
||||||
stretch_bounding_box(&mut self.shape_bounds, &command, stroke_width);
|
self.shape_bounds = stretch_bounds(&self.shape_bounds, &command, stroke_width);
|
||||||
stretch_bounding_box(&mut self.edge_bounds, &command, Twips::ZERO);
|
self.edge_bounds = stretch_bounds(&self.edge_bounds, &command, Twips::ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.cursor = command.end_point();
|
self.cursor = command.end_point();
|
||||||
|
@ -296,8 +295,8 @@ impl Drawing {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn self_bounds(&self) -> BoundingBox {
|
pub fn self_bounds(&self) -> &Rectangle<Twips> {
|
||||||
self.shape_bounds.clone()
|
&self.shape_bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hit_test(
|
pub fn hit_test(
|
||||||
|
@ -432,26 +431,24 @@ enum DrawingPath {
|
||||||
Line(DrawingLine),
|
Line(DrawingLine),
|
||||||
}
|
}
|
||||||
|
|
||||||
fn stretch_bounding_box(
|
fn stretch_bounds(
|
||||||
bounding_box: &mut BoundingBox,
|
bounds: &Rectangle<Twips>,
|
||||||
command: &DrawCommand,
|
command: &DrawCommand,
|
||||||
stroke_width: Twips,
|
stroke_width: Twips,
|
||||||
) {
|
) -> Rectangle<Twips> {
|
||||||
let radius = stroke_width / 2;
|
let radius = stroke_width / 2;
|
||||||
|
let bounds = bounds.clone();
|
||||||
match *command {
|
match *command {
|
||||||
DrawCommand::MoveTo { x, y } => {
|
DrawCommand::MoveTo { x, y } => bounds
|
||||||
bounding_box.encompass(x - radius, y - radius);
|
.encompass(x - radius, y - radius)
|
||||||
bounding_box.encompass(x + radius, y + radius);
|
.encompass(x + radius, y + radius),
|
||||||
}
|
DrawCommand::LineTo { x, y } => bounds
|
||||||
DrawCommand::LineTo { x, y } => {
|
.encompass(x - radius, y - radius)
|
||||||
bounding_box.encompass(x - radius, y - radius);
|
.encompass(x + radius, y + radius),
|
||||||
bounding_box.encompass(x + radius, y + radius);
|
DrawCommand::CurveTo { x1, y1, x2, y2 } => bounds
|
||||||
}
|
.encompass(x1 - radius, y1 - radius)
|
||||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
.encompass(x1 + radius, y1 + radius)
|
||||||
bounding_box.encompass(x1 - radius, y1 - radius);
|
.encompass(x2 - radius, y2 - radius)
|
||||||
bounding_box.encompass(x1 + radius, y1 + radius);
|
.encompass(x2 + radius, y2 + radius),
|
||||||
bounding_box.encompass(x2 - radius, y2 - radius);
|
|
||||||
bounding_box.encompass(x2 + radius, y2 + radius);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2308,7 +2308,7 @@ pub struct DragObject<'gc> {
|
||||||
|
|
||||||
/// The bounding rectangle where the clip will be maintained.
|
/// The bounding rectangle where the clip will be maintained.
|
||||||
#[collect(require_static)]
|
#[collect(require_static)]
|
||||||
pub constraint: BoundingBox,
|
pub constraint: Rectangle<Twips>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_mouse_pick<'gc>(
|
fn run_mouse_pick<'gc>(
|
||||||
|
|
|
@ -2,11 +2,10 @@ pub use crate::avm2::Value as Avm2Value;
|
||||||
pub use crate::display_object::{
|
pub use crate::display_object::{
|
||||||
DisplayObject, DisplayObjectContainer, HitTestOptions, TDisplayObject, TDisplayObjectContainer,
|
DisplayObject, DisplayObjectContainer, HitTestOptions, TDisplayObject, TDisplayObjectContainer,
|
||||||
};
|
};
|
||||||
pub use ruffle_render::bounding_box::BoundingBox;
|
|
||||||
pub use ruffle_render::color_transform::ColorTransform;
|
pub use ruffle_render::color_transform::ColorTransform;
|
||||||
pub use ruffle_render::matrix::Matrix;
|
pub use ruffle_render::matrix::Matrix;
|
||||||
pub use std::ops::{Bound, RangeBounds};
|
pub use std::ops::{Bound, RangeBounds};
|
||||||
pub use swf::{CharacterId, Color, Twips};
|
pub use swf::{CharacterId, Color, Rectangle, Twips};
|
||||||
pub use tracing::{error, info, trace, warn};
|
pub use tracing::{error, info, trace, warn};
|
||||||
|
|
||||||
/// A depth for a Flash display object in AVM1.
|
/// A depth for a Flash display object in AVM1.
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
use crate::matrix::Matrix;
|
|
||||||
use swf::Twips;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
|
||||||
pub struct BoundingBox {
|
|
||||||
pub x_min: Twips,
|
|
||||||
pub y_min: Twips,
|
|
||||||
pub x_max: Twips,
|
|
||||||
pub y_max: Twips,
|
|
||||||
pub valid: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BoundingBox {
|
|
||||||
/// Clamps the given point inside this bounding box.
|
|
||||||
pub fn clamp(&self, (x, y): (Twips, Twips)) -> (Twips, Twips) {
|
|
||||||
if self.valid {
|
|
||||||
(
|
|
||||||
x.clamp(self.x_min, self.x_max),
|
|
||||||
y.clamp(self.y_min, self.y_max),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
(x, y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn transform(&self, matrix: &Matrix) -> Self {
|
|
||||||
if !self.valid {
|
|
||||||
return Self::default();
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::cmp::{max, min};
|
|
||||||
let pt0 = *matrix * (self.x_min, self.y_min);
|
|
||||||
let pt1 = *matrix * (self.x_min, self.y_max);
|
|
||||||
let pt2 = *matrix * (self.x_max, self.y_min);
|
|
||||||
let pt3 = *matrix * (self.x_max, self.y_max);
|
|
||||||
BoundingBox {
|
|
||||||
x_min: min(pt0.0, min(pt1.0, min(pt2.0, pt3.0))),
|
|
||||||
y_min: min(pt0.1, min(pt1.1, min(pt2.1, pt3.1))),
|
|
||||||
x_max: max(pt0.0, max(pt1.0, max(pt2.0, pt3.0))),
|
|
||||||
y_max: max(pt0.1, max(pt1.1, max(pt2.1, pt3.1))),
|
|
||||||
valid: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn encompass(&mut self, x: Twips, y: Twips) {
|
|
||||||
if self.valid {
|
|
||||||
if x < self.x_min {
|
|
||||||
self.x_min = x;
|
|
||||||
}
|
|
||||||
if x > self.x_max {
|
|
||||||
self.x_max = x;
|
|
||||||
}
|
|
||||||
if y < self.y_min {
|
|
||||||
self.y_min = y;
|
|
||||||
}
|
|
||||||
if y > self.y_max {
|
|
||||||
self.y_max = y;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
self.x_min = x;
|
|
||||||
self.x_max = x;
|
|
||||||
self.y_min = y;
|
|
||||||
self.y_max = y;
|
|
||||||
self.valid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn union(&mut self, other: &BoundingBox) {
|
|
||||||
use std::cmp::{max, min};
|
|
||||||
if other.valid {
|
|
||||||
if self.valid {
|
|
||||||
self.x_min = min(self.x_min, other.x_min);
|
|
||||||
self.x_max = max(self.x_max, other.x_max);
|
|
||||||
self.y_min = min(self.y_min, other.y_min);
|
|
||||||
self.y_max = max(self.y_max, other.y_max);
|
|
||||||
} else {
|
|
||||||
*self = other.clone();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn intersects(&self, other: &BoundingBox) -> bool {
|
|
||||||
if !self.valid || !other.valid {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
use std::cmp::{max, min};
|
|
||||||
let x_min = max(self.x_min, other.x_min);
|
|
||||||
let y_min = max(self.y_min, other.y_min);
|
|
||||||
let x_max = min(self.x_max, other.x_max);
|
|
||||||
let y_max = min(self.y_max, other.y_max);
|
|
||||||
|
|
||||||
x_min <= x_max && y_min <= y_max
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn contains(&self, (x, y): (Twips, Twips)) -> bool {
|
|
||||||
self.valid && x >= self.x_min && x <= self.x_max && y >= self.y_min && y <= self.y_max
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the X coordinate to a particular value, maintaining the width of
|
|
||||||
/// this box (if possible).
|
|
||||||
pub fn set_x(&mut self, x: Twips) {
|
|
||||||
let width = self.width();
|
|
||||||
self.x_min = x;
|
|
||||||
self.x_max = x + width;
|
|
||||||
|
|
||||||
if self.y_max >= self.y_min {
|
|
||||||
self.valid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the Y coordinate to a particular value, maintaining the width of
|
|
||||||
/// this box (if possible).
|
|
||||||
pub fn set_y(&mut self, y: Twips) {
|
|
||||||
let height = self.height();
|
|
||||||
self.y_min = y;
|
|
||||||
self.y_max = y + height;
|
|
||||||
|
|
||||||
if self.x_max >= self.x_min {
|
|
||||||
self.valid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determine the width of the bounding box.
|
|
||||||
pub fn width(&self) -> Twips {
|
|
||||||
if self.valid {
|
|
||||||
self.x_max - self.x_min
|
|
||||||
} else {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adjust the width of the bounding box.
|
|
||||||
pub fn set_width(&mut self, width: Twips) {
|
|
||||||
self.x_max = self.x_min + width;
|
|
||||||
|
|
||||||
self.valid = self.x_max >= self.x_min && self.y_max >= self.y_min;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Determine the height of the bounding box.
|
|
||||||
pub fn height(&self) -> Twips {
|
|
||||||
if self.valid {
|
|
||||||
self.y_max - self.y_min
|
|
||||||
} else {
|
|
||||||
Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adjust the height of the bounding box.
|
|
||||||
pub fn set_height(&mut self, height: Twips) {
|
|
||||||
self.y_max = self.y_min + height;
|
|
||||||
|
|
||||||
self.valid = self.x_max >= self.x_min && self.y_max >= self.y_min;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<swf::Rectangle<Twips>> for BoundingBox {
|
|
||||||
fn from(rect: swf::Rectangle<Twips>) -> Self {
|
|
||||||
Self {
|
|
||||||
x_min: rect.x_min,
|
|
||||||
y_min: rect.y_min,
|
|
||||||
x_max: rect.x_max,
|
|
||||||
y_max: rect.y_max,
|
|
||||||
valid: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<&swf::Rectangle<Twips>> for BoundingBox {
|
|
||||||
fn from(rect: &swf::Rectangle<Twips>) -> Self {
|
|
||||||
Self {
|
|
||||||
x_min: rect.x_min,
|
|
||||||
y_min: rect.y_min,
|
|
||||||
x_max: rect.x_max,
|
|
||||||
y_max: rect.y_max,
|
|
||||||
valid: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod bitmap;
|
pub mod bitmap;
|
||||||
pub mod bounding_box;
|
|
||||||
pub mod color_transform;
|
pub mod color_transform;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod filters;
|
pub mod filters;
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#![allow(clippy::suspicious_operation_groupings)]
|
use swf::{Fixed16, Rectangle, Twips};
|
||||||
|
|
||||||
use swf::{Fixed16, Twips};
|
|
||||||
|
|
||||||
/// The transformation matrix used by Flash display objects.
|
/// The transformation matrix used by Flash display objects.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
@ -162,6 +160,27 @@ impl std::ops::Mul<(Twips, Twips)> for Matrix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::ops::Mul<Rectangle<Twips>> for Matrix {
|
||||||
|
type Output = Rectangle<Twips>;
|
||||||
|
|
||||||
|
fn mul(self, rhs: Rectangle<Twips>) -> Self::Output {
|
||||||
|
if !rhs.is_valid() {
|
||||||
|
return Default::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
let (x0, y0) = self * (rhs.x_min, rhs.y_min);
|
||||||
|
let (x1, y1) = self * (rhs.x_min, rhs.y_max);
|
||||||
|
let (x2, y2) = self * (rhs.x_max, rhs.y_min);
|
||||||
|
let (x3, y3) = self * (rhs.x_max, rhs.y_max);
|
||||||
|
Rectangle {
|
||||||
|
x_min: x0.min(x1).min(x2).min(x3),
|
||||||
|
x_max: x0.max(x1).max(x2).max(x3),
|
||||||
|
y_min: y0.min(y1).min(y2).min(y3),
|
||||||
|
y_max: y0.max(y1).max(y2).max(y3),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Default for Matrix {
|
impl Default for Matrix {
|
||||||
fn default() -> Matrix {
|
fn default() -> Matrix {
|
||||||
Matrix::IDENTITY
|
Matrix::IDENTITY
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
use crate::bounding_box::BoundingBox;
|
|
||||||
use crate::matrix::Matrix;
|
use crate::matrix::Matrix;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use swf::{CharacterId, FillStyle, LineStyle, Shape, ShapeRecord, Twips};
|
use swf::{CharacterId, FillStyle, LineStyle, Rectangle, Shape, ShapeRecord, Twips};
|
||||||
|
|
||||||
pub fn calculate_shape_bounds(shape_records: &[swf::ShapeRecord]) -> swf::Rectangle<Twips> {
|
pub fn calculate_shape_bounds(shape_records: &[swf::ShapeRecord]) -> swf::Rectangle<Twips> {
|
||||||
let mut bounds = swf::Rectangle {
|
let mut bounds = swf::Rectangle {
|
||||||
|
@ -80,8 +79,8 @@ pub enum DrawPath<'a> {
|
||||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct DistilledShape<'a> {
|
pub struct DistilledShape<'a> {
|
||||||
pub paths: Vec<DrawPath<'a>>,
|
pub paths: Vec<DrawPath<'a>>,
|
||||||
pub shape_bounds: BoundingBox,
|
pub shape_bounds: Rectangle<Twips>,
|
||||||
pub edge_bounds: BoundingBox,
|
pub edge_bounds: Rectangle<Twips>,
|
||||||
pub id: CharacterId,
|
pub id: CharacterId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,8 +88,8 @@ impl<'a> From<&'a swf::Shape> for DistilledShape<'a> {
|
||||||
fn from(shape: &'a Shape) -> Self {
|
fn from(shape: &'a Shape) -> Self {
|
||||||
Self {
|
Self {
|
||||||
paths: ShapeConverter::from_shape(shape).into_commands(),
|
paths: ShapeConverter::from_shape(shape).into_commands(),
|
||||||
shape_bounds: (&shape.shape_bounds).into(),
|
shape_bounds: shape.shape_bounds.clone(),
|
||||||
edge_bounds: (&shape.edge_bounds).into(),
|
edge_bounds: shape.edge_bounds.clone(),
|
||||||
id: shape.id,
|
id: shape.id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2763,7 +2763,15 @@ pub mod tests {
|
||||||
let buf = [0b00000_000];
|
let buf = [0b00000_000];
|
||||||
let mut reader = Reader::new(&buf[..], 1);
|
let mut reader = Reader::new(&buf[..], 1);
|
||||||
let rectangle = reader.read_rectangle().unwrap();
|
let rectangle = reader.read_rectangle().unwrap();
|
||||||
assert_eq!(rectangle, Default::default());
|
assert_eq!(
|
||||||
|
rectangle,
|
||||||
|
Rectangle {
|
||||||
|
x_min: Twips::ZERO,
|
||||||
|
y_min: Twips::ZERO,
|
||||||
|
x_max: Twips::ZERO,
|
||||||
|
y_max: Twips::ZERO,
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -1,11 +1,17 @@
|
||||||
use std::ops::Sub;
|
use crate::Twips;
|
||||||
|
use std::cmp::Ord;
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
pub trait Coordinate: Copy + Sub<Output = Self> {}
|
pub trait Coordinate: Copy + Ord + Add<Output = Self> + Sub<Output = Self> {
|
||||||
|
const INVALID: Self;
|
||||||
|
}
|
||||||
|
|
||||||
impl Coordinate for crate::Twips {}
|
impl Coordinate for Twips {
|
||||||
|
const INVALID: Self = Self::new(0x7ffffff);
|
||||||
|
}
|
||||||
|
|
||||||
/// A rectangular region defined by minimum and maximum x- and y-coordinate positions.
|
/// A rectangular region defined by minimum and maximum x- and y-coordinate positions.
|
||||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub struct Rectangle<T: Coordinate> {
|
pub struct Rectangle<T: Coordinate> {
|
||||||
/// The minimum x-position of the rectangle.
|
/// The minimum x-position of the rectangle.
|
||||||
pub x_min: T,
|
pub x_min: T,
|
||||||
|
@ -21,11 +27,102 @@ pub struct Rectangle<T: Coordinate> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Coordinate> Rectangle<T> {
|
impl<T: Coordinate> Rectangle<T> {
|
||||||
|
const INVALID: Self = Self {
|
||||||
|
x_min: T::INVALID,
|
||||||
|
x_max: T::INVALID,
|
||||||
|
y_min: T::INVALID,
|
||||||
|
y_max: T::INVALID,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
|
pub fn is_valid(&self) -> bool {
|
||||||
|
self.x_min != T::INVALID
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
pub fn width(&self) -> T {
|
pub fn width(&self) -> T {
|
||||||
self.x_max - self.x_min
|
self.x_max - self.x_min
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_width(&mut self, width: T) {
|
||||||
|
self.x_max = self.x_min + width;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
#[must_use]
|
||||||
pub fn height(&self) -> T {
|
pub fn height(&self) -> T {
|
||||||
self.y_max - self.y_min
|
self.y_max - self.y_min
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn set_height(&mut self, height: T) {
|
||||||
|
self.y_max = self.y_min + height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clamp a given point inside this rectangle.
|
||||||
|
#[must_use]
|
||||||
|
pub fn clamp(&self, (x, y): (T, T)) -> (T, T) {
|
||||||
|
if self.is_valid() {
|
||||||
|
(
|
||||||
|
x.clamp(self.x_min, self.x_max),
|
||||||
|
y.clamp(self.y_min, self.y_max),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn encompass(mut self, x: T, y: T) -> Self {
|
||||||
|
if self.is_valid() {
|
||||||
|
self.x_min = self.x_min.min(x);
|
||||||
|
self.x_max = self.x_max.max(x);
|
||||||
|
self.y_min = self.y_min.min(y);
|
||||||
|
self.y_max = self.y_max.max(y);
|
||||||
|
} else {
|
||||||
|
self.x_min = x;
|
||||||
|
self.x_max = x;
|
||||||
|
self.y_min = y;
|
||||||
|
self.y_max = y;
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn union(mut self, other: &Self) -> Self {
|
||||||
|
if !self.is_valid() {
|
||||||
|
other.clone()
|
||||||
|
} else {
|
||||||
|
if other.is_valid() {
|
||||||
|
self.x_min = self.x_min.min(other.x_min);
|
||||||
|
self.x_max = self.x_max.max(other.x_max);
|
||||||
|
self.y_min = self.y_min.min(other.y_min);
|
||||||
|
self.y_max = self.y_max.max(other.y_max);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn intersects(&self, other: &Self) -> bool {
|
||||||
|
self.is_valid()
|
||||||
|
&& self.x_min <= other.x_max
|
||||||
|
&& self.x_max >= other.x_min
|
||||||
|
&& self.y_min <= other.y_max
|
||||||
|
&& self.y_max >= other.y_min
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub fn contains(&self, (x, y): (T, T)) -> bool {
|
||||||
|
x >= self.x_min && x <= self.x_max && y >= self.y_min && y <= self.y_max
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Coordinate> Default for Rectangle<T> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::INVALID
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2594,18 +2594,23 @@ mod tests {
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn write_rectangle_zero() {
|
fn write_rectangle_zero() {
|
||||||
let rect: Rectangle<Twips> = Default::default();
|
let rectangle = Rectangle {
|
||||||
|
x_min: Twips::ZERO,
|
||||||
|
y_min: Twips::ZERO,
|
||||||
|
x_max: Twips::ZERO,
|
||||||
|
y_max: Twips::ZERO,
|
||||||
|
};
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
{
|
||||||
let mut writer = Writer::new(&mut buf, 1);
|
let mut writer = Writer::new(&mut buf, 1);
|
||||||
writer.write_rectangle(&rect).unwrap();
|
writer.write_rectangle(&rectangle).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(buf, [0]);
|
assert_eq!(buf, [0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn write_rectangle_signed() {
|
fn write_rectangle_signed() {
|
||||||
let rect = Rectangle {
|
let rectangle = Rectangle {
|
||||||
x_min: Twips::from_pixels(-1.0),
|
x_min: Twips::from_pixels(-1.0),
|
||||||
x_max: Twips::from_pixels(1.0),
|
x_max: Twips::from_pixels(1.0),
|
||||||
y_min: Twips::from_pixels(-1.0),
|
y_min: Twips::from_pixels(-1.0),
|
||||||
|
@ -2614,7 +2619,7 @@ mod tests {
|
||||||
let mut buf = Vec::new();
|
let mut buf = Vec::new();
|
||||||
{
|
{
|
||||||
let mut writer = Writer::new(&mut buf, 1);
|
let mut writer = Writer::new(&mut buf, 1);
|
||||||
writer.write_rectangle(&rect).unwrap();
|
writer.write_rectangle(&rectangle).unwrap();
|
||||||
}
|
}
|
||||||
assert_eq!(buf, [0b_00110_101, 0b100_01010, 0b0_101100_0, 0b_10100_000]);
|
assert_eq!(buf, [0b_00110_101, 0b100_01010, 0b0_101100_0, 0b_10100_000]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue