core: Add initial drawing API to MovieClip
This commit is contained in:
parent
7ab6703fc9
commit
61e464099c
|
@ -7,9 +7,13 @@ use crate::avm1::{Avm1, Error, Object, ScriptObject, TObject, UpdateContext, Val
|
|||
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, Matrix, Twips,
|
||||
};
|
||||
|
||||
/// Implements `MovieClip`
|
||||
pub fn constructor<'gc>(
|
||||
|
@ -123,12 +127,332 @@ 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 = if matrix_object
|
||||
.get("matrixType", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.coerce_to_string(avm, context)?
|
||||
== "box"
|
||||
{
|
||||
let width = matrix_object
|
||||
.get("w", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?;
|
||||
let height = matrix_object
|
||||
.get("h", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?;
|
||||
let tx = matrix_object
|
||||
.get("x", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?
|
||||
+ width / 2.0;
|
||||
let ty = matrix_object
|
||||
.get("y", avm, context)?
|
||||
.resolve(avm, context)?
|
||||
.as_number(avm, context)?
|
||||
+ height / 2.0;
|
||||
// TODO: This is wrong, doesn't account for rotations.
|
||||
Matrix {
|
||||
translate_x: Twips::from_pixels(tx),
|
||||
translate_y: Twips::from_pixels(ty),
|
||||
scale_x: width as f32 / 1638.4,
|
||||
scale_y: height as f32 / 1638.4,
|
||||
rotate_skew_0: 0.0,
|
||||
rotate_skew_1: 0.0,
|
||||
}
|
||||
} else {
|
||||
log::warn!(
|
||||
"beginGradientFill() received unsupported matrix object {:?}",
|
||||
matrix_object
|
||||
);
|
||||
return Ok(Value::Undefined.into());
|
||||
};
|
||||
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>,
|
||||
|
|
|
@ -43,6 +43,22 @@ impl BoundingBox {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn encompass(&mut self, x: Twips, y: Twips) {
|
||||
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;
|
||||
}
|
||||
self.valid = true;
|
||||
}
|
||||
|
||||
pub fn union(&mut self, other: &BoundingBox) {
|
||||
use std::cmp::{max, min};
|
||||
if self.valid && other.valid {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! `MovieClip` display object and support code.
|
||||
use crate::avm1::{Avm1, Object, StageObject, TObject, Value};
|
||||
use crate::backend::audio::AudioStreamHandle;
|
||||
|
||||
use crate::backend::render::ShapeHandle;
|
||||
use crate::character::Character;
|
||||
use crate::context::{ActionType, RenderContext, UpdateContext};
|
||||
use crate::display_object::{
|
||||
|
@ -9,6 +11,7 @@ use crate::display_object::{
|
|||
use crate::events::{ButtonKeyCode, ClipEvent};
|
||||
use crate::font::Font;
|
||||
use crate::prelude::*;
|
||||
use crate::shape_utils::{DistilledShape, DrawCommand, DrawPath};
|
||||
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,14 @@ pub struct MovieClipData<'gc> {
|
|||
clip_actions: SmallVec<[ClipAction; 2]>,
|
||||
flags: EnumSet<MovieClipFlags>,
|
||||
avm1_constructor: Option<Object<'gc>>,
|
||||
custom_shape: Option<ShapeHandle>,
|
||||
custom_shape_bounds: BoundingBox,
|
||||
custom_edge_bounds: BoundingBox,
|
||||
dirty_shape: bool,
|
||||
custom_fills: Vec<(FillStyle, Vec<DrawCommand>)>,
|
||||
custom_lines: Vec<(LineStyle, Vec<DrawCommand>)>,
|
||||
current_fill: Option<(FillStyle, Vec<DrawCommand>)>,
|
||||
current_line: Option<(LineStyle, Vec<DrawCommand>)>,
|
||||
}
|
||||
|
||||
impl<'gc> MovieClip<'gc> {
|
||||
|
@ -60,6 +72,14 @@ impl<'gc> MovieClip<'gc> {
|
|||
clip_actions: SmallVec::new(),
|
||||
flags: EnumSet::empty(),
|
||||
avm1_constructor: None,
|
||||
custom_shape: None,
|
||||
custom_shape_bounds: BoundingBox::default(),
|
||||
custom_edge_bounds: BoundingBox::default(),
|
||||
dirty_shape: false,
|
||||
custom_fills: Vec::new(),
|
||||
custom_lines: Vec::new(),
|
||||
current_fill: None,
|
||||
current_line: None,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -92,6 +112,14 @@ impl<'gc> MovieClip<'gc> {
|
|||
clip_actions: SmallVec::new(),
|
||||
flags: MovieClipFlags::Playing.into(),
|
||||
avm1_constructor: None,
|
||||
custom_shape: None,
|
||||
custom_shape_bounds: BoundingBox::default(),
|
||||
custom_edge_bounds: BoundingBox::default(),
|
||||
dirty_shape: false,
|
||||
custom_fills: Vec::new(),
|
||||
custom_lines: Vec::new(),
|
||||
current_fill: None,
|
||||
current_line: None,
|
||||
},
|
||||
))
|
||||
}
|
||||
|
@ -550,6 +578,132 @@ 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);
|
||||
|
||||
// TODO: If current_fill is not closed, we should close it and also close current_line
|
||||
|
||||
if let Some(existing) = mc.current_fill.take() {
|
||||
mc.custom_fills.push(existing);
|
||||
}
|
||||
if let Some(style) = style {
|
||||
mc.current_fill = Some((style, Vec::new()));
|
||||
}
|
||||
|
||||
mc.dirty_shape = true;
|
||||
}
|
||||
|
||||
pub fn clear(self, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
mc.current_fill = None;
|
||||
mc.current_line = None;
|
||||
mc.custom_fills.clear();
|
||||
mc.custom_lines.clear();
|
||||
mc.custom_edge_bounds = BoundingBox::default();
|
||||
mc.custom_shape_bounds = BoundingBox::default();
|
||||
mc.dirty_shape = true;
|
||||
}
|
||||
|
||||
pub fn set_line_style(
|
||||
self,
|
||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||
style: Option<LineStyle>,
|
||||
) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
|
||||
if let Some(existing) = mc.current_line.take() {
|
||||
mc.custom_lines.push(existing);
|
||||
}
|
||||
if let Some(style) = style {
|
||||
mc.current_line = Some((style, Vec::new()));
|
||||
}
|
||||
|
||||
mc.dirty_shape = true;
|
||||
}
|
||||
|
||||
pub fn draw_command(self, context: &mut UpdateContext<'_, 'gc, '_>, command: DrawCommand) {
|
||||
let mut mc = self.0.write(context.gc_context);
|
||||
|
||||
let mut include_last = false;
|
||||
|
||||
match command {
|
||||
DrawCommand::MoveTo { .. } => {}
|
||||
DrawCommand::LineTo { x, y } => {
|
||||
mc.custom_shape_bounds.encompass(x, y);
|
||||
mc.custom_edge_bounds.encompass(x, y);
|
||||
include_last = true;
|
||||
}
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
||||
mc.custom_shape_bounds.encompass(x1, y1);
|
||||
mc.custom_shape_bounds.encompass(x2, y2);
|
||||
mc.custom_edge_bounds.encompass(x1, y1);
|
||||
mc.custom_edge_bounds.encompass(x2, y2);
|
||||
include_last = true;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((_, commands)) = &mut mc.current_line {
|
||||
commands.push(command.clone());
|
||||
}
|
||||
if let Some((_, commands)) = &mut mc.current_fill {
|
||||
commands.push(command);
|
||||
}
|
||||
|
||||
if include_last {
|
||||
if let Some(command) = mc
|
||||
.current_fill
|
||||
.as_ref()
|
||||
.and_then(|(_, commands)| commands.last().cloned())
|
||||
{
|
||||
match command {
|
||||
DrawCommand::MoveTo { x, y } => {
|
||||
mc.custom_shape_bounds.encompass(x, y);
|
||||
mc.custom_edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::LineTo { x, y } => {
|
||||
mc.custom_shape_bounds.encompass(x, y);
|
||||
mc.custom_edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
||||
mc.custom_shape_bounds.encompass(x1, y1);
|
||||
mc.custom_shape_bounds.encompass(x2, y2);
|
||||
mc.custom_edge_bounds.encompass(x1, y1);
|
||||
mc.custom_edge_bounds.encompass(x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(command) = mc
|
||||
.current_line
|
||||
.as_ref()
|
||||
.and_then(|(_, commands)| commands.last().cloned())
|
||||
{
|
||||
match command {
|
||||
DrawCommand::MoveTo { x, y } => {
|
||||
mc.custom_shape_bounds.encompass(x, y);
|
||||
mc.custom_edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::LineTo { x, y } => {
|
||||
mc.custom_shape_bounds.encompass(x, y);
|
||||
mc.custom_edge_bounds.encompass(x, y);
|
||||
}
|
||||
DrawCommand::CurveTo { x1, y1, x2, y2 } => {
|
||||
mc.custom_shape_bounds.encompass(x1, y1);
|
||||
mc.custom_shape_bounds.encompass(x2, y2);
|
||||
mc.custom_edge_bounds.encompass(x1, y1);
|
||||
mc.custom_edge_bounds.encompass(x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mc.dirty_shape = true;
|
||||
}
|
||||
}
|
||||
|
||||
impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
||||
|
@ -587,17 +741,71 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> {
|
|||
if is_load_frame {
|
||||
mc.run_clip_postaction((*self).into(), context, ClipEvent::Load);
|
||||
}
|
||||
|
||||
if mc.dirty_shape {
|
||||
mc.dirty_shape = false;
|
||||
let mut paths = Vec::new();
|
||||
|
||||
for (style, commands) in &mc.custom_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)) = &mc.current_fill {
|
||||
paths.push(DrawPath::Fill {
|
||||
style,
|
||||
commands: commands.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
for (style, commands) in &mc.custom_lines {
|
||||
paths.push(DrawPath::Stroke {
|
||||
style,
|
||||
commands: commands.to_owned(),
|
||||
is_closed: false, // TODO: Determine this
|
||||
})
|
||||
}
|
||||
|
||||
if let Some((style, commands)) = &mc.current_line {
|
||||
paths.push(DrawPath::Stroke {
|
||||
style,
|
||||
commands: commands.to_owned(),
|
||||
is_closed: false, // TODO: Determine this
|
||||
})
|
||||
}
|
||||
|
||||
let shape = DistilledShape {
|
||||
paths,
|
||||
shape_bounds: mc.custom_shape_bounds.clone(),
|
||||
edge_bounds: mc.custom_shape_bounds.clone(),
|
||||
id: mc.id(),
|
||||
};
|
||||
|
||||
if let Some(handle) = mc.custom_shape {
|
||||
context.renderer.replace_shape(shape, handle);
|
||||
} else {
|
||||
mc.custom_shape = Some(context.renderer.register_shape(shape));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn render(&self, context: &mut RenderContext<'_, 'gc>) {
|
||||
context.transform_stack.push(&*self.transform());
|
||||
crate::display_object::render_children(context, &self.0.read().children);
|
||||
if let Some(handle) = self.0.read().custom_shape {
|
||||
context
|
||||
.renderer
|
||||
.render_shape(handle, context.transform_stack.transform());
|
||||
}
|
||||
context.transform_stack.pop();
|
||||
}
|
||||
|
||||
fn self_bounds(&self) -> BoundingBox {
|
||||
// No inherent bounds; contains child DisplayObjects.
|
||||
BoundingBox::default()
|
||||
self.0.read().custom_shape_bounds.clone()
|
||||
}
|
||||
|
||||
fn hit_test(&self, point: (Twips, Twips)) -> bool {
|
||||
|
|
Loading…
Reference in New Issue