avm2: Implement Graphics::drawRoundRectComplex

This commit is contained in:
Adrian Wielgosik 2024-08-13 21:57:13 +02:00 committed by Adrian Wielgosik
parent 84e4270f27
commit db61726e5b
6 changed files with 178 additions and 80 deletions

View File

@ -495,50 +495,61 @@ const UNIT_CIRCLE_POINTS: [(f64, f64); 5] = [
]; */ ]; */
/// Draw a roundrect. /// Draw a roundrect.
#[allow(clippy::too_many_arguments)]
fn draw_round_rect_internal( fn draw_round_rect_internal(
draw: &mut Drawing, draw: &mut Drawing,
x: f64, x: f64,
y: f64, y: f64,
width: f64, width: f64,
height: f64, height: f64,
mut ellipse_width: f64, top_left_width: f64,
mut ellipse_height: f64, top_left_height: f64,
top_right_width: f64,
top_right_height: f64,
bottom_left_width: f64,
bottom_left_height: f64,
bottom_right_width: f64,
bottom_right_height: f64,
) { ) {
if ellipse_height.is_nan() { let top_left_width = top_left_width.min(width / 2.0);
ellipse_height = ellipse_width; let top_left_height = top_left_height.min(height / 2.0);
} let top_right_width = top_right_width.min(width / 2.0);
let top_right_height = top_right_height.min(height / 2.0);
let bottom_left_width = bottom_left_width.min(width / 2.0);
let bottom_left_height = bottom_left_height.min(height / 2.0);
let bottom_right_width = bottom_right_width.min(width / 2.0);
let bottom_right_height = bottom_right_height.min(height / 2.0);
//Clamp the ellipse sizes to the size of the rectangle. let ucp = UNIT_CIRCLE_POINTS;
if ellipse_width > width {
ellipse_width = width;
}
if ellipse_height > height { let br_ellipse_center_x = x + width - bottom_right_width;
ellipse_height = height; let br_ellipse_center_y = y + height - bottom_right_height;
}
let bl_ellipse_center_x = x + bottom_left_width;
let bl_ellipse_center_y = y + height - bottom_left_height;
let tl_ellipse_center_x = x + top_left_width;
let tl_ellipse_center_y = y + top_left_height;
let tr_ellipse_center_x = x + width - top_right_width;
let tr_ellipse_center_y = y + top_right_height;
// We'll start from the bottom-right corner of the rectangle, // We'll start from the bottom-right corner of the rectangle,
// because that's what Flash Player does. // because that's what Flash Player does.
let ucp = UNIT_CIRCLE_POINTS;
let line_width = width - ellipse_width; // Middle of bottom-right ellipse
let line_height = height - ellipse_height; let br_point_x = br_ellipse_center_x + bottom_right_width * ucp[2].0;
let br_point_y = br_ellipse_center_y + bottom_right_height * ucp[2].1;
let br_ellipse_center_x = x + ellipse_width / 2.0 + line_width;
let br_ellipse_center_y = y + ellipse_height / 2.0 + line_height;
let br_point_x = br_ellipse_center_x + ellipse_width / 2.0 * ucp[2].0;
let br_point_y = br_ellipse_center_y + ellipse_height / 2.0 * ucp[2].1;
let br_point = Point::from_pixels(br_point_x, br_point_y); let br_point = Point::from_pixels(br_point_x, br_point_y);
draw.draw_command(DrawCommand::MoveTo(br_point)); draw.draw_command(DrawCommand::MoveTo(br_point));
let br_b_curve_x = br_ellipse_center_x + ellipse_width / 2.0 * ucp[3].0; let br_b_curve_x = br_ellipse_center_x + bottom_right_width * ucp[3].0;
let br_b_curve_y = br_ellipse_center_y + ellipse_height / 2.0 * ucp[3].1; let br_b_curve_y = br_ellipse_center_y + bottom_right_height * ucp[3].1;
let br_b_curve = Point::from_pixels(br_b_curve_x, br_b_curve_y); let br_b_curve = Point::from_pixels(br_b_curve_x, br_b_curve_y);
let right_b_point_x = br_ellipse_center_x + ellipse_width / 2.0 * ucp[4].0; let right_b_point_x = br_ellipse_center_x + bottom_right_width * ucp[4].0;
let right_b_point_y = br_ellipse_center_y + ellipse_height / 2.0 * ucp[4].1; let right_b_point_y = br_ellipse_center_y + bottom_right_height * ucp[4].1;
let right_b_point = Point::from_pixels(right_b_point_x, right_b_point_y); let right_b_point = Point::from_pixels(right_b_point_x, right_b_point_y);
draw.draw_command(DrawCommand::QuadraticCurveTo { draw.draw_command(DrawCommand::QuadraticCurveTo {
@ -546,24 +557,20 @@ fn draw_round_rect_internal(
anchor: right_b_point, anchor: right_b_point,
}); });
// Oh, since we're drawing roundrects, we also need to draw lines // Bottom line
// in between each ellipse. This is the bottom line. let left_b_point_x = bl_ellipse_center_x + bottom_left_width * ucp[4].0;
let tl_ellipse_center_x = x + ellipse_width / 2.0; let left_b_point_y = bl_ellipse_center_y + bottom_left_height * ucp[4].1;
let tl_ellipse_center_y = y + ellipse_height / 2.0;
let left_b_point_x = tl_ellipse_center_x + ellipse_width / -2.0 * ucp[4].0;
let left_b_point_y = br_ellipse_center_y + ellipse_height / 2.0 * ucp[4].1;
let left_b_point = Point::from_pixels(left_b_point_x, left_b_point_y); let left_b_point = Point::from_pixels(left_b_point_x, left_b_point_y);
draw.draw_command(DrawCommand::LineTo(left_b_point)); draw.draw_command(DrawCommand::LineTo(left_b_point));
// Bottom-left ellipse // Bottom-left ellipse
let b_bl_curve_x = tl_ellipse_center_x + ellipse_width / -2.0 * ucp[3].0; let b_bl_curve_x = bl_ellipse_center_x - bottom_left_width * ucp[3].0;
let b_bl_curve_y = br_ellipse_center_y + ellipse_height / 2.0 * ucp[3].1; let b_bl_curve_y = bl_ellipse_center_y + bottom_left_height * ucp[3].1;
let b_bl_curve = Point::from_pixels(b_bl_curve_x, b_bl_curve_y); let b_bl_curve = Point::from_pixels(b_bl_curve_x, b_bl_curve_y);
let bl_point_x = tl_ellipse_center_x + ellipse_width / -2.0 * ucp[2].0; let bl_point_x = bl_ellipse_center_x - bottom_left_width * ucp[2].0;
let bl_point_y = br_ellipse_center_y + ellipse_height / 2.0 * ucp[2].1; let bl_point_y = bl_ellipse_center_y + bottom_left_height * ucp[2].1;
let bl_point = Point::from_pixels(bl_point_x, bl_point_y); let bl_point = Point::from_pixels(bl_point_x, bl_point_y);
draw.draw_command(DrawCommand::QuadraticCurveTo { draw.draw_command(DrawCommand::QuadraticCurveTo {
@ -571,12 +578,12 @@ fn draw_round_rect_internal(
anchor: bl_point, anchor: bl_point,
}); });
let bl_l_curve_x = tl_ellipse_center_x + ellipse_width / -2.0 * ucp[1].0; let bl_l_curve_x = bl_ellipse_center_x - bottom_left_width * ucp[1].0;
let bl_l_curve_y = br_ellipse_center_y + ellipse_height / 2.0 * ucp[1].1; let bl_l_curve_y = bl_ellipse_center_y + bottom_left_height * ucp[1].1;
let bl_l_curve = Point::from_pixels(bl_l_curve_x, bl_l_curve_y); let bl_l_curve = Point::from_pixels(bl_l_curve_x, bl_l_curve_y);
let bottom_l_point_x = tl_ellipse_center_x + ellipse_width / -2.0 * ucp[0].0; let bottom_l_point_x = bl_ellipse_center_x - bottom_left_width * ucp[0].0;
let bottom_l_point_y = br_ellipse_center_y + ellipse_height / 2.0 * ucp[0].1; let bottom_l_point_y = bl_ellipse_center_y + bottom_left_height * ucp[0].1;
let bottom_l_point = Point::from_pixels(bottom_l_point_x, bottom_l_point_y); let bottom_l_point = Point::from_pixels(bottom_l_point_x, bottom_l_point_y);
draw.draw_command(DrawCommand::QuadraticCurveTo { draw.draw_command(DrawCommand::QuadraticCurveTo {
@ -585,19 +592,19 @@ fn draw_round_rect_internal(
}); });
// Left side // Left side
let top_l_point_x = tl_ellipse_center_x + ellipse_width / -2.0 * ucp[0].0; let top_l_point_x = tl_ellipse_center_x - top_left_width * ucp[0].0;
let top_l_point_y = tl_ellipse_center_y + ellipse_height / -2.0 * ucp[0].1; let top_l_point_y = tl_ellipse_center_y - top_left_height * ucp[0].1;
let top_l_point = Point::from_pixels(top_l_point_x, top_l_point_y); let top_l_point = Point::from_pixels(top_l_point_x, top_l_point_y);
draw.draw_command(DrawCommand::LineTo(top_l_point)); draw.draw_command(DrawCommand::LineTo(top_l_point));
// Top-left ellipse // Top-left ellipse
let l_tl_curve_x = tl_ellipse_center_x + ellipse_width / -2.0 * ucp[1].0; let l_tl_curve_x = tl_ellipse_center_x - top_left_width * ucp[1].0;
let l_tl_curve_y = tl_ellipse_center_y + ellipse_height / -2.0 * ucp[1].1; let l_tl_curve_y = tl_ellipse_center_y - top_left_height * ucp[1].1;
let l_tl_curve = Point::from_pixels(l_tl_curve_x, l_tl_curve_y); let l_tl_curve = Point::from_pixels(l_tl_curve_x, l_tl_curve_y);
let tl_point_x = tl_ellipse_center_x + ellipse_width / -2.0 * ucp[2].0; let tl_point_x = tl_ellipse_center_x - top_left_width * ucp[2].0;
let tl_point_y = tl_ellipse_center_y + ellipse_height / -2.0 * ucp[2].1; let tl_point_y = tl_ellipse_center_y - top_left_height * ucp[2].1;
let tl_point = Point::from_pixels(tl_point_x, tl_point_y); let tl_point = Point::from_pixels(tl_point_x, tl_point_y);
draw.draw_command(DrawCommand::QuadraticCurveTo { draw.draw_command(DrawCommand::QuadraticCurveTo {
@ -605,12 +612,12 @@ fn draw_round_rect_internal(
anchor: tl_point, anchor: tl_point,
}); });
let tl_t_curve_x = tl_ellipse_center_x + ellipse_width / -2.0 * ucp[3].0; let tl_t_curve_x = tl_ellipse_center_x - top_left_width * ucp[3].0;
let tl_t_curve_y = tl_ellipse_center_y + ellipse_height / -2.0 * ucp[3].1; let tl_t_curve_y = tl_ellipse_center_y - top_left_height * ucp[3].1;
let tl_t_curve = Point::from_pixels(tl_t_curve_x, tl_t_curve_y); let tl_t_curve = Point::from_pixels(tl_t_curve_x, tl_t_curve_y);
let left_t_point_x = tl_ellipse_center_x + ellipse_width / -2.0 * ucp[4].0; let left_t_point_x = tl_ellipse_center_x - top_left_width * ucp[4].0;
let left_t_point_y = tl_ellipse_center_y + ellipse_height / -2.0 * ucp[4].1; let left_t_point_y = tl_ellipse_center_y - top_left_height * ucp[4].1;
let left_t_point = Point::from_pixels(left_t_point_x, left_t_point_y); let left_t_point = Point::from_pixels(left_t_point_x, left_t_point_y);
draw.draw_command(DrawCommand::QuadraticCurveTo { draw.draw_command(DrawCommand::QuadraticCurveTo {
@ -619,19 +626,19 @@ fn draw_round_rect_internal(
}); });
// Top side // Top side
let right_t_point_x = br_ellipse_center_x + ellipse_width / 2.0 * ucp[4].0; let right_t_point_x = tr_ellipse_center_x + top_right_width * ucp[4].0;
let right_t_point_y = tl_ellipse_center_y + ellipse_height / -2.0 * ucp[4].1; let right_t_point_y = tr_ellipse_center_y - top_right_height * ucp[4].1;
let right_t_point = Point::from_pixels(right_t_point_x, right_t_point_y); let right_t_point = Point::from_pixels(right_t_point_x, right_t_point_y);
draw.draw_command(DrawCommand::LineTo(right_t_point)); draw.draw_command(DrawCommand::LineTo(right_t_point));
// Top-right ellipse // Top-right ellipse
let t_tr_curve_x = br_ellipse_center_x + ellipse_width / 2.0 * ucp[3].0; let t_tr_curve_x = tr_ellipse_center_x + top_right_width * ucp[3].0;
let t_tr_curve_y = tl_ellipse_center_y + ellipse_height / -2.0 * ucp[3].1; let t_tr_curve_y = tr_ellipse_center_y - top_right_height * ucp[3].1;
let t_tr_curve = Point::from_pixels(t_tr_curve_x, t_tr_curve_y); let t_tr_curve = Point::from_pixels(t_tr_curve_x, t_tr_curve_y);
let tr_point_x = br_ellipse_center_x + ellipse_width / 2.0 * ucp[2].0; let tr_point_x = tr_ellipse_center_x + top_right_width * ucp[2].0;
let tr_point_y = tl_ellipse_center_y + ellipse_height / -2.0 * ucp[2].1; let tr_point_y = tr_ellipse_center_y - top_right_height * ucp[2].1;
let tr_point = Point::from_pixels(tr_point_x, tr_point_y); let tr_point = Point::from_pixels(tr_point_x, tr_point_y);
draw.draw_command(DrawCommand::QuadraticCurveTo { draw.draw_command(DrawCommand::QuadraticCurveTo {
@ -639,12 +646,12 @@ fn draw_round_rect_internal(
anchor: tr_point, anchor: tr_point,
}); });
let tr_r_curve_x = br_ellipse_center_x + ellipse_width / 2.0 * ucp[1].0; let tr_r_curve_x = tr_ellipse_center_x + top_right_width * ucp[1].0;
let tr_r_curve_y = tl_ellipse_center_y + ellipse_height / -2.0 * ucp[1].1; let tr_r_curve_y = tr_ellipse_center_y - top_right_height * ucp[1].1;
let tr_r_curve = Point::from_pixels(tr_r_curve_x, tr_r_curve_y); let tr_r_curve = Point::from_pixels(tr_r_curve_x, tr_r_curve_y);
let top_r_point_x = br_ellipse_center_x + ellipse_width / 2.0 * ucp[0].0; let top_r_point_x = tr_ellipse_center_x + top_right_width * ucp[0].0;
let top_r_point_y = tl_ellipse_center_y + ellipse_height / -2.0 * ucp[0].1; let top_r_point_y = tr_ellipse_center_y - top_right_height * ucp[0].1;
let top_r_point = Point::from_pixels(top_r_point_x, top_r_point_y); let top_r_point = Point::from_pixels(top_r_point_x, top_r_point_y);
draw.draw_command(DrawCommand::QuadraticCurveTo { draw.draw_command(DrawCommand::QuadraticCurveTo {
@ -653,14 +660,14 @@ fn draw_round_rect_internal(
}); });
// Right side & other half of bottom-right ellipse // Right side & other half of bottom-right ellipse
let bottom_r_point_x = br_ellipse_center_x + ellipse_width / 2.0 * ucp[0].0; let bottom_r_point_x = br_ellipse_center_x + bottom_right_width * ucp[0].0;
let bottom_r_point_y = br_ellipse_center_y + ellipse_height / 2.0 * ucp[0].1; let bottom_r_point_y = br_ellipse_center_y + bottom_right_height * ucp[0].1;
let bottom_r_point = Point::from_pixels(bottom_r_point_x, bottom_r_point_y); let bottom_r_point = Point::from_pixels(bottom_r_point_x, bottom_r_point_y);
draw.draw_command(DrawCommand::LineTo(bottom_r_point)); draw.draw_command(DrawCommand::LineTo(bottom_r_point));
let r_br_curve_x = br_ellipse_center_x + ellipse_width / 2.0 * ucp[1].0; let r_br_curve_x = br_ellipse_center_x + bottom_right_width * ucp[1].0;
let r_br_curve_y = br_ellipse_center_y + ellipse_height / 2.0 * ucp[1].1; let r_br_curve_y = br_ellipse_center_y + bottom_right_height * ucp[1].1;
let r_br_curve = Point::from_pixels(r_br_curve_x, r_br_curve_y); let r_br_curve = Point::from_pixels(r_br_curve_x, r_br_curve_y);
draw.draw_command(DrawCommand::QuadraticCurveTo { draw.draw_command(DrawCommand::QuadraticCurveTo {
@ -681,7 +688,11 @@ pub fn draw_round_rect<'gc>(
let width = args.get_f64(activation, 2)?; let width = args.get_f64(activation, 2)?;
let height = args.get_f64(activation, 3)?; let height = args.get_f64(activation, 3)?;
let ellipse_width = args.get_f64(activation, 4)?; let ellipse_width = args.get_f64(activation, 4)?;
let ellipse_height = args.get_f64(activation, 5)?; let mut ellipse_height = args.get_f64(activation, 5)?;
if ellipse_height.is_nan() {
ellipse_height = ellipse_width;
}
if let Some(mut draw) = this.as_drawing(activation.context.gc_context) { if let Some(mut draw) = this.as_drawing(activation.context.gc_context) {
draw_round_rect_internal( draw_round_rect_internal(
@ -690,8 +701,52 @@ pub fn draw_round_rect<'gc>(
y, y,
width, width,
height, height,
ellipse_width, ellipse_width / 2.0,
ellipse_height, ellipse_height / 2.0,
ellipse_width / 2.0,
ellipse_height / 2.0,
ellipse_width / 2.0,
ellipse_height / 2.0,
ellipse_width / 2.0,
ellipse_height / 2.0,
);
}
}
Ok(Value::Undefined)
}
/// Implements `Graphics.drawRoundRectComplex`
pub fn draw_round_rect_complex<'gc>(
activation: &mut Activation<'_, 'gc>,
this: Object<'gc>,
args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
if let Some(this) = this.as_display_object() {
let x = args.get_f64(activation, 0)?;
let y = args.get_f64(activation, 1)?;
let width = args.get_f64(activation, 2)?;
let height = args.get_f64(activation, 3)?;
let top_left = args.get_f64(activation, 4)?;
let top_right = args.get_f64(activation, 5)?;
let bottom_left = args.get_f64(activation, 6)?;
let bottom_right = args.get_f64(activation, 7)?;
if let Some(mut draw) = this.as_drawing(activation.context.gc_context) {
draw_round_rect_internal(
&mut draw,
x,
y,
width,
height,
top_left,
top_left,
top_right,
top_right,
bottom_left,
bottom_left,
bottom_right,
bottom_right,
); );
} }
} }
@ -717,8 +772,14 @@ pub fn draw_circle<'gc>(
y - radius, y - radius,
radius * 2.0, radius * 2.0,
radius * 2.0, radius * 2.0,
radius * 2.0, radius,
radius * 2.0, radius,
radius,
radius,
radius,
radius,
radius,
radius,
); );
} }
} }
@ -739,7 +800,21 @@ pub fn draw_ellipse<'gc>(
let height = args.get_f64(activation, 3)?; let height = args.get_f64(activation, 3)?;
if let Some(mut draw) = this.as_drawing(activation.context.gc_context) { if let Some(mut draw) = this.as_drawing(activation.context.gc_context) {
draw_round_rect_internal(&mut draw, x, y, width, height, width, height) draw_round_rect_internal(
&mut draw,
x,
y,
width,
height,
width / 2.0,
height / 2.0,
width / 2.0,
height / 2.0,
width / 2.0,
height / 2.0,
width / 2.0,
height / 2.0,
)
} }
} }
@ -891,16 +966,6 @@ pub fn draw_path<'gc>(
Ok(Value::Undefined) Ok(Value::Undefined)
} }
/// Implements `Graphics.drawRoundRectComplex`
pub fn draw_round_rect_complex<'gc>(
activation: &mut Activation<'_, 'gc>,
_this: Object<'gc>,
_args: &[Value<'gc>],
) -> Result<Value<'gc>, Error<'gc>> {
avm2_stub_method!(activation, "flash.display.Graphics", "drawRoundRectComplex");
Ok(Value::Undefined)
}
/// Implements `Graphics.drawTriangles` /// Implements `Graphics.drawTriangles`
pub fn draw_triangles<'gc>( pub fn draw_triangles<'gc>(
activation: &mut Activation<'_, 'gc>, activation: &mut Activation<'_, 'gc>,

View File

@ -0,0 +1,29 @@
// compiled with mxmlc
package {
import flash.display.MovieClip;
public class Test extends MovieClip {
public function Test(){
graphics.beginFill(0xFFCC00);
// top left: normal
graphics.drawRoundRect(10, 10, 100, 100, 70, 30);
// top right: clamping
graphics.drawRoundRect(150, 10, 100, 100, 100, 100);
// middle left: complex, normal
graphics.drawRoundRectComplex(10, 150, 100, 100,
20, 30, 40, 50);
// middle right: complex, clamping
graphics.drawRoundRectComplex(150, 150, 100, 100,
80, 60, 50, 30);
// bottom left: circle
graphics.drawCircle(80, 300, 50);
// bottom right: ellipse
graphics.drawEllipse(150, 300, 100, 50);
graphics.endFill();
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

Binary file not shown.

View File

@ -0,0 +1,4 @@
num_ticks = 1
[player_options]
with_renderer = { optional = false, sample_count = 1 }