avm2: Implement `Graphics.lineStyle`

This commit is contained in:
David Wendt 2021-02-10 23:02:22 -05:00 committed by Mike Welsh
parent 41ad756792
commit 6396b90890
1 changed files with 138 additions and 10 deletions

View File

@ -11,7 +11,7 @@ use crate::avm2::Error;
use crate::display_object::TDisplayObject;
use crate::shape_utils::DrawCommand;
use gc_arena::{GcCell, MutationContext};
use swf::{Color, FillStyle, Twips};
use swf::{Color, FillStyle, LineCapStyle, LineJoinStyle, LineStyle, Twips};
/// Implements `flash.display.Graphics`'s instance constructor.
pub fn instance_init<'gc>(
@ -31,6 +31,16 @@ pub fn class_init<'gc>(
Ok(Value::Undefined)
}
/// Convert a `color`/`alpha` argument pair into a `swf::Color`.
fn color_from_args(color: u32, alpha: f64) -> Color {
let r = (color & 0xFF0000 >> 16) as u8;
let g = (color & 0x00FF00 >> 8) as u8;
let b = (color & 0x0000FF) as u8;
let a = (alpha * 255.0) as u8;
Color { r, g, b, a }
}
/// Implements `Graphics.beginFill`.
pub fn begin_fill<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
@ -45,19 +55,16 @@ pub fn begin_fill<'gc>(
.cloned()
.unwrap_or(Value::Undefined)
.coerce_to_u32(activation)?;
let r = (color & 0xFF0000 >> 16) as u8;
let g = (color & 0x00FF00 >> 8) as u8;
let b = (color & 0x0000FF) as u8;
let a = (args
let alpha = args
.get(1)
.cloned()
.unwrap_or_else(|| 1.0.into())
.coerce_to_number(activation)?
* 255.0) as u8;
.coerce_to_number(activation)?;
let color = Color { r, g, b, a };
mc.set_fill_style(&mut activation.context, Some(FillStyle::Color(color)));
mc.set_fill_style(
&mut activation.context,
Some(FillStyle::Color(color_from_args(color, alpha))),
);
}
}
}
@ -144,6 +151,123 @@ pub fn end_fill<'gc>(
Ok(Value::Undefined)
}
fn caps_to_cap_style<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
caps: Value<'gc>,
) -> Result<LineCapStyle, Error> {
let caps_string = caps.coerce_to_string(activation);
let caps_str = caps_string.as_deref();
match (caps, caps_str) {
(Value::Null, _) | (_, Ok("none")) => Ok(LineCapStyle::None),
(_, Ok("round")) => Ok(LineCapStyle::Round),
(_, Ok("square")) => Ok(LineCapStyle::Square),
(_, Ok(_)) => Err("ArgumentError: caps is invalid".into()),
(_, Err(_)) => Err(caps_string.unwrap_err()),
}
}
fn joints_to_join_style<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
joints: Value<'gc>,
miter_limit: f32,
) -> Result<LineJoinStyle, Error> {
let joints_string = joints.coerce_to_string(activation);
let joints_str = joints_string.as_deref();
match (joints, joints_str) {
(Value::Null, _) | (_, Ok("round")) => Ok(LineJoinStyle::Round),
(_, Ok("miter")) => Ok(LineJoinStyle::Miter(miter_limit)),
(_, Ok("bevel")) => Ok(LineJoinStyle::Bevel),
(_, Ok(_)) => Err("ArgumentError: joints is invalid".into()),
(_, Err(_)) => Err(joints_string.unwrap_err()),
}
}
fn scale_mode_to_allow_scale_bits(scale_mode: &str) -> Result<(bool, bool), Error> {
match scale_mode {
"normal" => Ok((true, true)),
"none" => Ok((false, false)),
"horizontal" => Ok((true, false)),
"vertical" => Ok((false, true)),
_ => Err("ArgumentError: scaleMode parameter is invalid".into()),
}
}
/// Implements `Graphics.lineStyle`.
pub fn line_style<'gc>(
activation: &mut Activation<'_, 'gc, '_>,
this: Option<Object<'gc>>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error> {
if let Some(this) = this {
if let Some(dobj) = this.as_display_object() {
if let Some(mc) = dobj.as_movie_clip() {
let thickness = args
.get(0)
.cloned()
.unwrap_or_else(|| f64::NAN.into())
.coerce_to_number(activation)?;
if thickness.is_nan() {
mc.set_line_style(&mut activation.context, None);
} else {
let color = args
.get(1)
.cloned()
.unwrap_or_else(|| 0.into())
.coerce_to_u32(activation)?;
let alpha = args
.get(2)
.cloned()
.unwrap_or_else(|| 1.0.into())
.coerce_to_number(activation)?;
let is_pixel_hinted = args
.get(3)
.cloned()
.unwrap_or_else(|| false.into())
.coerce_to_boolean();
let scale_mode = args
.get(4)
.cloned()
.unwrap_or_else(|| "normal".into())
.coerce_to_string(activation)?;
let caps =
caps_to_cap_style(activation, args.get(5).cloned().unwrap_or(Value::Null))?;
let joints = args.get(6).cloned().unwrap_or(Value::Null);
let miter_limit = args
.get(7)
.cloned()
.unwrap_or_else(|| 3.0.into())
.coerce_to_number(activation)?;
let width = Twips::from_pixels(thickness.min(255.0).max(0.0));
let color = color_from_args(color, alpha);
let join_style = joints_to_join_style(activation, joints, miter_limit as f32)?;
let (allow_scale_x, allow_scale_y) =
scale_mode_to_allow_scale_bits(&scale_mode)?;
let line_style = LineStyle {
width,
color,
start_cap: caps,
end_cap: caps,
join_style,
fill_style: None,
allow_scale_x,
allow_scale_y,
is_pixel_hinted,
allow_close: true,
};
mc.set_line_style(&mut activation.context, Some(line_style));
}
}
}
}
Ok(Value::Undefined)
}
/// Construct `Graphics`'s class.
pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> {
let class = Class::new(
@ -172,6 +296,10 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>
QName::new(Namespace::public(), "endFill"),
Method::from_builtin(end_fill),
));
write.define_instance_trait(Trait::from_method(
QName::new(Namespace::public(), "lineStyle"),
Method::from_builtin(line_style),
));
class
}