core: First pass of drawing API (merge #611)
This commit is contained in:
commit
97db518fb1
|
@ -2172,6 +2172,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
name = "swf"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"approx 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"byteorder 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"enumset 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"flate2 1.0.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -17,6 +17,7 @@ pub(crate) mod display_object;
|
|||
mod function;
|
||||
mod key;
|
||||
mod math;
|
||||
mod matrix;
|
||||
pub(crate) mod mouse;
|
||||
pub(crate) mod movie_clip;
|
||||
mod movie_clip_loader;
|
||||
|
@ -127,6 +128,7 @@ pub struct SystemPrototypes<'gc> {
|
|||
pub string: Object<'gc>,
|
||||
pub number: Object<'gc>,
|
||||
pub boolean: Object<'gc>,
|
||||
pub matrix: Object<'gc>,
|
||||
}
|
||||
|
||||
unsafe impl<'gc> gc_arena::Collect for SystemPrototypes<'gc> {
|
||||
|
@ -144,6 +146,7 @@ unsafe impl<'gc> gc_arena::Collect for SystemPrototypes<'gc> {
|
|||
self.string.trace(cc);
|
||||
self.number.trace(cc);
|
||||
self.boolean.trace(cc);
|
||||
self.matrix.trace(cc);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -183,6 +186,7 @@ pub fn create_globals<'gc>(
|
|||
let number_proto: Object<'gc> = number::create_proto(gc_context, object_proto, function_proto);
|
||||
let boolean_proto: Object<'gc> =
|
||||
boolean::create_proto(gc_context, object_proto, function_proto);
|
||||
let matrix_proto: Object<'gc> = matrix::create_proto(gc_context, object_proto, function_proto);
|
||||
|
||||
//TODO: These need to be constructors and should also set `.prototype` on each one
|
||||
let object = object::create_object_object(gc_context, object_proto, function_proto);
|
||||
|
@ -253,9 +257,17 @@ pub fn create_globals<'gc>(
|
|||
let boolean =
|
||||
boolean::create_boolean_object(gc_context, Some(boolean_proto), Some(function_proto));
|
||||
|
||||
let flash = ScriptObject::object(gc_context, Some(object_proto));
|
||||
let geom = ScriptObject::object(gc_context, Some(object_proto));
|
||||
let matrix = matrix::create_matrix_object(gc_context, Some(matrix_proto), Some(function_proto));
|
||||
|
||||
flash.define_value(gc_context, "geom", geom.into(), EnumSet::empty());
|
||||
geom.define_value(gc_context, "Matrix", matrix.into(), EnumSet::empty());
|
||||
|
||||
let listeners = SystemListeners::new(gc_context, Some(array_proto));
|
||||
|
||||
let mut globals = ScriptObject::bare_object(gc_context);
|
||||
globals.define_value(gc_context, "flash", flash.into(), EnumSet::empty());
|
||||
globals.define_value(gc_context, "Array", array.into(), EnumSet::empty());
|
||||
globals.define_value(gc_context, "Button", button.into(), EnumSet::empty());
|
||||
globals.define_value(gc_context, "Color", color.into(), EnumSet::empty());
|
||||
|
@ -381,6 +393,7 @@ pub fn create_globals<'gc>(
|
|||
string: string_proto,
|
||||
number: number_proto,
|
||||
boolean: boolean_proto,
|
||||
matrix: matrix_proto,
|
||||
},
|
||||
globals.into(),
|
||||
listeners,
|
||||
|
|
|
@ -0,0 +1,498 @@
|
|||
//! flash.geom.Matrix
|
||||
|
||||
use crate::avm1::function::{Executable, FunctionObject};
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, Object, ScriptObject, TObject, Value};
|
||||
use crate::context::UpdateContext;
|
||||
use enumset::EnumSet;
|
||||
use gc_arena::MutationContext;
|
||||
use swf::{Matrix, Twips};
|
||||
|
||||
pub fn value_to_matrix<'gc>(
|
||||
value: Value<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<Matrix, Error> {
|
||||
object_to_matrix(value.as_object()?, avm, context)
|
||||
}
|
||||
|
||||
pub fn gradient_object_to_matrix<'gc>(
|
||||
object: Object<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<Matrix, Error> {
|
||||
if object
|
||||
.get("matrixType", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.coerce_to_string(avm, context)?
|
||||
== "box"
|
||||
{
|
||||
let width = object
|
||||
.get("w", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?;
|
||||
let height = object
|
||||
.get("h", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?;
|
||||
let rotation = object
|
||||
.get("r", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?;
|
||||
let tx = object
|
||||
.get("x", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?;
|
||||
let ty = object
|
||||
.get("y", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?;
|
||||
Ok(Matrix::create_gradient_box(
|
||||
width as f32,
|
||||
height as f32,
|
||||
rotation as f32,
|
||||
Twips::from_pixels(tx),
|
||||
Twips::from_pixels(ty),
|
||||
))
|
||||
} else {
|
||||
// TODO: You can apparently pass a 3x3 matrix here. Did anybody actually? How does it work?
|
||||
object_to_matrix(object, avm, context)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn object_to_matrix<'gc>(
|
||||
object: Object<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<Matrix, Error> {
|
||||
let a = object
|
||||
.get("a", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)? as f32;
|
||||
let b = object
|
||||
.get("b", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)? as f32;
|
||||
let c = object
|
||||
.get("c", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)? as f32;
|
||||
let d = object
|
||||
.get("d", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)? as f32;
|
||||
let tx = Twips::from_pixels(
|
||||
object
|
||||
.get("tx", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?,
|
||||
);
|
||||
let ty = Twips::from_pixels(
|
||||
object
|
||||
.get("ty", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?,
|
||||
);
|
||||
|
||||
Ok(Matrix { a, b, c, d, tx, ty })
|
||||
}
|
||||
|
||||
// We'll need this soon!
|
||||
#[allow(dead_code)]
|
||||
pub fn matrix_to_object<'gc>(
|
||||
matrix: Matrix,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<Object<'gc>, Error> {
|
||||
let proto = context.system_prototypes.matrix;
|
||||
let args = [
|
||||
matrix.a.into(),
|
||||
matrix.b.into(),
|
||||
matrix.c.into(),
|
||||
matrix.d.into(),
|
||||
matrix.tx.to_pixels().into(),
|
||||
matrix.ty.to_pixels().into(),
|
||||
];
|
||||
let object = proto.new(avm, context, proto, &args)?;
|
||||
let _ = constructor(avm, context, object, &args)?;
|
||||
Ok(object)
|
||||
}
|
||||
|
||||
pub fn apply_matrix_to_object<'gc>(
|
||||
matrix: Matrix,
|
||||
object: Object<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
) -> Result<(), Error> {
|
||||
object.set("a", matrix.a.into(), avm, context)?;
|
||||
object.set("b", matrix.b.into(), avm, context)?;
|
||||
object.set("c", matrix.c.into(), avm, context)?;
|
||||
object.set("d", matrix.d.into(), avm, context)?;
|
||||
object.set("tx", matrix.tx.to_pixels().into(), avm, context)?;
|
||||
object.set("ty", matrix.ty.to_pixels().into(), avm, context)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn constructor<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if args.is_empty() {
|
||||
apply_matrix_to_object(Matrix::identity(), this, avm, context)?;
|
||||
} else {
|
||||
if let Some(a) = args.get(0) {
|
||||
this.set("a", a.clone(), avm, context)?;
|
||||
}
|
||||
if let Some(b) = args.get(1) {
|
||||
this.set("b", b.clone(), avm, context)?;
|
||||
}
|
||||
if let Some(c) = args.get(2) {
|
||||
this.set("c", c.clone(), avm, context)?;
|
||||
}
|
||||
if let Some(d) = args.get(3) {
|
||||
this.set("d", d.clone(), avm, context)?;
|
||||
}
|
||||
if let Some(tx) = args.get(4) {
|
||||
this.set("tx", tx.clone(), avm, context)?;
|
||||
}
|
||||
if let Some(ty) = args.get(5) {
|
||||
this.set("ty", ty.clone(), avm, context)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn identity<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
apply_matrix_to_object(Matrix::identity(), this, avm, context)?;
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn clone<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let proto = context.system_prototypes.matrix;
|
||||
let args = [
|
||||
this.get("a", avm, context)?.resolve(avm, context)?,
|
||||
this.get("b", avm, context)?.resolve(avm, context)?,
|
||||
this.get("c", avm, context)?.resolve(avm, context)?,
|
||||
this.get("d", avm, context)?.resolve(avm, context)?,
|
||||
this.get("tx", avm, context)?.resolve(avm, context)?,
|
||||
this.get("ty", avm, context)?.resolve(avm, context)?,
|
||||
];
|
||||
let cloned = proto.new(avm, context, proto, &args)?;
|
||||
let _ = constructor(avm, context, cloned, &args)?;
|
||||
Ok(cloned.into())
|
||||
}
|
||||
|
||||
fn scale<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let scale_x = args
|
||||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.as_number(avm, context)?;
|
||||
let scale_y = args
|
||||
.get(1)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.as_number(avm, context)?;
|
||||
let mut matrix = Matrix::scale(scale_x as f32, scale_y as f32);
|
||||
matrix *= object_to_matrix(this, avm, context)?;
|
||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn rotate<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let angle = args
|
||||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.as_number(avm, context)?;
|
||||
let mut matrix = Matrix::rotate(angle as f32);
|
||||
matrix *= object_to_matrix(this, avm, context)?;
|
||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn translate<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let translate_x = args
|
||||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.as_number(avm, context)?;
|
||||
let translate_y = args
|
||||
.get(1)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.as_number(avm, context)?;
|
||||
let mut matrix = Matrix::translate(
|
||||
Twips::from_pixels(translate_x),
|
||||
Twips::from_pixels(translate_y),
|
||||
);
|
||||
matrix *= object_to_matrix(this, avm, context)?;
|
||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn concat<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let mut matrix = object_to_matrix(this, avm, context)?;
|
||||
let other = value_to_matrix(
|
||||
args.get(0).unwrap_or(&Value::Undefined).clone(),
|
||||
avm,
|
||||
context,
|
||||
)?;
|
||||
matrix = other * matrix;
|
||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn invert<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let mut matrix = object_to_matrix(this, avm, context)?;
|
||||
matrix.invert();
|
||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn create_box<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let scale_x = args
|
||||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.as_number(avm, context)?;
|
||||
let scale_y = args
|
||||
.get(1)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.as_number(avm, context)?;
|
||||
// [NA] Docs say rotation is optional and defaults to 0, but that's wrong?
|
||||
let rotation = args
|
||||
.get(2)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.as_number(avm, context)?;
|
||||
let translate_x = if let Some(value) = args.get(3) {
|
||||
value.as_number(avm, context)?
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let translate_y = if let Some(value) = args.get(4) {
|
||||
value.as_number(avm, context)?
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let matrix = Matrix::create_box(
|
||||
scale_x as f32,
|
||||
scale_y as f32,
|
||||
rotation as f32,
|
||||
Twips::from_pixels(translate_x),
|
||||
Twips::from_pixels(translate_y),
|
||||
);
|
||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn create_gradient_box<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let width = args
|
||||
.get(0)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.as_number(avm, context)?;
|
||||
let height = args
|
||||
.get(1)
|
||||
.unwrap_or(&Value::Undefined)
|
||||
.as_number(avm, context)?;
|
||||
let rotation = if let Some(value) = args.get(2) {
|
||||
value.as_number(avm, context)?
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let translate_x = if let Some(value) = args.get(3) {
|
||||
value.as_number(avm, context)?
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
let translate_y = if let Some(value) = args.get(4) {
|
||||
value.as_number(avm, context)?
|
||||
} else {
|
||||
0.0
|
||||
};
|
||||
|
||||
let matrix = Matrix::create_gradient_box(
|
||||
width as f32,
|
||||
height as f32,
|
||||
rotation as f32,
|
||||
Twips::from_pixels(translate_x),
|
||||
Twips::from_pixels(translate_y),
|
||||
);
|
||||
apply_matrix_to_object(matrix, this, avm, context)?;
|
||||
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn to_string<'gc>(
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
this: Object<'gc>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
let a = this
|
||||
.get("a", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.coerce_to_string(avm, context)?;
|
||||
let b = this
|
||||
.get("b", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.coerce_to_string(avm, context)?;
|
||||
let c = this
|
||||
.get("c", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.coerce_to_string(avm, context)?;
|
||||
let d = this
|
||||
.get("d", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.coerce_to_string(avm, context)?;
|
||||
let tx = this
|
||||
.get("tx", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.coerce_to_string(avm, context)?;
|
||||
let ty = this
|
||||
.get("ty", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.coerce_to_string(avm, context)?;
|
||||
|
||||
Ok(format!("(a={}, b={}, c={}, d={}, tx={}, ty={})", a, b, c, d, tx, ty).into())
|
||||
}
|
||||
|
||||
pub fn create_matrix_object<'gc>(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
matrix_proto: Option<Object<'gc>>,
|
||||
fn_proto: Option<Object<'gc>>,
|
||||
) -> Object<'gc> {
|
||||
FunctionObject::function(
|
||||
gc_context,
|
||||
Executable::Native(constructor),
|
||||
fn_proto,
|
||||
matrix_proto,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn create_proto<'gc>(
|
||||
gc_context: MutationContext<'gc, '_>,
|
||||
proto: Object<'gc>,
|
||||
fn_proto: Object<'gc>,
|
||||
) -> Object<'gc> {
|
||||
let mut object = ScriptObject::object(gc_context, Some(proto));
|
||||
|
||||
object.force_set_function(
|
||||
"toString",
|
||||
to_string,
|
||||
gc_context,
|
||||
EnumSet::empty(),
|
||||
Some(fn_proto),
|
||||
);
|
||||
|
||||
object.force_set_function(
|
||||
"identity",
|
||||
identity,
|
||||
gc_context,
|
||||
EnumSet::empty(),
|
||||
Some(fn_proto),
|
||||
);
|
||||
|
||||
object.force_set_function("clone", clone, gc_context, EnumSet::empty(), Some(fn_proto));
|
||||
|
||||
object.force_set_function("scale", scale, gc_context, EnumSet::empty(), Some(fn_proto));
|
||||
|
||||
object.force_set_function(
|
||||
"rotate",
|
||||
rotate,
|
||||
gc_context,
|
||||
EnumSet::empty(),
|
||||
Some(fn_proto),
|
||||
);
|
||||
|
||||
object.force_set_function(
|
||||
"translate",
|
||||
translate,
|
||||
gc_context,
|
||||
EnumSet::empty(),
|
||||
Some(fn_proto),
|
||||
);
|
||||
|
||||
object.force_set_function(
|
||||
"concat",
|
||||
concat,
|
||||
gc_context,
|
||||
EnumSet::empty(),
|
||||
Some(fn_proto),
|
||||
);
|
||||
|
||||
object.force_set_function(
|
||||
"invert",
|
||||
invert,
|
||||
gc_context,
|
||||
EnumSet::empty(),
|
||||
Some(fn_proto),
|
||||
);
|
||||
|
||||
object.force_set_function(
|
||||
"createBox",
|
||||
create_box,
|
||||
gc_context,
|
||||
EnumSet::empty(),
|
||||
Some(fn_proto),
|
||||
);
|
||||
|
||||
object.force_set_function(
|
||||
"createGradientBox",
|
||||
create_gradient_box,
|
||||
gc_context,
|
||||
EnumSet::empty(),
|
||||
Some(fn_proto),
|
||||
);
|
||||
|
||||
object.into()
|
||||
}
|
|
@ -1,15 +1,20 @@
|
|||
//! MovieClip prototype
|
||||
|
||||
use crate::avm1::globals::display_object::{self, AVM_DEPTH_BIAS, AVM_MAX_DEPTH};
|
||||
use crate::avm1::globals::matrix::gradient_object_to_matrix;
|
||||
use crate::avm1::property::Attribute::*;
|
||||
use crate::avm1::return_value::ReturnValue;
|
||||
use crate::avm1::{Avm1, Error, Object, ScriptObject, TObject, UpdateContext, Value};
|
||||
use crate::backend::navigator::NavigationMethod;
|
||||
use crate::display_object::{DisplayObject, EditText, MovieClip, TDisplayObject};
|
||||
use crate::prelude::*;
|
||||
use crate::shape_utils::DrawCommand;
|
||||
use crate::tag_utils::SwfSlice;
|
||||
use gc_arena::MutationContext;
|
||||
use swf::Twips;
|
||||
use swf::{
|
||||
FillStyle, Gradient, GradientInterpolation, GradientRecord, GradientSpread, LineCapStyle,
|
||||
LineJoinStyle, LineStyle, Twips,
|
||||
};
|
||||
|
||||
/// Implements `MovieClip`
|
||||
pub fn constructor<'gc>(
|
||||
|
@ -123,12 +128,293 @@ pub fn create_proto<'gc>(
|
|||
"stopDrag" => stop_drag,
|
||||
"swapDepths" => swap_depths,
|
||||
"toString" => to_string,
|
||||
"unloadMovie" => unload_movie
|
||||
"unloadMovie" => unload_movie,
|
||||
"beginFill" => begin_fill,
|
||||
"beginGradientFill" => begin_gradient_fill,
|
||||
"moveTo" => move_to,
|
||||
"lineTo" => line_to,
|
||||
"curveTo" => curve_to,
|
||||
"endFill" => end_fill,
|
||||
"lineStyle" => line_style,
|
||||
"clear" => clear
|
||||
);
|
||||
|
||||
object.into()
|
||||
}
|
||||
|
||||
fn line_style<'gc>(
|
||||
movie_clip: MovieClip<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(width) = args.get(0) {
|
||||
let width = Twips::from_pixels(width.as_number(avm, context)?.min(255.0).max(0.0));
|
||||
let color = if let Some(rgb) = args.get(1) {
|
||||
let rgb = rgb.coerce_to_u32(avm, context)?;
|
||||
let alpha = if let Some(alpha) = args.get(2) {
|
||||
alpha.as_number(avm, context)?.min(100.0).max(0.0)
|
||||
} else {
|
||||
100.0
|
||||
} as f32
|
||||
/ 100.0
|
||||
* 255.0;
|
||||
Color::from_rgb(rgb, alpha as u8)
|
||||
} else {
|
||||
Color::from_rgb(0, 255)
|
||||
};
|
||||
let is_pixel_hinted = args
|
||||
.get(3)
|
||||
.map_or(false, |v| v.as_bool(avm.current_swf_version()));
|
||||
let (allow_scale_x, allow_scale_y) = match args
|
||||
.get(4)
|
||||
.and_then(|v| v.clone().coerce_to_string(avm, context).ok())
|
||||
.as_deref()
|
||||
{
|
||||
Some("normal") => (true, true),
|
||||
Some("vertical") => (true, false),
|
||||
Some("horizontal") => (false, true),
|
||||
_ => (false, false),
|
||||
};
|
||||
let cap_style = match args
|
||||
.get(5)
|
||||
.and_then(|v| v.clone().coerce_to_string(avm, context).ok())
|
||||
.as_deref()
|
||||
{
|
||||
Some("square") => LineCapStyle::Square,
|
||||
Some("none") => LineCapStyle::None,
|
||||
_ => LineCapStyle::Round,
|
||||
};
|
||||
let join_style = match args
|
||||
.get(6)
|
||||
.and_then(|v| v.clone().coerce_to_string(avm, context).ok())
|
||||
.as_deref()
|
||||
{
|
||||
Some("miter") => {
|
||||
if let Some(limit) = args.get(7) {
|
||||
LineJoinStyle::Miter(limit.as_number(avm, context)?.max(0.0).min(255.0) as f32)
|
||||
} else {
|
||||
LineJoinStyle::Miter(3.0)
|
||||
}
|
||||
}
|
||||
Some("bevel") => LineJoinStyle::Bevel,
|
||||
_ => LineJoinStyle::Round,
|
||||
};
|
||||
movie_clip.set_line_style(
|
||||
context,
|
||||
Some(LineStyle {
|
||||
width,
|
||||
color,
|
||||
start_cap: cap_style,
|
||||
end_cap: cap_style,
|
||||
join_style,
|
||||
fill_style: None,
|
||||
allow_scale_x,
|
||||
allow_scale_y,
|
||||
is_pixel_hinted,
|
||||
allow_close: false,
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
movie_clip.set_line_style(context, None);
|
||||
}
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn begin_fill<'gc>(
|
||||
movie_clip: MovieClip<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let Some(rgb) = args.get(0) {
|
||||
let rgb = rgb.coerce_to_u32(avm, context)?;
|
||||
let alpha = if let Some(alpha) = args.get(1) {
|
||||
alpha.as_number(avm, context)?.min(100.0).max(0.0)
|
||||
} else {
|
||||
100.0
|
||||
} as f32
|
||||
/ 100.0
|
||||
* 255.0;
|
||||
movie_clip.set_fill_style(
|
||||
context,
|
||||
Some(FillStyle::Color(Color::from_rgb(rgb, alpha as u8))),
|
||||
);
|
||||
} else {
|
||||
movie_clip.set_fill_style(context, None);
|
||||
}
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn begin_gradient_fill<'gc>(
|
||||
movie_clip: MovieClip<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let (Some(method), Some(colors), Some(alphas), Some(ratios), Some(matrix)) = (
|
||||
args.get(0),
|
||||
args.get(1),
|
||||
args.get(2),
|
||||
args.get(3),
|
||||
args.get(4),
|
||||
) {
|
||||
let method = method.clone().coerce_to_string(avm, context)?;
|
||||
let colors = colors.as_object()?.array();
|
||||
let alphas = alphas.as_object()?.array();
|
||||
let ratios = ratios.as_object()?.array();
|
||||
let matrix_object = matrix.as_object()?;
|
||||
if colors.len() != alphas.len() || colors.len() != ratios.len() {
|
||||
log::warn!(
|
||||
"beginGradientFill() received different sized arrays for colors, alphas and ratios"
|
||||
);
|
||||
return Ok(Value::Undefined.into());
|
||||
}
|
||||
let mut records = Vec::with_capacity(colors.len());
|
||||
for i in 0..colors.len() {
|
||||
let ratio = ratios[i].as_number(avm, context)?.min(255.0).max(0.0);
|
||||
let rgb = colors[i].coerce_to_u32(avm, context)?;
|
||||
let alpha = alphas[i].as_number(avm, context)?.min(100.0).max(0.0);
|
||||
records.push(GradientRecord {
|
||||
ratio: ratio as u8,
|
||||
color: Color::from_rgb(rgb, (alpha / 100.0 * 255.0) as u8),
|
||||
});
|
||||
}
|
||||
let matrix = gradient_object_to_matrix(matrix_object, avm, context)?;
|
||||
let spread = match args
|
||||
.get(5)
|
||||
.and_then(|v| v.clone().coerce_to_string(avm, context).ok())
|
||||
.as_deref()
|
||||
{
|
||||
Some("reflect") => GradientSpread::Reflect,
|
||||
Some("repeat") => GradientSpread::Repeat,
|
||||
_ => GradientSpread::Pad,
|
||||
};
|
||||
let interpolation = match args
|
||||
.get(6)
|
||||
.and_then(|v| v.clone().coerce_to_string(avm, context).ok())
|
||||
.as_deref()
|
||||
{
|
||||
Some("linearRGB") => GradientInterpolation::LinearRGB,
|
||||
_ => GradientInterpolation::RGB,
|
||||
};
|
||||
|
||||
let gradient = Gradient {
|
||||
matrix,
|
||||
spread,
|
||||
interpolation,
|
||||
records,
|
||||
};
|
||||
let style = match method.as_str() {
|
||||
"linear" => FillStyle::LinearGradient(gradient),
|
||||
"radial" => {
|
||||
if let Some(focal_point) = args.get(7) {
|
||||
FillStyle::FocalGradient {
|
||||
gradient,
|
||||
focal_point: focal_point.as_number(avm, context)? as f32,
|
||||
}
|
||||
} else {
|
||||
FillStyle::RadialGradient(gradient)
|
||||
}
|
||||
}
|
||||
other => {
|
||||
log::warn!("beginGradientFill() received invalid fill type {:?}", other);
|
||||
return Ok(Value::Undefined.into());
|
||||
}
|
||||
};
|
||||
movie_clip.set_fill_style(context, Some(style));
|
||||
} else {
|
||||
movie_clip.set_fill_style(context, None);
|
||||
}
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn move_to<'gc>(
|
||||
movie_clip: MovieClip<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let (Some(x), Some(y)) = (args.get(0), args.get(1)) {
|
||||
let x = x.as_number(avm, context)?;
|
||||
let y = y.as_number(avm, context)?;
|
||||
movie_clip.draw_command(
|
||||
context,
|
||||
DrawCommand::MoveTo {
|
||||
x: Twips::from_pixels(x),
|
||||
y: Twips::from_pixels(y),
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn line_to<'gc>(
|
||||
movie_clip: MovieClip<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let (Some(x), Some(y)) = (args.get(0), args.get(1)) {
|
||||
let x = x.as_number(avm, context)?;
|
||||
let y = y.as_number(avm, context)?;
|
||||
movie_clip.draw_command(
|
||||
context,
|
||||
DrawCommand::LineTo {
|
||||
x: Twips::from_pixels(x),
|
||||
y: Twips::from_pixels(y),
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn curve_to<'gc>(
|
||||
movie_clip: MovieClip<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
if let (Some(x1), Some(y1), Some(x2), Some(y2)) =
|
||||
(args.get(0), args.get(1), args.get(2), args.get(3))
|
||||
{
|
||||
let x1 = x1.as_number(avm, context)?;
|
||||
let y1 = y1.as_number(avm, context)?;
|
||||
let x2 = x2.as_number(avm, context)?;
|
||||
let y2 = y2.as_number(avm, context)?;
|
||||
movie_clip.draw_command(
|
||||
context,
|
||||
DrawCommand::CurveTo {
|
||||
x1: Twips::from_pixels(x1),
|
||||
y1: Twips::from_pixels(y1),
|
||||
x2: Twips::from_pixels(x2),
|
||||
y2: Twips::from_pixels(y2),
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn end_fill<'gc>(
|
||||
movie_clip: MovieClip<'gc>,
|
||||
_avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
movie_clip.set_fill_style(context, None);
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn clear<'gc>(
|
||||
movie_clip: MovieClip<'gc>,
|
||||
_avm: &mut Avm1<'gc>,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
_args: &[Value<'gc>],
|
||||
) -> Result<ReturnValue<'gc>, Error> {
|
||||
movie_clip.clear(context);
|
||||
Ok(Value::Undefined.into())
|
||||
}
|
||||
|
||||
fn attach_movie<'gc>(
|
||||
mut movie_clip: MovieClip<'gc>,
|
||||
avm: &mut Avm1<'gc>,
|
||||
|
|
|
@ -7,6 +7,7 @@ pub use swf;
|
|||
pub trait RenderBackend: Downcast {
|
||||
fn set_viewport_dimensions(&mut self, width: u32, height: u32);
|
||||
fn register_shape(&mut self, shape: DistilledShape) -> ShapeHandle;
|
||||
fn replace_shape(&mut self, shape: DistilledShape, handle: ShapeHandle);
|
||||
fn register_glyph_shape(&mut self, shape: &swf::Glyph) -> ShapeHandle;
|
||||
fn register_bitmap_jpeg(
|
||||
&mut self,
|
||||
|
@ -83,6 +84,7 @@ impl RenderBackend for NullRenderer {
|
|||
fn register_shape(&mut self, _shape: DistilledShape) -> ShapeHandle {
|
||||
ShapeHandle(0)
|
||||
}
|
||||
fn replace_shape(&mut self, _shape: DistilledShape, _handle: ShapeHandle) {}
|
||||
fn register_glyph_shape(&mut self, _shape: &swf::Glyph) -> ShapeHandle {
|
||||
ShapeHandle(0)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::matrix::Matrix;
|
||||
use swf::Matrix;
|
||||
use swf::Twips;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct BoundingBox {
|
||||
pub x_min: Twips,
|
||||
pub y_min: Twips,
|
||||
|
@ -43,6 +43,29 @@ impl BoundingBox {
|
|||
}
|
||||
}
|
||||
|
||||
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 self.valid && other.valid {
|
||||
|
@ -97,3 +120,15 @@ impl From<swf::Rectangle> for BoundingBox {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&swf::Rectangle> for BoundingBox {
|
||||
fn from(rect: &swf::Rectangle) -> Self {
|
||||
Self {
|
||||
x_min: rect.x_min,
|
||||
y_min: rect.y_min,
|
||||
x_max: rect.x_max,
|
||||
y_max: rect.y_max,
|
||||
valid: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -767,7 +767,7 @@ pub trait TDisplayObject<'gc>: 'gc + Collect + Debug + Into<DisplayObject<'gc>>
|
|||
// PlaceObject tags only apply if this onject has not been dynamically moved by AS code.
|
||||
if !self.transformed_by_script() {
|
||||
if let Some(matrix) = &place_object.matrix {
|
||||
self.set_matrix(gc_context, &matrix.clone().into());
|
||||
self.set_matrix(gc_context, &matrix);
|
||||
}
|
||||
if let Some(color_transform) = &place_object.color_transform {
|
||||
self.set_color_transform(gc_context, &color_transform.clone().into());
|
||||
|
@ -918,19 +918,19 @@ macro_rules! impl_display_object {
|
|||
fn transform(&self) -> std::cell::Ref<crate::transform::Transform> {
|
||||
std::cell::Ref::map(self.0.read(), |o| o.$field.transform())
|
||||
}
|
||||
fn matrix(&self) -> std::cell::Ref<crate::matrix::Matrix> {
|
||||
fn matrix(&self) -> std::cell::Ref<swf::Matrix> {
|
||||
std::cell::Ref::map(self.0.read(), |o| o.$field.matrix())
|
||||
}
|
||||
fn matrix_mut(
|
||||
&mut self,
|
||||
context: gc_arena::MutationContext<'gc, '_>,
|
||||
) -> std::cell::RefMut<crate::matrix::Matrix> {
|
||||
) -> std::cell::RefMut<swf::Matrix> {
|
||||
std::cell::RefMut::map(self.0.write(context), |o| o.$field.matrix_mut(context))
|
||||
}
|
||||
fn set_matrix(
|
||||
&mut self,
|
||||
context: gc_arena::MutationContext<'gc, '_>,
|
||||
matrix: &crate::matrix::Matrix,
|
||||
matrix: &swf::Matrix,
|
||||
) {
|
||||
self.0.write(context).$field.set_matrix(context, matrix)
|
||||
}
|
||||
|
|
|
@ -247,7 +247,7 @@ impl<'gc> ButtonData<'gc> {
|
|||
.instantiate_by_id(record.id, context.gc_context)
|
||||
{
|
||||
child.set_parent(context.gc_context, Some(self_display_object));
|
||||
child.set_matrix(context.gc_context, &record.matrix.clone().into());
|
||||
child.set_matrix(context.gc_context, &record.matrix);
|
||||
child.set_color_transform(
|
||||
context.gc_context,
|
||||
&record.color_transform.clone().into(),
|
||||
|
@ -280,7 +280,7 @@ impl<'gc> ButtonData<'gc> {
|
|||
{
|
||||
Ok(mut child) => {
|
||||
{
|
||||
child.set_matrix(context.gc_context, &record.matrix.clone().into());
|
||||
child.set_matrix(context.gc_context, &record.matrix);
|
||||
child.set_parent(context.gc_context, Some(self_display_object));
|
||||
child.set_depth(context.gc_context, record.depth.into());
|
||||
child.post_instantiation(avm, context, child, None);
|
||||
|
|
|
@ -161,7 +161,7 @@ impl MorphShapeStatic {
|
|||
.collect();
|
||||
|
||||
FillStyle::LinearGradient(Gradient {
|
||||
matrix: start.matrix.clone(),
|
||||
matrix: start.matrix,
|
||||
spread: start.spread,
|
||||
interpolation: start.interpolation,
|
||||
records,
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
//! `MovieClip` display object and support code.
|
||||
use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
|
||||
use crate::backend::audio::AudioStreamHandle;
|
||||
|
||||
use crate::character::Character;
|
||||
use crate::context::{ActionType, RenderContext, UpdateContext};
|
||||
use crate::display_object::{
|
||||
Bitmap, Button, DisplayObjectBase, EditText, Graphic, MorphShapeStatic, TDisplayObject, Text,
|
||||
};
|
||||
use crate::drawing::Drawing;
|
||||
use crate::events::{ButtonKeyCode, ClipEvent};
|
||||
use crate::font::Font;
|
||||
use crate::prelude::*;
|
||||
use crate::shape_utils::DrawCommand;
|
||||
use crate::tag_utils::{self, DecodeResult, SwfMovie, SwfSlice, SwfStream};
|
||||
use enumset::{EnumSet, EnumSetType};
|
||||
use gc_arena::{Collect, Gc, GcCell, MutationContext};
|
||||
|
@ -18,6 +21,7 @@ use std::collections::{BTreeMap, HashMap};
|
|||
use std::convert::TryFrom;
|
||||
use std::sync::Arc;
|
||||
use swf::read::SwfRead;
|
||||
use swf::{FillStyle, LineStyle};
|
||||
|
||||
type FrameNumber = u16;
|
||||
|
||||
|
@ -42,6 +46,7 @@ pub struct MovieClipData<'gc> {
|
|||
clip_actions: SmallVec<[ClipAction; 2]>,
|
||||
flags: EnumSet<MovieClipFlags>,
|
||||
avm1_constructor: Option<Object<'gc>>,
|
||||
drawing: Drawing,
|
||||
}
|
||||
|
||||
impl<'gc> MovieClip<'gc> {
|
||||
|
@ -60,6 +65,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
clip_actions: SmallVec::new(),
|
||||
flags: EnumSet::empty(),
|
||||
avm1_constructor: None,
|
||||
drawing: Drawing::new(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -92,6 +98,7 @@ impl<'gc> MovieClip<'gc> {
|
|||
clip_actions: SmallVec::new(),
|
||||
flags: MovieClipFlags::Playing.into(),
|
||||
avm1_constructor: None,
|
||||
drawing: Drawing::new(),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -550,6 +557,34 @@ impl<'gc> MovieClip<'gc> {
|
|||
|
||||
actions.into_iter()
|
||||
}
|
||||
|
||||
pub fn set_fill_style(
|
||||
self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
style: Option<FillStyle>,
|
||||
) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
mc.drawing.set_fill_style(style);
|
||||
}
|
||||
|
||||
pub fn clear(self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
mc.drawing.clear();
|
||||
}
|
||||
|
||||
pub fn set_line_style(
|
||||
self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
style: Option<LineStyle>,
|
||||
) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
mc.drawing.set_line_style(style);
|
||||
}
|
||||
|
||||
pub fn draw_command(self, context: &mut UpdateContext<'_, 'gc, '_>, command: DrawCommand) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
mc.drawing.draw_command(command);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||
|
@ -592,12 +627,12 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
fn render(&self, context: &mut RenderContext<'_, 'gc>) {
|
||||
context.transform_stack.push(&*self.transform());
|
||||
crate::display_object::render_children(context, &self.0.read().children);
|
||||
self.0.read().drawing.render(context);
|
||||
context.transform_stack.pop();
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
// No inherent bounds; contains child DisplayObjects.
|
||||
BoundingBox::default()
|
||||
self.0.read().drawing.self_bounds()
|
||||
}
|
||||
|
||||
fn hit_test(&self, point: (Twips, Twips)) -> bool {
|
||||
|
|
|
@ -33,7 +33,7 @@ impl<'gc> Text<'gc> {
|
|||
swf,
|
||||
id: tag.id,
|
||||
bounds: tag.bounds.clone().into(),
|
||||
text_transform: tag.matrix.clone().into(),
|
||||
text_transform: tag.matrix,
|
||||
text_blocks: tag.records.clone(),
|
||||
},
|
||||
),
|
||||
|
|
|
@ -0,0 +1,223 @@
|
|||
use crate::backend::render::ShapeHandle;
|
||||
use crate::bounding_box::BoundingBox;
|
||||
use crate::context::RenderContext;
|
||||
use crate::shape_utils::{DistilledShape, DrawCommand, DrawPath};
|
||||
use std::cell::Cell;
|
||||
use swf::{FillStyle, LineStyle, Twips};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Drawing {
|
||||
render_handle: Cell<Option<ShapeHandle>>,
|
||||
shape_bounds: BoundingBox,
|
||||
edge_bounds: BoundingBox,
|
||||
dirty: Cell<bool>,
|
||||
fills: Vec<(FillStyle, Vec<DrawCommand>)>,
|
||||
lines: Vec<(LineStyle, Vec<DrawCommand>)>,
|
||||
current_fill: Option<(FillStyle, Vec<DrawCommand>)>,
|
||||
current_line: Option<(LineStyle, Vec<DrawCommand>)>,
|
||||
cursor: (Twips, Twips),
|
||||
}
|
||||
|
||||
impl Drawing {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
render_handle: Cell::new(None),
|
||||
shape_bounds: BoundingBox::default(),
|
||||
edge_bounds: BoundingBox::default(),
|
||||
dirty: Cell::new(false),
|
||||
fills: Vec::new(),
|
||||
lines: Vec::new(),
|
||||
current_fill: None,
|
||||
current_line: None,
|
||||
cursor: (Twips::zero(), Twips::zero()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_fill_style(&mut self, style: Option<FillStyle>) {
|
||||
// TODO: If current_fill is not closed, we should close it and also close current_line
|
||||
|
||||
if let Some(existing) = self.current_fill.take() {
|
||||
self.fills.push(existing);
|
||||
}
|
||||
if let Some(style) = style {
|
||||
self.current_fill = Some((
|
||||
style,
|
||||
vec![DrawCommand::MoveTo {
|
||||
x: self.cursor.0,
|
||||
y: self.cursor.1,
|
||||
}],
|
||||
));
|
||||
}
|
||||
|
||||
self.dirty.set(true);
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.current_fill = None;
|
||||
self.current_line = None;
|
||||
self.fills.clear();
|
||||
self.lines.clear();
|
||||
self.edge_bounds = BoundingBox::default();
|
||||
self.shape_bounds = BoundingBox::default();
|
||||
self.dirty.set(true);
|
||||
self.cursor = (Twips::zero(), Twips::zero());
|
||||
}
|
||||
|
||||
pub fn set_line_style(&mut self, style: Option<LineStyle>) {
|
||||
if let Some(existing) = self.current_line.take() {
|
||||
self.lines.push(existing);
|
||||
}
|
||||
if let Some(style) = style {
|
||||
self.current_line = Some((
|
||||
style,
|
||||
vec![DrawCommand::MoveTo {
|
||||
x: self.cursor.0,
|
||||
y: self.cursor.1,
|
||||
}],
|
||||
));
|
||||
}
|
||||
|
||||
self.dirty.set(true);
|
||||
}
|
||||
|
||||
pub fn draw_command(&mut self, command: DrawCommand) {
|
||||
let mut include_last = false;
|
||||
let stroke_width = if let Some((style, _)) = &self.current_line {
|
||||
style.width
|
||||
} else {
|
||||
Twips::zero()
|
||||
};
|
||||
|
||||
match command {
|
||||
DrawCommand::MoveTo { .. } => {}
|
||||
DrawCommand::LineTo { .. } => {
|
||||
stretch_bounding_box(&mut self.shape_bounds, &command, stroke_width);
|
||||
stretch_bounding_box(&mut self.edge_bounds, &command, Twips::zero());
|
||||
include_last = true;
|
||||
}
|
||||
DrawCommand::CurveTo { .. } => {
|
||||
stretch_bounding_box(&mut self.shape_bounds, &command, stroke_width);
|
||||
stretch_bounding_box(&mut self.edge_bounds, &command, Twips::zero());
|
||||
include_last = true;
|
||||
}
|
||||
}
|
||||
|
||||
self.cursor = command.end_point();
|
||||
|
||||
if let Some((_, commands)) = &mut self.current_line {
|
||||
commands.push(command.clone());
|
||||
}
|
||||
if let Some((_, commands)) = &mut self.current_fill {
|
||||
commands.push(command);
|
||||
}
|
||||
|
||||
if include_last {
|
||||
if let Some(command) = self
|
||||
.current_fill
|
||||
.as_ref()
|
||||
.and_then(|(_, commands)| commands.last())
|
||||
{
|
||||
stretch_bounding_box(&mut self.shape_bounds, command, stroke_width);
|
||||
stretch_bounding_box(&mut self.edge_bounds, command, Twips::zero());
|
||||
}
|
||||
|
||||
if let Some(command) = self
|
||||
.current_line
|
||||
.as_ref()
|
||||
.and_then(|(_, commands)| commands.last())
|
||||
{
|
||||
stretch_bounding_box(&mut self.shape_bounds, command, stroke_width);
|
||||
stretch_bounding_box(&mut self.edge_bounds, command, Twips::zero());
|
||||
}
|
||||
}
|
||||
|
||||
self.dirty.set(true);
|
||||
}
|
||||
|
||||
pub fn render(&self, context: &mut RenderContext) {
|
||||
if self.dirty.get() {
|
||||
self.dirty.set(false);
|
||||
let mut paths = Vec::new();
|
||||
|
||||
for (style, commands) in &self.fills {
|
||||
paths.push(DrawPath::Fill {
|
||||
style,
|
||||
commands: commands.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
// TODO: If the current_fill is not closed, we should automatically close current_line
|
||||
|
||||
if let Some((style, commands)) = &self.current_fill {
|
||||
paths.push(DrawPath::Fill {
|
||||
style,
|
||||
commands: commands.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
for (style, commands) in &self.lines {
|
||||
paths.push(DrawPath::Stroke {
|
||||
style,
|
||||
commands: commands.to_owned(),
|
||||
is_closed: false, // TODO: Determine this
|
||||
})
|
||||
}
|
||||
|
||||
if let Some((style, commands)) = &self.current_line {
|
||||
paths.push(DrawPath::Stroke {
|
||||
style,
|
||||
commands: commands.to_owned(),
|
||||
is_closed: false, // TODO: Determine this
|
||||
})
|
||||
}
|
||||
|
||||
let shape = DistilledShape {
|
||||
paths,
|
||||
shape_bounds: self.shape_bounds.clone(),
|
||||
edge_bounds: self.edge_bounds.clone(),
|
||||
id: 0,
|
||||
};
|
||||
|
||||
if let Some(handle) = self.render_handle.get() {
|
||||
context.renderer.replace_shape(shape, handle);
|
||||
} else {
|
||||
self.render_handle
|
||||
.set(Some(context.renderer.register_shape(shape)));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(handle) = self.render_handle.get() {
|
||||
context
|
||||
.renderer
|
||||
.render_shape(handle, context.transform_stack.transform());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn self_bounds(&self) -> BoundingBox {
|
||||
self.shape_bounds.clone()
|
||||
}
|
||||
}
|
||||
|
||||
fn stretch_bounding_box(
|
||||
bounding_box: &mut BoundingBox,
|
||||
command: &DrawCommand,
|
||||
stroke_width: Twips,
|
||||
) {
|
||||
let radius = stroke_width / 2;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -14,11 +14,11 @@ mod bounding_box;
|
|||
mod character;
|
||||
pub mod color_transform;
|
||||
mod context;
|
||||
mod drawing;
|
||||
pub mod events;
|
||||
mod font;
|
||||
mod library;
|
||||
mod loader;
|
||||
pub mod matrix;
|
||||
mod player;
|
||||
mod prelude;
|
||||
mod property_map;
|
||||
|
|
|
@ -2,8 +2,8 @@ pub use crate::bounding_box::BoundingBox;
|
|||
pub use crate::color_transform::ColorTransform;
|
||||
pub use crate::display_object::{DisplayObject, TDisplayObject};
|
||||
pub use crate::impl_display_object;
|
||||
pub use crate::matrix::Matrix;
|
||||
pub use log::{error, info, trace, warn};
|
||||
pub use swf::Matrix;
|
||||
pub use swf::{CharacterId, Color, Twips};
|
||||
|
||||
/// A depth for a Flash display object in AVM1.
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::bounding_box::BoundingBox;
|
||||
use fnv::FnvHashMap;
|
||||
use std::num::NonZeroU32;
|
||||
use swf::{CharacterId, FillStyle, LineStyle, Rectangle, Shape, ShapeRecord, Twips};
|
||||
use swf::{CharacterId, FillStyle, LineStyle, Shape, ShapeRecord, Twips};
|
||||
|
||||
pub fn calculate_shape_bounds(shape_records: &[swf::ShapeRecord]) -> swf::Rectangle {
|
||||
let mut bounds = swf::Rectangle {
|
||||
|
@ -79,8 +80,8 @@ pub enum DrawPath<'a> {
|
|||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct DistilledShape<'a> {
|
||||
pub paths: Vec<DrawPath<'a>>,
|
||||
pub shape_bounds: Rectangle,
|
||||
pub edge_bounds: Rectangle,
|
||||
pub shape_bounds: BoundingBox,
|
||||
pub edge_bounds: BoundingBox,
|
||||
pub id: CharacterId,
|
||||
}
|
||||
|
||||
|
@ -88,8 +89,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.clone(),
|
||||
edge_bounds: shape.edge_bounds.clone(),
|
||||
shape_bounds: (&shape.shape_bounds).into(),
|
||||
edge_bounds: (&shape.edge_bounds).into(),
|
||||
id: shape.id,
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +116,16 @@ pub enum DrawCommand {
|
|||
},
|
||||
}
|
||||
|
||||
impl DrawCommand {
|
||||
pub fn end_point(&self) -> (Twips, Twips) {
|
||||
match self {
|
||||
DrawCommand::MoveTo { x, y } => (*x, *y),
|
||||
DrawCommand::LineTo { x, y } => (*x, *y),
|
||||
DrawCommand::CurveTo { x2, y2, .. } => (*x2, *y2),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
struct Point {
|
||||
x: Twips,
|
||||
|
|
|
@ -72,6 +72,7 @@ swf_tests! {
|
|||
(execution_order3, "avm1/execution_order3", 5),
|
||||
(single_frame, "avm1/single_frame", 2),
|
||||
(looping, "avm1/looping", 6),
|
||||
(matrix, "avm1/matrix", 1),
|
||||
(goto_advance1, "avm1/goto_advance1", 2),
|
||||
(goto_advance2, "avm1/goto_advance2", 2),
|
||||
(goto_both_ways1, "avm1/goto_both_ways1", 2),
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
// new Matrix()
|
||||
(a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
|
||||
// new Matrix(1)
|
||||
(a=1, b=undefined, c=undefined, d=undefined, tx=undefined, ty=undefined)
|
||||
|
||||
// new Matrix(1, 2)
|
||||
(a=1, b=2, c=undefined, d=undefined, tx=undefined, ty=undefined)
|
||||
|
||||
// new Matrix(1, 2, 3)
|
||||
(a=1, b=2, c=3, d=undefined, tx=undefined, ty=undefined)
|
||||
|
||||
// new Matrix(1, 2, 3, {})
|
||||
(a=1, b=2, c=3, d=[object Object], tx=undefined, ty=undefined)
|
||||
|
||||
// new Matrix(1, 2, 3, 4, 5)
|
||||
(a=1, b=2, c=3, d=4, tx=5, ty=undefined)
|
||||
|
||||
// new Matrix(1, 2, 3, 4, 5, 6)
|
||||
(a=1, b=2, c=3, d=4, tx=5, ty=6)
|
||||
|
||||
// new Matrix(1, 2, 3, 4, 5, 6, 7)
|
||||
(a=1, b=2, c=3, d=4, tx=5, ty=6)
|
||||
|
||||
// new Matrix(1, 2, 3, 4, 5, 6) .identity()
|
||||
(a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
|
||||
|
||||
/// Clones
|
||||
// matrix
|
||||
(a=1, b=2, c=3, d=4, tx=5, ty=6)
|
||||
|
||||
// cloned
|
||||
(a=1, b=2, c=3, d=4, tx=5, ty=6)
|
||||
|
||||
// matrix === cloned
|
||||
false
|
||||
|
||||
|
||||
// matrix
|
||||
(a=1, b=2, c=[object Object], d=4, tx=5, ty=6)
|
||||
|
||||
// cloned
|
||||
(a=1, b=2, c=[object Object], d=4, tx=5, ty=6)
|
||||
|
||||
// matrix === cloned
|
||||
false
|
||||
|
||||
|
||||
/// scale
|
||||
// matrix
|
||||
(a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
|
||||
// matrix.scale(3, 5)
|
||||
(a=3, b=0, c=0, d=5, tx=0, ty=0)
|
||||
|
||||
|
||||
// matrix
|
||||
(a=2, b=0, c=0, d=2, tx=100, ty=100)
|
||||
|
||||
// matrix.scale(7, 11)
|
||||
(a=14, b=0, c=0, d=22, tx=700, ty=1100)
|
||||
|
||||
|
||||
// matrix
|
||||
(a=1, b=2, c=3, d=4, tx=5, ty=6)
|
||||
|
||||
// matrix.scale()
|
||||
(a=13, b=34, c=39, d=68, tx=65, ty=102)
|
||||
|
||||
|
||||
/// rotate
|
||||
(a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
|
||||
// matrix.rotate(0)
|
||||
(a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
|
||||
|
||||
/// rotate
|
||||
// matrix
|
||||
(a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
|
||||
// matrix
|
||||
(a=1, b=2, c=3, d=4, tx=5, ty=6)
|
||||
|
||||
// matrix.rotate(0)
|
||||
(a=1, b=2, c=3, d=4, tx=5, ty=6)
|
||||
|
||||
|
||||
// matrix
|
||||
(a=1, b=2, c=3, d=4, tx=5, ty=6)
|
||||
|
||||
/// translate
|
||||
// matrix
|
||||
// matrix.translate(3, 5)
|
||||
(a=1, b=0, c=0, d=1, tx=3, ty=5)
|
||||
|
||||
|
||||
// matrix
|
||||
// matrix.translate(7, 11)
|
||||
(a=2, b=0, c=0, d=2, tx=107, ty=111)
|
||||
|
||||
|
||||
/// concat
|
||||
// matrix
|
||||
(a=11, b=13, c=17, d=19, tx=23, ty=29)
|
||||
|
||||
// matrix
|
||||
(a=33, b=65, c=51, d=95, tx=69, ty=145)
|
||||
|
||||
// matrix
|
||||
(a=33, b=65, c=51, d=95, tx=76, ty=154)
|
||||
|
||||
|
||||
/// invert
|
||||
// matrix
|
||||
(a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
|
||||
// matrix.invert()
|
||||
(a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
|
||||
|
||||
// matrix
|
||||
(a=2, b=3, c=5, d=7, tx=9, ty=11)
|
||||
|
||||
// matrix.invert()
|
||||
(a=-7, b=3, c=5, d=-2, tx=8, ty=-5)
|
||||
|
||||
|
||||
/// createBox
|
||||
// matrix = nw Matrix();
|
||||
(a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
|
||||
// matrix.createBox(2, 3)
|
||||
(a=NaN, b=NaN, c=NaN, d=NaN, tx=0, ty=0)
|
||||
|
||||
// matrix.createBox(2, 3, 0)
|
||||
(a=2, b=0, c=0, d=3, tx=0, ty=0)
|
||||
|
||||
|
||||
/// createGradientBox
|
||||
// matrix = nw Matrix();
|
||||
(a=1, b=0, c=0, d=1, tx=0, ty=0)
|
||||
|
||||
// matrix.createGradientBox(200, 300)
|
||||
(a=0.1220703125, b=0, c=0, d=0.18310546875, tx=100, ty=150)
|
||||
|
||||
// matrix.createGradientBox(200, 300, 0)
|
||||
(a=0.1220703125, b=0, c=0, d=0.18310546875, tx=100, ty=150)
|
||||
|
||||
|
Binary file not shown.
Binary file not shown.
|
@ -455,6 +455,26 @@ impl RenderBackend for WebCanvasRenderBackend {
|
|||
handle
|
||||
}
|
||||
|
||||
fn replace_shape(&mut self, shape: DistilledShape, handle: ShapeHandle) {
|
||||
let mut bitmaps = HashMap::new();
|
||||
for (id, handle) in &self.id_to_bitmap {
|
||||
let bitmap_data = &self.bitmaps[handle.0];
|
||||
bitmaps.insert(
|
||||
*id,
|
||||
(&bitmap_data.data[..], bitmap_data.width, bitmap_data.height),
|
||||
);
|
||||
}
|
||||
|
||||
let data = swf_shape_to_canvas_commands(
|
||||
&shape,
|
||||
&bitmaps,
|
||||
self.pixelated_property_value,
|
||||
&self.context,
|
||||
)
|
||||
.unwrap_or_else(|| swf_shape_to_svg(shape, &bitmaps, self.pixelated_property_value));
|
||||
self.shapes[handle.0] = data;
|
||||
}
|
||||
|
||||
fn register_glyph_shape(&mut self, glyph: &swf::Glyph) -> ShapeHandle {
|
||||
// Per SWF19 p.164, the FontBoundsTable can contain empty bounds for every glyph (reserved).
|
||||
// SWF19 says this is true through SWFv7, but it seems like it might be generally true?
|
||||
|
@ -723,8 +743,8 @@ fn swf_shape_to_svg(
|
|||
pixelated_property_value: &str,
|
||||
) -> ShapeData {
|
||||
use fnv::FnvHashSet;
|
||||
use ruffle_core::matrix::Matrix;
|
||||
use ruffle_core::shape_utils::DrawPath;
|
||||
use ruffle_core::swf::Matrix;
|
||||
use svg::node::element::{
|
||||
path::Data, Definitions, Image, LinearGradient, Path as SvgPath, Pattern, RadialGradient,
|
||||
Stop,
|
||||
|
@ -782,7 +802,7 @@ fn swf_shape_to_svg(
|
|||
format!("rgba({},{},{},{})", r, g, b, f32::from(*a) / 255.0)
|
||||
}
|
||||
FillStyle::LinearGradient(gradient) => {
|
||||
let matrix: Matrix = Matrix::from(gradient.matrix.clone());
|
||||
let matrix: Matrix = gradient.matrix;
|
||||
let shift = Matrix {
|
||||
a: 32768.0 / width,
|
||||
d: 32768.0 / height,
|
||||
|
@ -829,7 +849,7 @@ fn swf_shape_to_svg(
|
|||
fill_id
|
||||
}
|
||||
FillStyle::RadialGradient(gradient) => {
|
||||
let matrix = Matrix::from(gradient.matrix.clone());
|
||||
let matrix = gradient.matrix;
|
||||
let shift = Matrix {
|
||||
a: 32768.0,
|
||||
d: 32768.0,
|
||||
|
@ -880,7 +900,7 @@ fn swf_shape_to_svg(
|
|||
gradient,
|
||||
focal_point,
|
||||
} => {
|
||||
let matrix = Matrix::from(gradient.matrix.clone());
|
||||
let matrix = gradient.matrix;
|
||||
let shift = Matrix {
|
||||
a: 32768.0,
|
||||
d: 32768.0,
|
||||
|
@ -970,7 +990,7 @@ fn swf_shape_to_svg(
|
|||
defs = defs.add(bitmap_pattern);
|
||||
bitmap_defs.insert(*id);
|
||||
}
|
||||
let a = Matrix::from(matrix.clone());
|
||||
let a = *matrix;
|
||||
let bitmap_matrix = a;
|
||||
|
||||
let svg_pattern = Pattern::new()
|
||||
|
@ -1140,7 +1160,6 @@ fn swf_shape_to_canvas_commands(
|
|||
_pixelated_property_value: &str,
|
||||
context: &CanvasRenderingContext2d,
|
||||
) -> Option<ShapeData> {
|
||||
use ruffle_core::matrix::Matrix;
|
||||
use ruffle_core::shape_utils::DrawPath;
|
||||
use swf::{FillStyle, LineCapStyle, LineJoinStyle};
|
||||
|
||||
|
@ -1220,7 +1239,7 @@ fn swf_shape_to_canvas_commands(
|
|||
// when cached? (Issue #412)
|
||||
image.set_src(*bitmap_data);
|
||||
|
||||
let a = Matrix::from(matrix.clone());
|
||||
let a = *matrix;
|
||||
|
||||
let matrix = matrix_factory.create_svg_matrix();
|
||||
|
||||
|
|
|
@ -99,7 +99,7 @@ impl ShapeTessellator {
|
|||
ratios,
|
||||
colors,
|
||||
num_colors: gradient.records.len() as u32,
|
||||
matrix: swf_to_gl_matrix(gradient.matrix.clone()),
|
||||
matrix: swf_to_gl_matrix(gradient.matrix),
|
||||
repeat_mode: gradient.spread,
|
||||
focal_point: 0.0,
|
||||
};
|
||||
|
@ -141,7 +141,7 @@ impl ShapeTessellator {
|
|||
ratios,
|
||||
colors,
|
||||
num_colors: gradient.records.len() as u32,
|
||||
matrix: swf_to_gl_matrix(gradient.matrix.clone()),
|
||||
matrix: swf_to_gl_matrix(gradient.matrix),
|
||||
repeat_mode: gradient.spread,
|
||||
focal_point: 0.0,
|
||||
};
|
||||
|
@ -186,7 +186,7 @@ impl ShapeTessellator {
|
|||
ratios,
|
||||
colors,
|
||||
num_colors: gradient.records.len() as u32,
|
||||
matrix: swf_to_gl_matrix(gradient.matrix.clone()),
|
||||
matrix: swf_to_gl_matrix(gradient.matrix),
|
||||
repeat_mode: gradient.spread,
|
||||
focal_point: *focal_point,
|
||||
};
|
||||
|
@ -220,11 +220,7 @@ impl ShapeTessellator {
|
|||
(get_bitmap_dimensions)(*id).unwrap_or((1, 1));
|
||||
|
||||
let bitmap = Bitmap {
|
||||
matrix: swf_bitmap_to_gl_matrix(
|
||||
matrix.clone(),
|
||||
bitmap_width,
|
||||
bitmap_height,
|
||||
),
|
||||
matrix: swf_bitmap_to_gl_matrix(*matrix, bitmap_width, bitmap_height),
|
||||
id: *id,
|
||||
is_smoothed: *is_smoothed,
|
||||
is_repeating: *is_repeating,
|
||||
|
@ -342,15 +338,15 @@ pub struct Bitmap {
|
|||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn swf_to_gl_matrix(m: swf::Matrix) -> [[f32; 3]; 3] {
|
||||
let tx = m.translate_x.get() as f32;
|
||||
let ty = m.translate_y.get() as f32;
|
||||
let det = m.scale_x * m.scale_y - m.rotate_skew_1 * m.rotate_skew_0;
|
||||
let mut a = m.scale_y / det;
|
||||
let mut b = -m.rotate_skew_1 / det;
|
||||
let mut c = -(tx * m.scale_y - m.rotate_skew_1 * ty) / det;
|
||||
let mut d = -m.rotate_skew_0 / det;
|
||||
let mut e = m.scale_x / det;
|
||||
let mut f = (tx * m.rotate_skew_0 - m.scale_x * ty) / det;
|
||||
let tx = m.tx.get() as f32;
|
||||
let ty = m.ty.get() as f32;
|
||||
let det = m.a * m.d - m.c * m.b;
|
||||
let mut a = m.d / det;
|
||||
let mut b = -m.c / det;
|
||||
let mut c = -(tx * m.d - m.c * ty) / det;
|
||||
let mut d = -m.b / det;
|
||||
let mut e = m.a / det;
|
||||
let mut f = (tx * m.b - m.a * ty) / det;
|
||||
|
||||
a *= 20.0 / 32768.0;
|
||||
b *= 20.0 / 32768.0;
|
||||
|
@ -369,15 +365,15 @@ fn swf_bitmap_to_gl_matrix(m: swf::Matrix, bitmap_width: u32, bitmap_height: u32
|
|||
let bitmap_width = bitmap_width as f32;
|
||||
let bitmap_height = bitmap_height as f32;
|
||||
|
||||
let tx = m.translate_x.get() as f32;
|
||||
let ty = m.translate_y.get() as f32;
|
||||
let det = m.scale_x * m.scale_y - m.rotate_skew_1 * m.rotate_skew_0;
|
||||
let mut a = m.scale_y / det;
|
||||
let mut b = -m.rotate_skew_1 / det;
|
||||
let mut c = -(tx * m.scale_y - m.rotate_skew_1 * ty) / det;
|
||||
let mut d = -m.rotate_skew_0 / det;
|
||||
let mut e = m.scale_x / det;
|
||||
let mut f = (tx * m.rotate_skew_0 - m.scale_x * ty) / det;
|
||||
let tx = m.tx.get() as f32;
|
||||
let ty = m.ty.get() as f32;
|
||||
let det = m.a * m.d - m.c * m.b;
|
||||
let mut a = m.d / det;
|
||||
let mut b = -m.c / det;
|
||||
let mut c = -(tx * m.d - m.c * ty) / det;
|
||||
let mut d = -m.b / det;
|
||||
let mut e = m.a / det;
|
||||
let mut f = (tx * m.b - m.a * ty) / det;
|
||||
|
||||
a *= 20.0 / bitmap_width;
|
||||
b *= 20.0 / bitmap_width;
|
||||
|
|
|
@ -423,11 +423,9 @@ impl WebGlRenderBackend {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn register_shape_internal(&mut self, shape: DistilledShape) -> ShapeHandle {
|
||||
fn register_shape_internal(&mut self, shape: DistilledShape) -> Mesh {
|
||||
use ruffle_render_common_tess::DrawType as TessDrawType;
|
||||
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
|
||||
let textures = &self.textures;
|
||||
let lyon_mesh = self.shape_tessellator.tessellate_shape(shape, |id| {
|
||||
textures
|
||||
|
@ -573,9 +571,7 @@ impl WebGlRenderBackend {
|
|||
}
|
||||
}
|
||||
|
||||
self.meshes.push(Mesh { draws });
|
||||
|
||||
handle
|
||||
Mesh { draws }
|
||||
}
|
||||
|
||||
fn build_matrices(&mut self) {
|
||||
|
@ -718,7 +714,15 @@ impl RenderBackend for WebGlRenderBackend {
|
|||
}
|
||||
|
||||
fn register_shape(&mut self, shape: DistilledShape) -> ShapeHandle {
|
||||
self.register_shape_internal(shape)
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
let mesh = self.register_shape_internal(shape);
|
||||
self.meshes.push(mesh);
|
||||
handle
|
||||
}
|
||||
|
||||
fn replace_shape(&mut self, shape: DistilledShape, handle: ShapeHandle) {
|
||||
let mesh = self.register_shape_internal(shape);
|
||||
self.meshes[handle.0] = mesh;
|
||||
}
|
||||
|
||||
fn register_glyph_shape(&mut self, glyph: &swf::Glyph) -> ShapeHandle {
|
||||
|
@ -741,7 +745,10 @@ impl RenderBackend for WebGlRenderBackend {
|
|||
},
|
||||
shape: glyph.shape_records.clone(),
|
||||
};
|
||||
self.register_shape_internal((&shape).into())
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
let mesh = self.register_shape_internal((&shape).into());
|
||||
self.meshes.push(mesh);
|
||||
handle
|
||||
}
|
||||
|
||||
fn register_bitmap_jpeg(
|
||||
|
@ -904,7 +911,7 @@ impl RenderBackend for WebGlRenderBackend {
|
|||
}
|
||||
|
||||
// Scale the quad to the bitmap's dimensions.
|
||||
use ruffle_core::matrix::Matrix;
|
||||
use ruffle_core::swf::Matrix;
|
||||
let scale_transform = Transform {
|
||||
matrix: transform.matrix
|
||||
* Matrix {
|
||||
|
|
|
@ -226,9 +226,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn register_shape_internal(&mut self, shape: DistilledShape) -> ShapeHandle {
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
|
||||
fn register_shape_internal(&mut self, shape: DistilledShape) -> Mesh {
|
||||
use lyon::tessellation::{FillOptions, StrokeOptions};
|
||||
|
||||
let transforms_label = create_debug_label!("Shape {} transforms ubo", shape.id);
|
||||
|
@ -377,7 +375,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
repeat_mode: gradient_spread_mode_index(gradient.spread),
|
||||
focal_point: 0.0,
|
||||
};
|
||||
let matrix = swf_to_gl_matrix(gradient.matrix.clone());
|
||||
let matrix = swf_to_gl_matrix(gradient.matrix);
|
||||
|
||||
flush_draw(
|
||||
shape.id,
|
||||
|
@ -446,7 +444,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
repeat_mode: gradient_spread_mode_index(gradient.spread),
|
||||
focal_point: 0.0,
|
||||
};
|
||||
let matrix = swf_to_gl_matrix(gradient.matrix.clone());
|
||||
let matrix = swf_to_gl_matrix(gradient.matrix);
|
||||
|
||||
flush_draw(
|
||||
shape.id,
|
||||
|
@ -518,7 +516,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
repeat_mode: gradient_spread_mode_index(gradient.spread),
|
||||
focal_point: *focal_point,
|
||||
};
|
||||
let matrix = swf_to_gl_matrix(gradient.matrix.clone());
|
||||
let matrix = swf_to_gl_matrix(gradient.matrix);
|
||||
|
||||
flush_draw(
|
||||
shape.id,
|
||||
|
@ -585,7 +583,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
shape.id,
|
||||
IncompleteDrawType::Bitmap {
|
||||
texture_transform: swf_bitmap_to_gl_matrix(
|
||||
matrix.clone(),
|
||||
*matrix,
|
||||
texture.width,
|
||||
texture.height,
|
||||
),
|
||||
|
@ -671,15 +669,13 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
&self.pipelines,
|
||||
);
|
||||
|
||||
self.meshes.push(Mesh {
|
||||
Mesh {
|
||||
draws,
|
||||
transforms: transforms_ubo,
|
||||
colors_buffer: colors_ubo,
|
||||
colors_last: ColorTransform::default(),
|
||||
shape_id: shape.id,
|
||||
});
|
||||
|
||||
handle
|
||||
}
|
||||
}
|
||||
|
||||
fn register_bitmap(
|
||||
|
@ -928,7 +924,15 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
}
|
||||
|
||||
fn register_shape(&mut self, shape: DistilledShape) -> ShapeHandle {
|
||||
self.register_shape_internal(shape)
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
let mesh = self.register_shape_internal(shape);
|
||||
self.meshes.push(mesh);
|
||||
handle
|
||||
}
|
||||
|
||||
fn replace_shape(&mut self, shape: DistilledShape, handle: ShapeHandle) {
|
||||
let mesh = self.register_shape_internal(shape);
|
||||
self.meshes[handle.0] = mesh;
|
||||
}
|
||||
|
||||
fn register_glyph_shape(&mut self, glyph: &Glyph) -> ShapeHandle {
|
||||
|
@ -951,7 +955,10 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
},
|
||||
shape: glyph.shape_records.clone(),
|
||||
};
|
||||
self.register_shape_internal((&shape).into())
|
||||
let handle = ShapeHandle(self.meshes.len());
|
||||
let mesh = self.register_shape_internal((&shape).into());
|
||||
self.meshes.push(mesh);
|
||||
handle
|
||||
}
|
||||
|
||||
fn register_bitmap_jpeg(
|
||||
|
@ -1050,7 +1057,7 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
return;
|
||||
};
|
||||
|
||||
use ruffle_core::matrix::Matrix;
|
||||
use ruffle_core::swf::Matrix;
|
||||
let transform = Transform {
|
||||
matrix: transform.matrix
|
||||
* Matrix {
|
||||
|
|
|
@ -56,15 +56,15 @@ pub fn ruffle_path_to_lyon_path(commands: Vec<DrawCommand>, is_closed: bool) ->
|
|||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
pub fn swf_to_gl_matrix(m: swf::Matrix) -> [[f32; 4]; 4] {
|
||||
let tx = m.translate_x.get() as f32;
|
||||
let ty = m.translate_y.get() as f32;
|
||||
let det = m.scale_x * m.scale_y - m.rotate_skew_1 * m.rotate_skew_0;
|
||||
let mut a = m.scale_y / det;
|
||||
let mut b = -m.rotate_skew_1 / det;
|
||||
let mut c = -(tx * m.scale_y - m.rotate_skew_1 * ty) / det;
|
||||
let mut d = -m.rotate_skew_0 / det;
|
||||
let mut e = m.scale_x / det;
|
||||
let mut f = (tx * m.rotate_skew_0 - m.scale_x * ty) / det;
|
||||
let tx = m.tx.get() as f32;
|
||||
let ty = m.ty.get() as f32;
|
||||
let det = m.a * m.d - m.c * m.b;
|
||||
let mut a = m.d / det;
|
||||
let mut b = -m.c / det;
|
||||
let mut c = -(tx * m.d - m.c * ty) / det;
|
||||
let mut d = -m.b / det;
|
||||
let mut e = m.a / det;
|
||||
let mut f = (tx * m.b - m.a * ty) / det;
|
||||
|
||||
a *= 20.0 / 32768.0;
|
||||
b *= 20.0 / 32768.0;
|
||||
|
@ -92,15 +92,15 @@ pub fn swf_bitmap_to_gl_matrix(
|
|||
let bitmap_width = bitmap_width as f32;
|
||||
let bitmap_height = bitmap_height as f32;
|
||||
|
||||
let tx = m.translate_x.get() as f32;
|
||||
let ty = m.translate_y.get() as f32;
|
||||
let det = m.scale_x * m.scale_y - m.rotate_skew_1 * m.rotate_skew_0;
|
||||
let mut a = m.scale_y / det;
|
||||
let mut b = -m.rotate_skew_1 / det;
|
||||
let mut c = -(tx * m.scale_y - m.rotate_skew_1 * ty) / det;
|
||||
let mut d = -m.rotate_skew_0 / det;
|
||||
let mut e = m.scale_x / det;
|
||||
let mut f = (tx * m.rotate_skew_0 - m.scale_x * ty) / det;
|
||||
let tx = m.tx.get() as f32;
|
||||
let ty = m.ty.get() as f32;
|
||||
let det = m.a * m.d - m.c * m.b;
|
||||
let mut a = m.d / det;
|
||||
let mut b = -m.c / det;
|
||||
let mut c = -(tx * m.d - m.c * ty) / det;
|
||||
let mut d = -m.b / det;
|
||||
let mut e = m.a / det;
|
||||
let mut f = (tx * m.b - m.a * ty) / det;
|
||||
|
||||
a *= 20.0 / bitmap_width;
|
||||
b *= 20.0 / bitmap_width;
|
||||
|
|
|
@ -19,6 +19,9 @@ log = "0.4"
|
|||
flate2 = {version = "1.0", optional = true}
|
||||
xz2 = {version = "0.1.6", optional = true}
|
||||
|
||||
[dev-dependencies]
|
||||
approx = "0.3.2"
|
||||
|
||||
[features]
|
||||
default = ["libflate"]
|
||||
lzma = ["xz2"]
|
|
@ -830,23 +830,23 @@ impl<R: Read> Reader<R> {
|
|||
|
||||
fn read_matrix(&mut self) -> Result<Matrix> {
|
||||
self.byte_align();
|
||||
let mut m = Matrix::new();
|
||||
let mut m = Matrix::identity();
|
||||
// Scale
|
||||
if self.read_bit()? {
|
||||
let num_bits = self.read_ubits(5)? as usize;
|
||||
m.scale_x = self.read_fbits(num_bits)?;
|
||||
m.scale_y = self.read_fbits(num_bits)?;
|
||||
m.a = self.read_fbits(num_bits)?;
|
||||
m.d = self.read_fbits(num_bits)?;
|
||||
}
|
||||
// Rotate/Skew
|
||||
if self.read_bit()? {
|
||||
let num_bits = self.read_ubits(5)? as usize;
|
||||
m.rotate_skew_0 = self.read_fbits(num_bits)?;
|
||||
m.rotate_skew_1 = self.read_fbits(num_bits)?;
|
||||
m.b = self.read_fbits(num_bits)?;
|
||||
m.c = self.read_fbits(num_bits)?;
|
||||
}
|
||||
// Translate (always present)
|
||||
let num_bits = self.read_ubits(5)? as usize;
|
||||
m.translate_x = self.read_sbits_twips(num_bits)?;
|
||||
m.translate_y = self.read_sbits_twips(num_bits)?;
|
||||
m.tx = self.read_sbits_twips(num_bits)?;
|
||||
m.ty = self.read_sbits_twips(num_bits)?;
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
|
@ -3092,12 +3092,12 @@ pub mod tests {
|
|||
assert_eq!(
|
||||
matrix,
|
||||
Matrix {
|
||||
translate_x: Twips::from_pixels(0.0),
|
||||
translate_y: Twips::from_pixels(0.0),
|
||||
scale_x: 1f32,
|
||||
scale_y: 1f32,
|
||||
rotate_skew_0: 0f32,
|
||||
rotate_skew_1: 0f32,
|
||||
tx: Twips::from_pixels(0.0),
|
||||
ty: Twips::from_pixels(0.0),
|
||||
a: 1f32,
|
||||
d: 1f32,
|
||||
b: 0f32,
|
||||
c: 0f32,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -3173,7 +3173,7 @@ pub mod tests {
|
|||
|
||||
let fill_style = FillStyle::Bitmap {
|
||||
id: 20,
|
||||
matrix: Matrix::new(),
|
||||
matrix: Matrix::identity(),
|
||||
is_smoothed: false,
|
||||
is_repeating: true,
|
||||
};
|
||||
|
@ -3182,8 +3182,8 @@ pub mod tests {
|
|||
fill_style
|
||||
);
|
||||
|
||||
let mut matrix = Matrix::new();
|
||||
matrix.translate_x = Twips::from_pixels(1.0);
|
||||
let mut matrix = Matrix::identity();
|
||||
matrix.tx = Twips::from_pixels(1.0);
|
||||
let fill_style = FillStyle::Bitmap {
|
||||
id: 33,
|
||||
matrix,
|
||||
|
|
|
@ -203,7 +203,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
.into_iter()
|
||||
.collect(),
|
||||
depth: 1,
|
||||
matrix: Matrix::new(),
|
||||
matrix: Matrix::identity(),
|
||||
color_transform: ColorTransform::new(),
|
||||
filters: vec![],
|
||||
blend_mode: BlendMode::Normal,
|
||||
|
@ -214,7 +214,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
.into_iter()
|
||||
.collect(),
|
||||
depth: 1,
|
||||
matrix: Matrix::new(),
|
||||
matrix: Matrix::identity(),
|
||||
color_transform: ColorTransform::new(),
|
||||
filters: vec![],
|
||||
blend_mode: BlendMode::Normal,
|
||||
|
@ -242,7 +242,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
.into_iter()
|
||||
.collect(),
|
||||
depth: 1,
|
||||
matrix: Matrix::new(),
|
||||
matrix: Matrix::identity(),
|
||||
color_transform: ColorTransform {
|
||||
r_multiply: 1f32,
|
||||
g_multiply: 1f32,
|
||||
|
@ -266,7 +266,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
.into_iter()
|
||||
.collect(),
|
||||
depth: 1,
|
||||
matrix: Matrix::new(),
|
||||
matrix: Matrix::identity(),
|
||||
color_transform: ColorTransform {
|
||||
r_multiply: 0f32,
|
||||
g_multiply: 1f32,
|
||||
|
@ -737,12 +737,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
},
|
||||
fill_styles: vec![FillStyle::LinearGradient(Gradient {
|
||||
matrix: Matrix {
|
||||
translate_x: Twips::from_pixels(40.0),
|
||||
translate_y: Twips::from_pixels(40.0),
|
||||
scale_x: 0.024429321,
|
||||
scale_y: 0.024429321,
|
||||
rotate_skew_0: 0.024429321,
|
||||
rotate_skew_1: -0.024429321,
|
||||
tx: Twips::from_pixels(40.0),
|
||||
ty: Twips::from_pixels(40.0),
|
||||
a: 0.024429321,
|
||||
d: 0.024429321,
|
||||
b: 0.024429321,
|
||||
c: -0.024429321,
|
||||
},
|
||||
spread: GradientSpread::Pad,
|
||||
interpolation: GradientInterpolation::RGB,
|
||||
|
@ -840,12 +840,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
},
|
||||
fill_styles: vec![FillStyle::LinearGradient(Gradient {
|
||||
matrix: Matrix {
|
||||
translate_x: Twips::from_pixels(48.4),
|
||||
translate_y: Twips::from_pixels(34.65),
|
||||
scale_x: 0.0058898926,
|
||||
scale_y: 0.030914307,
|
||||
rotate_skew_0: 0.0,
|
||||
rotate_skew_1: 0.0,
|
||||
tx: Twips::from_pixels(48.4),
|
||||
ty: Twips::from_pixels(34.65),
|
||||
a: 0.0058898926,
|
||||
d: 0.030914307,
|
||||
b: 0.0,
|
||||
c: 0.0,
|
||||
},
|
||||
spread: GradientSpread::Pad,
|
||||
interpolation: GradientInterpolation::RGB,
|
||||
|
@ -958,12 +958,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
fill_styles: vec![FillStyle::FocalGradient {
|
||||
gradient: Gradient {
|
||||
matrix: Matrix {
|
||||
translate_x: Twips::from_pixels(116.05),
|
||||
translate_y: Twips::from_pixels(135.05),
|
||||
scale_x: 0.11468506,
|
||||
scale_y: 0.18927002,
|
||||
rotate_skew_0: 0.0,
|
||||
rotate_skew_1: 0.0,
|
||||
tx: Twips::from_pixels(116.05),
|
||||
ty: Twips::from_pixels(135.05),
|
||||
a: 0.11468506,
|
||||
d: 0.18927002,
|
||||
b: 0.0,
|
||||
c: 0.0,
|
||||
},
|
||||
spread: GradientSpread::Pad,
|
||||
interpolation: GradientInterpolation::RGB,
|
||||
|
@ -1081,12 +1081,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
fill_styles: vec![FillStyle::FocalGradient {
|
||||
gradient: Gradient {
|
||||
matrix: Matrix {
|
||||
translate_x: Twips::from_pixels(164.0),
|
||||
translate_y: Twips::from_pixels(150.05),
|
||||
scale_x: 0.036087036,
|
||||
scale_y: 0.041992188,
|
||||
rotate_skew_0: 0.1347351,
|
||||
rotate_skew_1: -0.15675354,
|
||||
tx: Twips::from_pixels(164.0),
|
||||
ty: Twips::from_pixels(150.05),
|
||||
a: 0.036087036,
|
||||
d: 0.041992188,
|
||||
b: 0.1347351,
|
||||
c: -0.15675354,
|
||||
},
|
||||
spread: GradientSpread::Pad,
|
||||
interpolation: GradientInterpolation::RGB,
|
||||
|
@ -1217,12 +1217,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
},
|
||||
fill_styles: vec![FillStyle::RadialGradient(Gradient {
|
||||
matrix: Matrix {
|
||||
translate_x: Twips::from_pixels(100.00),
|
||||
translate_y: Twips::from_pixels(100.00),
|
||||
scale_x: 0.1725769,
|
||||
scale_y: 0.1725769,
|
||||
rotate_skew_0: 0.0,
|
||||
rotate_skew_1: 0.0,
|
||||
tx: Twips::from_pixels(100.00),
|
||||
ty: Twips::from_pixels(100.00),
|
||||
a: 0.1725769,
|
||||
d: 0.1725769,
|
||||
b: 0.0,
|
||||
c: 0.0,
|
||||
},
|
||||
spread: GradientSpread::Reflect,
|
||||
interpolation: GradientInterpolation::LinearRGB,
|
||||
|
@ -1305,12 +1305,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
},
|
||||
fill_styles: vec![FillStyle::RadialGradient(Gradient {
|
||||
matrix: Matrix {
|
||||
translate_x: Twips::from_pixels(100.00),
|
||||
translate_y: Twips::from_pixels(100.00),
|
||||
scale_x: 0.000015258789,
|
||||
scale_y: 0.000015258789,
|
||||
rotate_skew_0: 0.084503174,
|
||||
rotate_skew_1: -0.084503174,
|
||||
tx: Twips::from_pixels(100.00),
|
||||
ty: Twips::from_pixels(100.00),
|
||||
a: 0.000015258789,
|
||||
d: 0.000015258789,
|
||||
b: 0.084503174,
|
||||
c: -0.084503174,
|
||||
},
|
||||
spread: GradientSpread::Reflect,
|
||||
interpolation: GradientInterpolation::LinearRGB,
|
||||
|
@ -1514,12 +1514,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
styles: ShapeStyles {
|
||||
fill_styles: vec![FillStyle::RadialGradient(Gradient {
|
||||
matrix: Matrix {
|
||||
translate_x: Twips::from_pixels(24.95),
|
||||
translate_y: Twips::from_pixels(24.95),
|
||||
scale_x: 0.030731201f32,
|
||||
scale_y: 0.030731201f32,
|
||||
rotate_skew_0: 0f32,
|
||||
rotate_skew_1: 0f32,
|
||||
tx: Twips::from_pixels(24.95),
|
||||
ty: Twips::from_pixels(24.95),
|
||||
a: 0.030731201f32,
|
||||
d: 0.030731201f32,
|
||||
b: 0f32,
|
||||
c: 0f32,
|
||||
},
|
||||
spread: GradientSpread::Pad,
|
||||
interpolation: GradientInterpolation::RGB,
|
||||
|
@ -1637,12 +1637,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
FillStyle::FocalGradient {
|
||||
gradient: Gradient {
|
||||
matrix: Matrix {
|
||||
translate_x: Twips::from_pixels(49.55),
|
||||
translate_y: Twips::from_pixels(46.55),
|
||||
scale_x: 0.06199646f32,
|
||||
scale_y: 0.06199646f32,
|
||||
rotate_skew_0: 0f32,
|
||||
rotate_skew_1: 0f32,
|
||||
tx: Twips::from_pixels(49.55),
|
||||
ty: Twips::from_pixels(46.55),
|
||||
a: 0.06199646f32,
|
||||
d: 0.06199646f32,
|
||||
b: 0f32,
|
||||
c: 0f32,
|
||||
},
|
||||
spread: GradientSpread::Pad,
|
||||
interpolation: GradientInterpolation::LinearRGB,
|
||||
|
@ -1701,12 +1701,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
join_style: LineJoinStyle::Round,
|
||||
fill_style: Some(FillStyle::LinearGradient(Gradient {
|
||||
matrix: Matrix {
|
||||
translate_x: Twips::from_pixels(50.0),
|
||||
translate_y: Twips::from_pixels(50.0),
|
||||
scale_x: 0.07324219f32,
|
||||
scale_y: 0.07324219f32,
|
||||
rotate_skew_0: 0f32,
|
||||
rotate_skew_1: 0f32,
|
||||
tx: Twips::from_pixels(50.0),
|
||||
ty: Twips::from_pixels(50.0),
|
||||
a: 0.07324219f32,
|
||||
d: 0.07324219f32,
|
||||
b: 0f32,
|
||||
c: 0f32,
|
||||
},
|
||||
spread: GradientSpread::Pad,
|
||||
interpolation: GradientInterpolation::RGB,
|
||||
|
@ -1888,7 +1888,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
y_min: Twips::from_pixels(4.1),
|
||||
y_max: Twips::from_pixels(18.45),
|
||||
},
|
||||
matrix: Matrix::new(),
|
||||
matrix: Matrix::identity(),
|
||||
records: vec![TextRecord {
|
||||
font_id: Some(1),
|
||||
color: Some(Color {
|
||||
|
@ -2090,7 +2090,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
version: 2,
|
||||
action: PlaceObjectAction::Place(1),
|
||||
depth: 1,
|
||||
matrix: Some(Matrix::new()),
|
||||
matrix: Some(Matrix::identity()),
|
||||
color_transform: None,
|
||||
ratio: None,
|
||||
name: None,
|
||||
|
@ -2113,7 +2113,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
version: 2,
|
||||
action: PlaceObjectAction::Place(2),
|
||||
depth: 1,
|
||||
matrix: Some(Matrix::new()),
|
||||
matrix: Some(Matrix::identity()),
|
||||
color_transform: None,
|
||||
ratio: None,
|
||||
name: None,
|
||||
|
@ -2143,7 +2143,7 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
version: 2,
|
||||
action: PlaceObjectAction::Place(2),
|
||||
depth: 1,
|
||||
matrix: Some(Matrix::new()),
|
||||
matrix: Some(Matrix::identity()),
|
||||
color_transform: None,
|
||||
ratio: None,
|
||||
name: None,
|
||||
|
@ -2186,12 +2186,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
action: PlaceObjectAction::Place(1),
|
||||
depth: 1,
|
||||
matrix: Some(Matrix {
|
||||
translate_x: Twips::from_pixels(0.0),
|
||||
translate_y: Twips::from_pixels(0.0),
|
||||
rotate_skew_0: 0f32,
|
||||
rotate_skew_1: 0f32,
|
||||
scale_x: 1.0f32,
|
||||
scale_y: 1.0f32,
|
||||
tx: Twips::from_pixels(0.0),
|
||||
ty: Twips::from_pixels(0.0),
|
||||
b: 0f32,
|
||||
c: 0f32,
|
||||
a: 1.0f32,
|
||||
d: 1.0f32,
|
||||
}),
|
||||
color_transform: None,
|
||||
ratio: None,
|
||||
|
@ -2216,12 +2216,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
action: PlaceObjectAction::Place(2),
|
||||
depth: 1,
|
||||
matrix: Some(Matrix {
|
||||
translate_x: Twips::from_pixels(10.0),
|
||||
translate_y: Twips::from_pixels(10.0),
|
||||
rotate_skew_0: 0f32,
|
||||
rotate_skew_1: 0f32,
|
||||
scale_x: 2.0f32,
|
||||
scale_y: 2.0f32,
|
||||
tx: Twips::from_pixels(10.0),
|
||||
ty: Twips::from_pixels(10.0),
|
||||
b: 0f32,
|
||||
c: 0f32,
|
||||
a: 2.0f32,
|
||||
d: 2.0f32,
|
||||
}),
|
||||
color_transform: Some(ColorTransform {
|
||||
a_multiply: 1.0f32,
|
||||
|
@ -2352,12 +2352,12 @@ pub fn tag_tests() -> Vec<TagTestData> {
|
|||
action: PlaceObjectAction::Place(2),
|
||||
depth: 1,
|
||||
matrix: Some(Matrix {
|
||||
translate_x: Twips::from_pixels(10.0),
|
||||
translate_y: Twips::from_pixels(10.0),
|
||||
rotate_skew_0: 0.0,
|
||||
rotate_skew_1: 0.0,
|
||||
scale_x: 1.0,
|
||||
scale_y: 1.0,
|
||||
tx: Twips::from_pixels(10.0),
|
||||
ty: Twips::from_pixels(10.0),
|
||||
b: 0.0,
|
||||
c: 0.0,
|
||||
a: 1.0,
|
||||
d: 1.0,
|
||||
}),
|
||||
color_transform: None,
|
||||
ratio: None,
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
use enumset::{EnumSet, EnumSetType};
|
||||
use std::collections::HashSet;
|
||||
|
||||
mod matrix;
|
||||
|
||||
pub use matrix::Matrix;
|
||||
|
||||
/// A complete header and tags in the SWF file.
|
||||
/// This is returned by the `swf::read_swf` convenience method.
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -68,6 +72,11 @@ impl Twips {
|
|||
Self(twips.into())
|
||||
}
|
||||
|
||||
/// Creates a new `Twips` object set to the value of 0.
|
||||
pub fn zero() -> Self {
|
||||
Self(0)
|
||||
}
|
||||
|
||||
/// Returns the number of twips.
|
||||
pub fn get(self) -> i32 {
|
||||
self.0
|
||||
|
@ -206,35 +215,6 @@ impl Default for ColorTransform {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Matrix {
|
||||
pub translate_x: Twips,
|
||||
pub translate_y: Twips,
|
||||
pub scale_x: f32,
|
||||
pub scale_y: f32,
|
||||
pub rotate_skew_0: f32,
|
||||
pub rotate_skew_1: f32,
|
||||
}
|
||||
|
||||
impl Matrix {
|
||||
pub fn new() -> Matrix {
|
||||
Matrix {
|
||||
translate_x: Default::default(),
|
||||
translate_y: Default::default(),
|
||||
scale_x: 1f32,
|
||||
scale_y: 1f32,
|
||||
rotate_skew_0: 0f32,
|
||||
rotate_skew_1: 0f32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Matrix {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum Language {
|
||||
Unknown,
|
||||
|
|
|
@ -1,16 +1,115 @@
|
|||
use swf::Twips;
|
||||
use crate::Twips;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Matrix {
|
||||
/// Serialized as `scale_x` in SWF files
|
||||
pub a: f32,
|
||||
|
||||
/// Serialized as `rotate_skew_0` in SWF files
|
||||
pub b: f32,
|
||||
|
||||
/// Serialized as `rotate_skew_1` in SWF files
|
||||
pub c: f32,
|
||||
|
||||
/// Serialized as `scale_y` in SWF files
|
||||
pub d: f32,
|
||||
|
||||
/// Serialized as `transform_x` in SWF files
|
||||
pub tx: Twips,
|
||||
|
||||
/// Serialized as `transform_y` in SWF files
|
||||
pub ty: Twips,
|
||||
}
|
||||
|
||||
impl Matrix {
|
||||
pub fn identity() -> Self {
|
||||
Self {
|
||||
a: 1.0,
|
||||
c: 0.0,
|
||||
tx: Twips::new(0),
|
||||
b: 0.0,
|
||||
d: 1.0,
|
||||
ty: Twips::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scale(scale_x: f32, scale_y: f32) -> Self {
|
||||
Self {
|
||||
a: scale_x,
|
||||
c: 0.0,
|
||||
tx: Twips::new(0),
|
||||
b: 0.0,
|
||||
d: scale_y,
|
||||
ty: Twips::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rotate(angle: f32) -> Self {
|
||||
Self {
|
||||
a: angle.cos(),
|
||||
c: -angle.sin(),
|
||||
tx: Twips::new(0),
|
||||
b: angle.sin(),
|
||||
d: angle.cos(),
|
||||
ty: Twips::new(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn translate(x: Twips, y: Twips) -> Self {
|
||||
Self {
|
||||
a: 1.0,
|
||||
c: 0.0,
|
||||
tx: x,
|
||||
b: 0.0,
|
||||
d: 1.0,
|
||||
ty: y,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_box(
|
||||
scale_x: f32,
|
||||
scale_y: f32,
|
||||
rotation: f32,
|
||||
translate_x: Twips,
|
||||
translate_y: Twips,
|
||||
) -> Self {
|
||||
if rotation != 0.0 {
|
||||
Self {
|
||||
a: rotation.cos() * scale_x,
|
||||
c: -rotation.sin() * scale_x,
|
||||
tx: translate_x,
|
||||
b: rotation.sin() * scale_y,
|
||||
d: rotation.cos() * scale_y,
|
||||
ty: translate_y,
|
||||
}
|
||||
} else {
|
||||
Self {
|
||||
a: scale_x,
|
||||
c: 0.0,
|
||||
tx: translate_x,
|
||||
b: 0.0,
|
||||
d: scale_y,
|
||||
ty: translate_y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_gradient_box(
|
||||
width: f32,
|
||||
height: f32,
|
||||
rotation: f32,
|
||||
translate_x: Twips,
|
||||
translate_y: Twips,
|
||||
) -> Self {
|
||||
Self::create_box(
|
||||
width / 1638.4,
|
||||
height / 1638.4,
|
||||
rotation,
|
||||
translate_x + Twips::from_pixels((width / 2.0) as f64),
|
||||
translate_y + Twips::from_pixels((height / 2.0) as f64),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn invert(&mut self) {
|
||||
let (tx, ty) = (self.tx.get() as f32, self.ty.get() as f32);
|
||||
let det = self.a * self.d - self.b * self.c;
|
||||
|
@ -33,19 +132,6 @@ impl Matrix {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<swf::Matrix> for Matrix {
|
||||
fn from(matrix: swf::Matrix) -> Matrix {
|
||||
Matrix {
|
||||
a: matrix.scale_x,
|
||||
b: matrix.rotate_skew_0,
|
||||
c: matrix.rotate_skew_1,
|
||||
d: matrix.scale_y,
|
||||
tx: matrix.translate_x,
|
||||
ty: matrix.translate_y,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Mul for Matrix {
|
||||
type Output = Self;
|
||||
fn mul(self, rhs: Self) -> Self {
|
||||
|
@ -77,14 +163,7 @@ impl std::ops::Mul<(Twips, Twips)> for Matrix {
|
|||
|
||||
impl std::default::Default for Matrix {
|
||||
fn default() -> Matrix {
|
||||
Matrix {
|
||||
a: 1.0,
|
||||
c: 0.0,
|
||||
tx: Twips::new(0),
|
||||
b: 0.0,
|
||||
d: 1.0,
|
||||
ty: Twips::new(0),
|
||||
}
|
||||
Matrix::identity()
|
||||
}
|
||||
}
|
||||
|
|
@ -475,31 +475,28 @@ impl<W: Write> Writer<W> {
|
|||
fn write_matrix(&mut self, m: &Matrix) -> Result<()> {
|
||||
self.flush_bits()?;
|
||||
// Scale
|
||||
let has_scale = m.scale_x != 1f32 || m.scale_y != 1f32;
|
||||
let has_scale = m.a != 1f32 || m.d != 1f32;
|
||||
self.write_bit(has_scale)?;
|
||||
if has_scale {
|
||||
let num_bits = max(count_fbits(m.scale_x), count_fbits(m.scale_y));
|
||||
let num_bits = max(count_fbits(m.a), count_fbits(m.d));
|
||||
self.write_ubits(5, num_bits.into())?;
|
||||
self.write_fbits(num_bits, m.scale_x)?;
|
||||
self.write_fbits(num_bits, m.scale_y)?;
|
||||
self.write_fbits(num_bits, m.a)?;
|
||||
self.write_fbits(num_bits, m.d)?;
|
||||
}
|
||||
// Rotate/Skew
|
||||
let has_rotate_skew = m.rotate_skew_0 != 0f32 || m.rotate_skew_1 != 0f32;
|
||||
let has_rotate_skew = m.b != 0f32 || m.c != 0f32;
|
||||
self.write_bit(has_rotate_skew)?;
|
||||
if has_rotate_skew {
|
||||
let num_bits = max(count_fbits(m.rotate_skew_0), count_fbits(m.rotate_skew_1));
|
||||
let num_bits = max(count_fbits(m.b), count_fbits(m.c));
|
||||
self.write_ubits(5, num_bits.into())?;
|
||||
self.write_fbits(num_bits, m.rotate_skew_0)?;
|
||||
self.write_fbits(num_bits, m.rotate_skew_1)?;
|
||||
self.write_fbits(num_bits, m.b)?;
|
||||
self.write_fbits(num_bits, m.c)?;
|
||||
}
|
||||
// Translate (always written)
|
||||
let num_bits = max(
|
||||
count_sbits_twips(m.translate_x),
|
||||
count_sbits_twips(m.translate_y),
|
||||
);
|
||||
let num_bits = max(count_sbits_twips(m.tx), count_sbits_twips(m.ty));
|
||||
self.write_ubits(5, num_bits.into())?;
|
||||
self.write_sbits_twips(num_bits, m.translate_x)?;
|
||||
self.write_sbits_twips(num_bits, m.translate_y)?;
|
||||
self.write_sbits_twips(num_bits, m.tx)?;
|
||||
self.write_sbits_twips(num_bits, m.ty)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -1911,7 +1908,7 @@ impl<W: Write> Writer<W> {
|
|||
if let Some(ref matrix) = place_object.matrix {
|
||||
writer.write_matrix(matrix)?;
|
||||
} else {
|
||||
writer.write_matrix(&Matrix::new())?;
|
||||
writer.write_matrix(&Matrix::identity())?;
|
||||
}
|
||||
if let Some(ref color_transform) = place_object.color_transform {
|
||||
writer.write_color_transform_no_alpha(color_transform)?;
|
||||
|
@ -3037,7 +3034,7 @@ mod tests {
|
|||
buf
|
||||
}
|
||||
|
||||
let m = Matrix::new();
|
||||
let m = Matrix::identity();
|
||||
assert_eq!(write_to_buf(&m), [0]);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue