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 instant::Instant;
|
||||
use rand::Rng;
|
||||
use ruffle_render::bounding_box::BoundingBox;
|
||||
use smallvec::SmallVec;
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::min;
|
||||
use std::fmt;
|
||||
use swf::avm1::read::Reader;
|
||||
use swf::avm1::types::*;
|
||||
use swf::Twips;
|
||||
use swf::{Rectangle, Twips};
|
||||
use url::form_urlencoded;
|
||||
|
||||
use super::object_reference::MovieClipReference;
|
||||
|
@ -3067,8 +3066,7 @@ pub fn start_drag<'gc>(
|
|||
if y_max.get() < y_min.get() {
|
||||
std::mem::swap(&mut y_min, &mut y_max);
|
||||
}
|
||||
BoundingBox {
|
||||
valid: true,
|
||||
Rectangle {
|
||||
x_min,
|
||||
y_min,
|
||||
x_max,
|
||||
|
|
|
@ -1205,8 +1205,7 @@ fn get_bounds<'gc>(
|
|||
// the final matrix, but this matches Flash's behavior.
|
||||
let to_global_matrix = movie_clip.local_to_global_matrix();
|
||||
let to_target_matrix = target.global_to_local_matrix();
|
||||
let bounds_transform = to_target_matrix * to_global_matrix;
|
||||
bounds.transform(&bounds_transform)
|
||||
to_target_matrix * to_global_matrix * bounds
|
||||
};
|
||||
|
||||
let out = ScriptObject::new(
|
||||
|
|
|
@ -18,8 +18,7 @@ use crate::vminterface::Instantiator;
|
|||
use crate::{avm2_stub_getter, avm2_stub_setter};
|
||||
use ruffle_render::filters::Filter;
|
||||
use std::str::FromStr;
|
||||
use swf::Twips;
|
||||
use swf::{BlendMode, Rectangle};
|
||||
use swf::BlendMode;
|
||||
|
||||
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>(
|
||||
activation: &mut Activation<'_, 'gc>,
|
||||
rectangle: BoundingBox,
|
||||
rectangle: Rectangle<Twips>,
|
||||
) -> Result<Object<'gc>, Error<'gc>> {
|
||||
let x = rectangle.x_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>> {
|
||||
if let Some(dobj) = this.and_then(|this| this.as_display_object()) {
|
||||
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 {
|
||||
return Ok(Value::Null);
|
||||
}
|
||||
|
@ -993,8 +992,7 @@ pub fn get_bounds<'gc>(
|
|||
// the final matrix, but this matches Flash's behavior.
|
||||
let to_global_matrix = dobj.local_to_global_matrix();
|
||||
let to_target_matrix = target.global_to_local_matrix();
|
||||
let bounds_transform = to_target_matrix * to_global_matrix;
|
||||
bounds.transform(&bounds_transform)
|
||||
to_target_matrix * to_global_matrix * bounds
|
||||
};
|
||||
|
||||
return Ok(new_rectangle(activation, out_bounds)?.into());
|
||||
|
|
|
@ -7,9 +7,8 @@ use crate::avm2::Error;
|
|||
use crate::avm2::Multiname;
|
||||
use crate::display_object::{MovieClip, SoundTransform, TDisplayObject};
|
||||
use crate::tag_utils::SwfMovie;
|
||||
use ruffle_render::bounding_box::BoundingBox;
|
||||
use std::sync::Arc;
|
||||
use swf::Twips;
|
||||
use swf::{Rectangle, Twips};
|
||||
|
||||
/// Implements `flash.display.Sprite`'s `init` method, which is called from the constructor
|
||||
pub fn init<'gc>(
|
||||
|
@ -215,8 +214,7 @@ pub fn start_drag<'gc>(
|
|||
std::mem::swap(&mut y_min, &mut y_max);
|
||||
}
|
||||
|
||||
BoundingBox {
|
||||
valid: true,
|
||||
Rectangle {
|
||||
x_min,
|
||||
y_min,
|
||||
x_max,
|
||||
|
|
|
@ -18,7 +18,7 @@ use ruffle_render::transform::Transform;
|
|||
use std::cell::{Ref, RefMut};
|
||||
use std::fmt::Debug;
|
||||
use std::sync::Arc;
|
||||
use swf::{BlendMode, Fixed8, Rectangle};
|
||||
use swf::{BlendMode, Fixed8};
|
||||
|
||||
mod avm1_button;
|
||||
mod avm2_button;
|
||||
|
@ -648,20 +648,20 @@ pub trait TDisplayObject<'gc>:
|
|||
/// Implementors must override this method.
|
||||
/// Leaf DisplayObjects should return their bounds.
|
||||
/// 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.
|
||||
fn bounds(&self) -> BoundingBox {
|
||||
fn bounds(&self) -> Rectangle<Twips> {
|
||||
self.bounds_with_transform(&Matrix::default())
|
||||
}
|
||||
|
||||
/// 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())
|
||||
}
|
||||
|
||||
/// 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())
|
||||
}
|
||||
|
||||
|
@ -669,26 +669,25 @@ pub trait TDisplayObject<'gc>:
|
|||
/// 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
|
||||
/// 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,
|
||||
// and can even the bounding box to be larger than the actual content
|
||||
if let Some(scroll_rect) = self.scroll_rect() {
|
||||
return BoundingBox {
|
||||
x_min: Twips::from_pixels(0.0),
|
||||
y_min: Twips::from_pixels(0.0),
|
||||
x_max: scroll_rect.width(),
|
||||
y_max: scroll_rect.height(),
|
||||
valid: true,
|
||||
}
|
||||
.transform(matrix);
|
||||
return *matrix
|
||||
* Rectangle {
|
||||
x_min: Twips::ZERO,
|
||||
y_min: Twips::ZERO,
|
||||
x_max: scroll_rect.width(),
|
||||
y_max: scroll_rect.height(),
|
||||
};
|
||||
}
|
||||
|
||||
let mut bounds = self.self_bounds().transform(matrix);
|
||||
let mut bounds = *matrix * self.self_bounds();
|
||||
|
||||
if let Some(ctr) = self.as_container() {
|
||||
for child in ctr.iter_render_list() {
|
||||
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);
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||
// No inherent bounds; contains child DisplayObjects.
|
||||
BoundingBox::default()
|
||||
Default::default()
|
||||
}
|
||||
|
||||
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.
|
||||
BoundingBox::default()
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn bounds_with_transform(&self, matrix: &Matrix) -> BoundingBox {
|
||||
fn bounds_with_transform(&self, matrix: &Matrix) -> Rectangle<Twips> {
|
||||
// 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
|
||||
let state = self.0.read().state;
|
||||
if let Some(child) = self.get_state_child(state.into()) {
|
||||
let child_bounds = child.bounds_with_transform(matrix);
|
||||
bounds.union(&child_bounds);
|
||||
bounds = bounds.union(&child_bounds);
|
||||
}
|
||||
|
||||
bounds
|
||||
|
|
|
@ -242,13 +242,12 @@ impl<'gc> TDisplayObject<'gc> for Bitmap<'gc> {
|
|||
self.0.read().id
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
BoundingBox {
|
||||
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||
Rectangle {
|
||||
x_min: Twips::ZERO,
|
||||
y_min: Twips::ZERO,
|
||||
x_max: Twips::from_pixels(Bitmap::width(*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.
|
||||
#[collect(require_static)]
|
||||
bounds: BoundingBox,
|
||||
bounds: Rectangle<Twips>,
|
||||
|
||||
/// The AVM1 object handle
|
||||
object: Option<AvmObject<'gc>>,
|
||||
|
@ -219,13 +219,11 @@ impl<'gc> EditText<'gc> {
|
|||
AutoSizeMode::None
|
||||
};
|
||||
|
||||
let bounds: BoundingBox = swf_tag.bounds().into();
|
||||
|
||||
let (layout, intrinsic_bounds) = LayoutBox::lower_from_text_spans(
|
||||
&text_spans,
|
||||
context,
|
||||
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.use_outlines(),
|
||||
);
|
||||
|
@ -233,8 +231,8 @@ impl<'gc> EditText<'gc> {
|
|||
|
||||
let mut base = InteractiveObjectBase::default();
|
||||
|
||||
base.base.matrix_mut().tx = bounds.x_min;
|
||||
base.base.matrix_mut().ty = bounds.y_min;
|
||||
base.base.matrix_mut().tx = swf_tag.bounds().x_min;
|
||||
base.base.matrix_mut().ty = swf_tag.bounds().y_min;
|
||||
|
||||
let variable = if !swf_tag.variable_name().is_empty() {
|
||||
Some(swf_tag.variable_name())
|
||||
|
@ -275,7 +273,7 @@ impl<'gc> EditText<'gc> {
|
|||
object: None,
|
||||
layout,
|
||||
intrinsic_bounds,
|
||||
bounds,
|
||||
bounds: swf_tag.bounds().clone(),
|
||||
autosize,
|
||||
variable: variable.map(|s| s.to_string_lossy(encoding)),
|
||||
bound_stage_object: None,
|
||||
|
@ -735,7 +733,7 @@ impl<'gc> EditText<'gc> {
|
|||
AutoSizeMode::Right => edit_text.bounds.x_max - width,
|
||||
AutoSizeMode::None => unreachable!(),
|
||||
};
|
||||
edit_text.bounds.set_x(new_x);
|
||||
edit_text.bounds.x_min = new_x;
|
||||
edit_text.bounds.set_width(width);
|
||||
} else {
|
||||
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());
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||
self.0.read().bounds.clone()
|
||||
}
|
||||
|
||||
|
@ -1607,9 +1605,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
|||
|
||||
fn width(&self) -> f64 {
|
||||
let edit_text = self.0.read();
|
||||
edit_text
|
||||
.bounds
|
||||
.transform(&edit_text.base.base.transform.matrix)
|
||||
(edit_text.base.base.transform.matrix * edit_text.bounds.clone())
|
||||
.width()
|
||||
.to_pixels()
|
||||
}
|
||||
|
@ -1626,9 +1622,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
|||
|
||||
fn height(&self) -> f64 {
|
||||
let edit_text = self.0.read();
|
||||
edit_text
|
||||
.bounds
|
||||
.transform(&edit_text.base.base.transform.matrix)
|
||||
(edit_text.base.base.transform.matrix * edit_text.bounds.clone())
|
||||
.height()
|
||||
.to_pixels()
|
||||
}
|
||||
|
|
|
@ -47,7 +47,7 @@ impl<'gc> Graphic<'gc> {
|
|||
let library = context.library.library_for_movie(movie.clone()).unwrap();
|
||||
let static_data = GraphicStatic {
|
||||
id: swf_shape.id,
|
||||
bounds: (&swf_shape.shape_bounds).into(),
|
||||
bounds: swf_shape.shape_bounds.clone(),
|
||||
render_handle: Some(context.renderer.register_shape(
|
||||
(&swf_shape).into(),
|
||||
&MovieLibrarySource {
|
||||
|
@ -134,9 +134,9 @@ impl<'gc> TDisplayObject<'gc> for Graphic<'gc> {
|
|||
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 {
|
||||
drawing.self_bounds()
|
||||
drawing.self_bounds().clone()
|
||||
} else {
|
||||
self.0.read().static_data.bounds.clone()
|
||||
}
|
||||
|
@ -269,6 +269,6 @@ struct GraphicStatic {
|
|||
id: CharacterId,
|
||||
shape: swf::Shape,
|
||||
render_handle: Option<ShapeHandle>,
|
||||
bounds: BoundingBox,
|
||||
bounds: Rectangle<Twips>,
|
||||
movie: Arc<SwfMovie>,
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ impl<'gc> TDisplayObject<'gc> for LoaderDisplay<'gc> {
|
|||
self.render_children(context);
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
|
|
|
@ -108,7 +108,7 @@ impl<'gc> TDisplayObject<'gc> for MorphShape<'gc> {
|
|||
.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 ratio = this.ratio;
|
||||
let static_data = this.static_data;
|
||||
|
@ -148,7 +148,7 @@ impl<'gc> TDisplayObject<'gc> for MorphShape<'gc> {
|
|||
struct Frame {
|
||||
shape_handle: Option<ShapeHandle>,
|
||||
shape: swf::Shape,
|
||||
bounds: BoundingBox,
|
||||
bounds: Rectangle<Twips>,
|
||||
}
|
||||
|
||||
/// Static data shared between all instances of a morph shape.
|
||||
|
@ -331,7 +331,7 @@ impl MorphShapeStatic {
|
|||
Frame {
|
||||
shape_handle: None,
|
||||
shape,
|
||||
bounds: bounds.into(),
|
||||
bounds,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2483,8 +2483,8 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
self.render_children(context);
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
self.0.read().drawing.self_bounds()
|
||||
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||
self.0.read().drawing.self_bounds().clone()
|
||||
}
|
||||
|
||||
fn hit_test_shape(
|
||||
|
|
|
@ -106,7 +106,7 @@ pub struct StageData<'gc> {
|
|||
|
||||
/// The bounds of the current viewport in twips, used for culling.
|
||||
#[collect(require_static)]
|
||||
view_bounds: BoundingBox,
|
||||
view_bounds: Rectangle<Twips>,
|
||||
|
||||
/// The window mode of the viewport.
|
||||
///
|
||||
|
@ -398,7 +398,7 @@ impl<'gc> Stage<'gc> {
|
|||
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()
|
||||
}
|
||||
|
||||
|
@ -514,12 +514,11 @@ impl<'gc> Stage<'gc> {
|
|||
|
||||
self.0.write(context.gc_context).view_bounds = if self.should_letterbox() {
|
||||
// Letterbox: movie area
|
||||
BoundingBox {
|
||||
Rectangle {
|
||||
x_min: Twips::ZERO,
|
||||
y_min: Twips::ZERO,
|
||||
x_max: Twips::from_pixels(movie_width),
|
||||
y_max: Twips::from_pixels(movie_height),
|
||||
valid: true,
|
||||
}
|
||||
} else {
|
||||
// No letterbox: full visible stage area
|
||||
|
@ -527,12 +526,11 @@ impl<'gc> Stage<'gc> {
|
|||
let margin_right = (width_delta - tx) / scale_x;
|
||||
let margin_top = ty / scale_y;
|
||||
let margin_bottom = (height_delta - ty) / scale_y;
|
||||
BoundingBox {
|
||||
Rectangle {
|
||||
x_min: Twips::from_pixels(-margin_left),
|
||||
y_min: Twips::from_pixels(-margin_top),
|
||||
x_max: Twips::from_pixels(movie_width + margin_right),
|
||||
y_max: Twips::from_pixels(movie_height + margin_bottom),
|
||||
valid: true,
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -763,7 +761,7 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> {
|
|||
u16::MAX
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,7 @@ impl<'gc> Text<'gc> {
|
|||
TextStatic {
|
||||
swf,
|
||||
id: tag.id,
|
||||
bounds: (&tag.bounds).into(),
|
||||
bounds: tag.bounds.clone(),
|
||||
text_transform: tag.matrix.into(),
|
||||
text_blocks: tag.records.clone(),
|
||||
},
|
||||
|
@ -163,7 +163,7 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
|
|||
context.transform_stack.pop();
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||
self.0.read().static_data.bounds.clone()
|
||||
}
|
||||
|
||||
|
@ -215,8 +215,7 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
|
|||
matrix.invert();
|
||||
let point = matrix * point;
|
||||
let glyph_shape = glyph.as_shape();
|
||||
let glyph_bounds: BoundingBox = (&glyph_shape.shape_bounds).into();
|
||||
if glyph_bounds.contains(point)
|
||||
if glyph_shape.shape_bounds.contains(point)
|
||||
&& ruffle_render::shape_utils::shape_hit_test(
|
||||
&glyph_shape,
|
||||
point,
|
||||
|
@ -281,7 +280,7 @@ impl<'gc> TDisplayObject<'gc> for Text<'gc> {
|
|||
struct TextStatic {
|
||||
swf: Arc<SwfMovie>,
|
||||
id: CharacterId,
|
||||
bounds: BoundingBox,
|
||||
bounds: Rectangle<Twips>,
|
||||
text_transform: Matrix,
|
||||
text_blocks: Vec<swf::TextRecord>,
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ use crate::vminterface::{AvmObject, Instantiator};
|
|||
use core::fmt;
|
||||
use gc_arena::{Collect, GcCell, MutationContext};
|
||||
use ruffle_render::bitmap::BitmapInfo;
|
||||
use ruffle_render::bounding_box::BoundingBox;
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use ruffle_render::quality::StageQuality;
|
||||
use ruffle_video::error::Error;
|
||||
|
@ -425,17 +424,15 @@ impl<'gc> TDisplayObject<'gc> for Video<'gc> {
|
|||
}
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
let mut bounding_box = BoundingBox::default();
|
||||
|
||||
fn self_bounds(&self) -> Rectangle<Twips> {
|
||||
match (*self.0.read().source.read()).borrow() {
|
||||
VideoSource::Swf { streamdef, .. } => {
|
||||
bounding_box.set_width(Twips::from_pixels(streamdef.width as f64));
|
||||
bounding_box.set_height(Twips::from_pixels(streamdef.height as f64));
|
||||
}
|
||||
VideoSource::Swf { streamdef, .. } => Rectangle {
|
||||
x_min: Twips::ZERO,
|
||||
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) {
|
||||
|
|
|
@ -2,18 +2,17 @@ use crate::context::RenderContext;
|
|||
use gc_arena::Collect;
|
||||
use ruffle_render::backend::{RenderBackend, ShapeHandle};
|
||||
use ruffle_render::bitmap::{BitmapHandle, BitmapInfo, BitmapSize, BitmapSource};
|
||||
use ruffle_render::bounding_box::BoundingBox;
|
||||
use ruffle_render::commands::CommandHandler;
|
||||
use ruffle_render::shape_utils::{DistilledShape, DrawCommand, DrawPath};
|
||||
use std::cell::Cell;
|
||||
use swf::{FillStyle, LineStyle, Twips};
|
||||
use swf::{FillStyle, LineStyle, Rectangle, Twips};
|
||||
|
||||
#[derive(Clone, Debug, Collect)]
|
||||
#[collect(require_static)]
|
||||
pub struct Drawing {
|
||||
render_handle: Cell<Option<ShapeHandle>>,
|
||||
shape_bounds: BoundingBox,
|
||||
edge_bounds: BoundingBox,
|
||||
shape_bounds: Rectangle<Twips>,
|
||||
edge_bounds: Rectangle<Twips>,
|
||||
dirty: Cell<bool>,
|
||||
paths: Vec<DrawingPath>,
|
||||
bitmaps: Vec<BitmapInfo>,
|
||||
|
@ -34,8 +33,8 @@ impl Drawing {
|
|||
pub fn new() -> Self {
|
||||
Self {
|
||||
render_handle: Cell::new(None),
|
||||
shape_bounds: BoundingBox::default(),
|
||||
edge_bounds: BoundingBox::default(),
|
||||
shape_bounds: Default::default(),
|
||||
edge_bounds: Default::default(),
|
||||
dirty: Cell::new(false),
|
||||
paths: Vec::new(),
|
||||
bitmaps: Vec::new(),
|
||||
|
@ -50,8 +49,8 @@ impl Drawing {
|
|||
pub fn from_swf_shape(shape: &swf::Shape) -> Self {
|
||||
let mut this = Self {
|
||||
render_handle: Cell::new(None),
|
||||
shape_bounds: (&shape.shape_bounds).into(),
|
||||
edge_bounds: (&shape.edge_bounds).into(),
|
||||
shape_bounds: shape.shape_bounds.clone(),
|
||||
edge_bounds: shape.edge_bounds.clone(),
|
||||
dirty: Cell::new(true),
|
||||
paths: Vec::new(),
|
||||
bitmaps: Vec::new(),
|
||||
|
@ -132,8 +131,8 @@ impl Drawing {
|
|||
self.pending_lines.clear();
|
||||
self.paths.clear();
|
||||
self.bitmaps.clear();
|
||||
self.edge_bounds = BoundingBox::default();
|
||||
self.shape_bounds = BoundingBox::default();
|
||||
self.edge_bounds = Default::default();
|
||||
self.shape_bounds = Default::default();
|
||||
self.dirty.set(true);
|
||||
self.cursor = (Twips::ZERO, Twips::ZERO);
|
||||
self.fill_start = (Twips::ZERO, Twips::ZERO);
|
||||
|
@ -192,11 +191,11 @@ impl Drawing {
|
|||
x: self.cursor.0,
|
||||
y: self.cursor.1,
|
||||
};
|
||||
stretch_bounding_box(&mut self.shape_bounds, &command, stroke_width);
|
||||
stretch_bounding_box(&mut self.edge_bounds, &command, Twips::ZERO);
|
||||
self.shape_bounds = stretch_bounds(&self.shape_bounds, &command, stroke_width);
|
||||
self.edge_bounds = stretch_bounds(&self.edge_bounds, &command, Twips::ZERO);
|
||||
}
|
||||
stretch_bounding_box(&mut self.shape_bounds, &command, stroke_width);
|
||||
stretch_bounding_box(&mut self.edge_bounds, &command, Twips::ZERO);
|
||||
self.shape_bounds = stretch_bounds(&self.shape_bounds, &command, stroke_width);
|
||||
self.edge_bounds = stretch_bounds(&self.edge_bounds, &command, Twips::ZERO);
|
||||
}
|
||||
|
||||
self.cursor = command.end_point();
|
||||
|
@ -296,8 +295,8 @@ impl Drawing {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn self_bounds(&self) -> BoundingBox {
|
||||
self.shape_bounds.clone()
|
||||
pub fn self_bounds(&self) -> &Rectangle<Twips> {
|
||||
&self.shape_bounds
|
||||
}
|
||||
|
||||
pub fn hit_test(
|
||||
|
@ -432,26 +431,24 @@ enum DrawingPath {
|
|||
Line(DrawingLine),
|
||||
}
|
||||
|
||||
fn stretch_bounding_box(
|
||||
bounding_box: &mut BoundingBox,
|
||||
fn stretch_bounds(
|
||||
bounds: &Rectangle<Twips>,
|
||||
command: &DrawCommand,
|
||||
stroke_width: Twips,
|
||||
) {
|
||||
) -> Rectangle<Twips> {
|
||||
let radius = stroke_width / 2;
|
||||
let bounds = bounds.clone();
|
||||
match *command {
|
||||
DrawCommand::MoveTo { x, y } => {
|
||||
bounding_box.encompass(x - radius, y - radius);
|
||||
bounding_box.encompass(x + radius, y + radius);
|
||||
}
|
||||
DrawCommand::LineTo { x, y } => {
|
||||
bounding_box.encompass(x - radius, y - radius);
|
||||
bounding_box.encompass(x + radius, y + radius);
|
||||
}
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
||||
bounding_box.encompass(x1 - radius, y1 - radius);
|
||||
bounding_box.encompass(x1 + radius, y1 + radius);
|
||||
bounding_box.encompass(x2 - radius, y2 - radius);
|
||||
bounding_box.encompass(x2 + radius, y2 + radius);
|
||||
}
|
||||
DrawCommand::MoveTo { x, y } => bounds
|
||||
.encompass(x - radius, y - radius)
|
||||
.encompass(x + radius, y + radius),
|
||||
DrawCommand::LineTo { x, y } => bounds
|
||||
.encompass(x - radius, y - radius)
|
||||
.encompass(x + radius, y + radius),
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => bounds
|
||||
.encompass(x1 - radius, y1 - radius)
|
||||
.encompass(x1 + radius, y1 + radius)
|
||||
.encompass(x2 - radius, y2 - radius)
|
||||
.encompass(x2 + radius, y2 + radius),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2308,7 +2308,7 @@ pub struct DragObject<'gc> {
|
|||
|
||||
/// The bounding rectangle where the clip will be maintained.
|
||||
#[collect(require_static)]
|
||||
pub constraint: BoundingBox,
|
||||
pub constraint: Rectangle<Twips>,
|
||||
}
|
||||
|
||||
fn run_mouse_pick<'gc>(
|
||||
|
|
|
@ -2,11 +2,10 @@ pub use crate::avm2::Value as Avm2Value;
|
|||
pub use crate::display_object::{
|
||||
DisplayObject, DisplayObjectContainer, HitTestOptions, TDisplayObject, TDisplayObjectContainer,
|
||||
};
|
||||
pub use ruffle_render::bounding_box::BoundingBox;
|
||||
pub use ruffle_render::color_transform::ColorTransform;
|
||||
pub use ruffle_render::matrix::Matrix;
|
||||
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};
|
||||
|
||||
/// 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 bitmap;
|
||||
pub mod bounding_box;
|
||||
pub mod color_transform;
|
||||
pub mod error;
|
||||
pub mod filters;
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
#![allow(clippy::suspicious_operation_groupings)]
|
||||
|
||||
use swf::{Fixed16, Twips};
|
||||
use swf::{Fixed16, Rectangle, Twips};
|
||||
|
||||
/// The transformation matrix used by Flash display objects.
|
||||
#[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 {
|
||||
fn default() -> Matrix {
|
||||
Matrix::IDENTITY
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::bounding_box::BoundingBox;
|
||||
use crate::matrix::Matrix;
|
||||
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> {
|
||||
let mut bounds = swf::Rectangle {
|
||||
|
@ -80,8 +79,8 @@ pub enum DrawPath<'a> {
|
|||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct DistilledShape<'a> {
|
||||
pub paths: Vec<DrawPath<'a>>,
|
||||
pub shape_bounds: BoundingBox,
|
||||
pub edge_bounds: BoundingBox,
|
||||
pub shape_bounds: Rectangle<Twips>,
|
||||
pub edge_bounds: Rectangle<Twips>,
|
||||
pub id: CharacterId,
|
||||
}
|
||||
|
||||
|
@ -89,8 +88,8 @@ impl<'a> From<&'a swf::Shape> for DistilledShape<'a> {
|
|||
fn from(shape: &'a Shape) -> Self {
|
||||
Self {
|
||||
paths: ShapeConverter::from_shape(shape).into_commands(),
|
||||
shape_bounds: (&shape.shape_bounds).into(),
|
||||
edge_bounds: (&shape.edge_bounds).into(),
|
||||
shape_bounds: shape.shape_bounds.clone(),
|
||||
edge_bounds: shape.edge_bounds.clone(),
|
||||
id: shape.id,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2763,7 +2763,15 @@ pub mod tests {
|
|||
let buf = [0b00000_000];
|
||||
let mut reader = Reader::new(&buf[..], 1);
|
||||
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]
|
||||
|
|
|
@ -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.
|
||||
#[derive(Clone, Debug, Default, Eq, PartialEq)]
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub struct Rectangle<T: Coordinate> {
|
||||
/// The minimum x-position of the rectangle.
|
||||
pub x_min: T,
|
||||
|
@ -21,11 +27,102 @@ pub struct Rectangle<T: Coordinate> {
|
|||
}
|
||||
|
||||
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 {
|
||||
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 {
|
||||
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]
|
||||
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 writer = Writer::new(&mut buf, 1);
|
||||
writer.write_rectangle(&rect).unwrap();
|
||||
writer.write_rectangle(&rectangle).unwrap();
|
||||
}
|
||||
assert_eq!(buf, [0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn write_rectangle_signed() {
|
||||
let rect = Rectangle {
|
||||
let rectangle = Rectangle {
|
||||
x_min: Twips::from_pixels(-1.0),
|
||||
x_max: 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 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]);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue