render: Add DrawCommand::CubicCurveTo

This commit is contained in:
Nathan Adams 2023-08-25 15:20:44 +02:00
parent 076977cc75
commit eb2afb19c4
6 changed files with 118 additions and 2 deletions

1
Cargo.lock generated
View File

@ -3972,6 +3972,7 @@ dependencies = [
"jpeg-decoder",
"lru",
"lyon",
"lyon_geom",
"num-derive 0.4.0",
"num-traits",
"png",

View File

@ -3,7 +3,7 @@ use ruffle_render::backend::{RenderBackend, ShapeHandle};
use ruffle_render::bitmap::{BitmapHandle, BitmapInfo, BitmapSize, BitmapSource};
use ruffle_render::commands::CommandHandler;
use ruffle_render::shape_utils::{
quadratic_curve_bounds, DistilledShape, DrawCommand, DrawPath, FillRule,
cubic_curve_bounds, quadratic_curve_bounds, DistilledShape, DrawCommand, DrawPath, FillRule,
};
use std::cell::{Cell, RefCell};
use swf::{FillStyle, LineStyle, Point, Rectangle, Twips};
@ -458,5 +458,16 @@ fn stretch_bounds(
DrawCommand::QuadraticCurveTo { control, anchor } => {
bounds.union(&quadratic_curve_bounds(from, stroke_width, control, anchor))
}
DrawCommand::CubicCurveTo {
control_a,
control_b,
anchor,
} => bounds.union(&cubic_curve_bounds(
from,
stroke_width,
control_a,
control_b,
anchor,
)),
}
}

View File

@ -17,6 +17,7 @@ flate2 = "1.0.27"
smallvec = { version = "1.11.0", features = ["union"] }
downcast-rs = "1.2.0"
lyon = { version = "1.0.1", optional = true }
lyon_geom = "1.0.1"
thiserror = "1.0"
wasm-bindgen = { version = "=0.2.87", optional = true }
enum-map = "2.6.1"

View File

@ -830,6 +830,18 @@ fn draw_commands_to_path2d(commands: &[DrawCommand], is_closed: bool) -> Path2d
anchor.x.get().into(),
anchor.y.get().into(),
),
DrawCommand::CubicCurveTo {
control_a,
control_b,
anchor,
} => path.bezier_curve_to(
control_a.x.get().into(),
control_a.y.get().into(),
control_b.x.get().into(),
control_b.y.get().into(),
anchor.x.get().into(),
anchor.y.get().into(),
),
};
}

View File

@ -123,6 +123,11 @@ pub enum DrawCommand {
control: swf::Point<Twips>,
anchor: swf::Point<Twips>,
},
CubicCurveTo {
control_a: swf::Point<Twips>,
control_b: swf::Point<Twips>,
anchor: swf::Point<Twips>,
},
}
impl DrawCommand {
@ -130,7 +135,8 @@ impl DrawCommand {
match self {
DrawCommand::MoveTo(point)
| DrawCommand::LineTo(point)
| DrawCommand::QuadraticCurveTo { anchor: point, .. } => *point,
| DrawCommand::QuadraticCurveTo { anchor: point, .. }
| DrawCommand::CubicCurveTo { anchor: point, .. } => *point,
}
}
}
@ -680,6 +686,27 @@ pub fn draw_command_fill_hit_test(commands: &[DrawCommand], test_point: swf::Poi
winding += winding_number_curve(test_point, cursor, *control, *anchor);
cursor = *anchor;
}
DrawCommand::CubicCurveTo {
control_a,
control_b,
anchor,
} => {
lyon_geom::CubicBezierSegment {
from: lyon_geom::Point::new(cursor.x.to_pixels(), cursor.y.to_pixels()),
ctrl1: lyon_geom::Point::new(control_a.x.to_pixels(), control_a.y.to_pixels()),
ctrl2: lyon_geom::Point::new(control_b.x.to_pixels(), control_b.y.to_pixels()),
to: lyon_geom::Point::new(anchor.x.to_pixels(), anchor.y.to_pixels()),
}
.for_each_quadratic_bezier(0.01, &mut |quadratic_curve| {
winding += winding_number_curve(
test_point,
swf::Point::from_pixels(quadratic_curve.from.x, quadratic_curve.from.y),
swf::Point::from_pixels(quadratic_curve.ctrl.x, quadratic_curve.ctrl.y),
swf::Point::from_pixels(quadratic_curve.to.x, quadratic_curve.to.y),
);
});
cursor = *anchor;
}
}
}
if cursor != fill_start {
@ -719,6 +746,35 @@ pub fn draw_command_stroke_hit_test(
}
cursor = *anchor;
}
DrawCommand::CubicCurveTo {
control_a,
control_b,
anchor,
} => {
let mut hit = false;
lyon_geom::CubicBezierSegment {
from: lyon_geom::Point::new(cursor.x.to_pixels(), cursor.y.to_pixels()),
ctrl1: lyon_geom::Point::new(control_a.x.to_pixels(), control_a.y.to_pixels()),
ctrl2: lyon_geom::Point::new(control_b.x.to_pixels(), control_b.y.to_pixels()),
to: lyon_geom::Point::new(anchor.x.to_pixels(), anchor.y.to_pixels()),
}
.for_each_quadratic_bezier(0.01, &mut |quadratic_curve| {
if hit_test_stroke_curve(
test_point,
swf::Point::from_pixels(quadratic_curve.from.x, quadratic_curve.from.y),
swf::Point::from_pixels(quadratic_curve.ctrl.x, quadratic_curve.ctrl.y),
swf::Point::from_pixels(quadratic_curve.to.x, quadratic_curve.to.y),
stroke_widths,
) {
hit = true;
}
});
cursor = *anchor;
if hit {
return true;
}
}
}
}
@ -1222,6 +1278,31 @@ pub fn quadratic_curve_bounds(
))
}
pub fn cubic_curve_bounds(
start: swf::Point<Twips>,
stroke_width: Twips,
control_a: swf::Point<Twips>,
control_b: swf::Point<Twips>,
anchor: swf::Point<Twips>,
) -> Rectangle<Twips> {
// [NA] Should we just move most of our math in this file to lyon_geom?
let bounds = lyon_geom::CubicBezierSegment {
from: lyon_geom::Point::new(start.x.to_pixels(), start.y.to_pixels()),
ctrl1: lyon_geom::Point::new(control_a.x.to_pixels(), control_a.y.to_pixels()),
ctrl2: lyon_geom::Point::new(control_b.x.to_pixels(), control_b.y.to_pixels()),
to: lyon_geom::Point::new(anchor.x.to_pixels(), anchor.y.to_pixels()),
}
.bounding_box();
let radius = stroke_width / 2;
Rectangle {
x_min: Twips::from_pixels(bounds.min.x) - radius,
x_max: Twips::from_pixels(bounds.max.x) + radius,
y_min: Twips::from_pixels(bounds.min.y) - radius,
y_max: Twips::from_pixels(bounds.max.y) + radius,
}
}
#[cfg(test)]
mod tests {
use super::*;

View File

@ -353,6 +353,16 @@ fn ruffle_path_to_lyon_path(commands: &[DrawCommand], is_closed: bool) -> Path {
}
builder.quadratic_bezier_to(point(*control), point(*anchor));
}
DrawCommand::CubicCurveTo {
control_a,
control_b,
anchor,
} => {
if let Some(cursor) = cursor.take() {
builder.begin(point(cursor));
}
builder.cubic_bezier_to(point(*control_a), point(*control_b), point(*anchor));
}
}
}