web: WebGL render backend (merge #520)
Initial implementation of a WebGL render backend. Switch the web target to use WebGL by default. Put the canvas backend behind a feature flag.
This commit is contained in:
commit
c586991a88
|
@ -1826,6 +1826,23 @@ dependencies = [
|
||||||
"ruffle_core 0.1.0",
|
"ruffle_core 0.1.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ruffle_render_webgl"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"jpeg-decoder 0.1.19 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"js-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"percent-encoding 2.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"png 0.16.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"ruffle_core 0.1.0",
|
||||||
|
"ruffle_render_common_tess 0.1.0",
|
||||||
|
"ruffle_web_common 0.1.0",
|
||||||
|
"wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"web-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ruffle_render_wgpu"
|
name = "ruffle_render_wgpu"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -1870,6 +1887,7 @@ dependencies = [
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ruffle_core 0.1.0",
|
"ruffle_core 0.1.0",
|
||||||
"ruffle_render_canvas 0.1.0",
|
"ruffle_render_canvas 0.1.0",
|
||||||
|
"ruffle_render_webgl 0.1.0",
|
||||||
"ruffle_web_common 0.1.0",
|
"ruffle_web_common 0.1.0",
|
||||||
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"url 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -1882,8 +1900,10 @@ dependencies = [
|
||||||
name = "ruffle_web_common"
|
name = "ruffle_web_common"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"js-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
"wasm-bindgen 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"web-sys 0.3.39 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -10,6 +10,7 @@ members = [
|
||||||
"render/canvas",
|
"render/canvas",
|
||||||
"render/wgpu",
|
"render/wgpu",
|
||||||
"render/common_tess",
|
"render/common_tess",
|
||||||
|
"render/webgl",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Don't optimize build scripts and macros.
|
# Don't optimize build scripts and macros.
|
||||||
|
|
|
@ -8,290 +8,297 @@ use lyon::tessellation::{FillOptions, StrokeOptions};
|
||||||
use ruffle_core::backend::render::swf::{self, FillStyle, Twips};
|
use ruffle_core::backend::render::swf::{self, FillStyle, Twips};
|
||||||
use ruffle_core::shape_utils::{DrawCommand, DrawPath};
|
use ruffle_core::shape_utils::{DrawCommand, DrawPath};
|
||||||
|
|
||||||
pub fn tessellate_shape<F>(shape: &swf::Shape, get_bitmap_dimenions: F) -> Mesh
|
pub struct ShapeTessellator {
|
||||||
where
|
fill_tess: FillTessellator,
|
||||||
F: Fn(swf::CharacterId) -> Option<(u32, u32)>,
|
stroke_tess: StrokeTessellator,
|
||||||
{
|
}
|
||||||
let paths = ruffle_core::shape_utils::swf_shape_to_paths(shape);
|
|
||||||
let mut mesh = Vec::new();
|
|
||||||
|
|
||||||
let mut fill_tess = FillTessellator::new();
|
impl ShapeTessellator {
|
||||||
let mut stroke_tess = StrokeTessellator::new();
|
pub fn new() -> Self {
|
||||||
let mut lyon_mesh: VertexBuffers<_, u32> = VertexBuffers::new();
|
Self {
|
||||||
|
fill_tess: FillTessellator::new(),
|
||||||
fn flush_draw(draw: DrawType, mesh: &mut Mesh, lyon_mesh: &mut VertexBuffers<Vertex, u32>) {
|
stroke_tess: StrokeTessellator::new(),
|
||||||
if lyon_mesh.vertices.is_empty() {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let draw_mesh = std::mem::replace(lyon_mesh, VertexBuffers::new());
|
|
||||||
mesh.push(Draw {
|
|
||||||
draw_type: draw,
|
|
||||||
vertices: draw_mesh.vertices,
|
|
||||||
indices: draw_mesh.indices,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for path in paths {
|
pub fn tessellate_shape<F>(&mut self, shape: &swf::Shape, get_bitmap_dimensions: F) -> Mesh
|
||||||
match path {
|
where
|
||||||
DrawPath::Fill { style, commands } => match style {
|
F: Fn(swf::CharacterId) -> Option<(u32, u32)>,
|
||||||
FillStyle::Color(color) => {
|
{
|
||||||
let color = [
|
let paths = ruffle_core::shape_utils::swf_shape_to_paths(shape);
|
||||||
f32::from(color.r) / 255.0,
|
let mut mesh = Vec::new();
|
||||||
f32::from(color.g) / 255.0,
|
|
||||||
f32::from(color.b) / 255.0,
|
let mut lyon_mesh: VertexBuffers<_, u32> = VertexBuffers::new();
|
||||||
f32::from(color.a) / 255.0,
|
|
||||||
];
|
fn flush_draw(draw: DrawType, mesh: &mut Mesh, lyon_mesh: &mut VertexBuffers<Vertex, u32>) {
|
||||||
|
if lyon_mesh.vertices.is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let draw_mesh = std::mem::replace(lyon_mesh, VertexBuffers::new());
|
||||||
|
mesh.push(Draw {
|
||||||
|
draw_type: draw,
|
||||||
|
vertices: draw_mesh.vertices,
|
||||||
|
indices: draw_mesh.indices,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for path in paths {
|
||||||
|
match path {
|
||||||
|
DrawPath::Fill { style, commands } => match style {
|
||||||
|
FillStyle::Color(color) => {
|
||||||
|
let color = ((color.a as u32) << 24)
|
||||||
|
| ((color.b as u32) << 16)
|
||||||
|
| ((color.g as u32) << 8)
|
||||||
|
| (color.r as u32);
|
||||||
|
|
||||||
|
let mut buffers_builder =
|
||||||
|
BuffersBuilder::new(&mut lyon_mesh, RuffleVertexCtor { color });
|
||||||
|
|
||||||
|
if let Err(e) = self.fill_tess.tessellate_path(
|
||||||
|
&ruffle_path_to_lyon_path(commands, true),
|
||||||
|
&FillOptions::even_odd(),
|
||||||
|
&mut buffers_builder,
|
||||||
|
) {
|
||||||
|
// This may just be a degenerate path; skip it.
|
||||||
|
log::error!("Tessellation failure: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FillStyle::LinearGradient(gradient) => {
|
||||||
|
flush_draw(DrawType::Color, &mut mesh, &mut lyon_mesh);
|
||||||
|
|
||||||
|
let mut buffers_builder = BuffersBuilder::new(
|
||||||
|
&mut lyon_mesh,
|
||||||
|
RuffleVertexCtor { color: 0xffff_ffff },
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(e) = self.fill_tess.tessellate_path(
|
||||||
|
&ruffle_path_to_lyon_path(commands, true),
|
||||||
|
&FillOptions::even_odd(),
|
||||||
|
&mut buffers_builder,
|
||||||
|
) {
|
||||||
|
// This may just be a degenerate path; skip it.
|
||||||
|
log::error!("Tessellation failure: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut colors: Vec<[f32; 4]> = Vec::with_capacity(8);
|
||||||
|
let mut ratios: Vec<f32> = Vec::with_capacity(8);
|
||||||
|
for record in &gradient.records {
|
||||||
|
colors.push([
|
||||||
|
f32::from(record.color.r) / 255.0,
|
||||||
|
f32::from(record.color.g) / 255.0,
|
||||||
|
f32::from(record.color.b) / 255.0,
|
||||||
|
f32::from(record.color.a) / 255.0,
|
||||||
|
]);
|
||||||
|
ratios.push(f32::from(record.ratio) / 255.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let gradient = Gradient {
|
||||||
|
gradient_type: GradientType::Linear,
|
||||||
|
ratios,
|
||||||
|
colors,
|
||||||
|
num_colors: gradient.records.len() as u32,
|
||||||
|
matrix: swf_to_gl_matrix(gradient.matrix.clone()),
|
||||||
|
repeat_mode: gradient.spread,
|
||||||
|
focal_point: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
flush_draw(DrawType::Gradient(gradient), &mut mesh, &mut lyon_mesh);
|
||||||
|
}
|
||||||
|
FillStyle::RadialGradient(gradient) => {
|
||||||
|
flush_draw(DrawType::Color, &mut mesh, &mut lyon_mesh);
|
||||||
|
|
||||||
|
let mut buffers_builder = BuffersBuilder::new(
|
||||||
|
&mut lyon_mesh,
|
||||||
|
RuffleVertexCtor { color: 0xffff_ffff },
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(e) = self.fill_tess.tessellate_path(
|
||||||
|
&ruffle_path_to_lyon_path(commands, true),
|
||||||
|
&FillOptions::even_odd(),
|
||||||
|
&mut buffers_builder,
|
||||||
|
) {
|
||||||
|
// This may just be a degenerate path; skip it.
|
||||||
|
log::error!("Tessellation failure: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut colors: Vec<[f32; 4]> = Vec::with_capacity(8);
|
||||||
|
let mut ratios: Vec<f32> = Vec::with_capacity(8);
|
||||||
|
for record in &gradient.records {
|
||||||
|
colors.push([
|
||||||
|
f32::from(record.color.r) / 255.0,
|
||||||
|
f32::from(record.color.g) / 255.0,
|
||||||
|
f32::from(record.color.b) / 255.0,
|
||||||
|
f32::from(record.color.a) / 255.0,
|
||||||
|
]);
|
||||||
|
ratios.push(f32::from(record.ratio) / 255.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let gradient = Gradient {
|
||||||
|
gradient_type: GradientType::Radial,
|
||||||
|
ratios,
|
||||||
|
colors,
|
||||||
|
num_colors: gradient.records.len() as u32,
|
||||||
|
matrix: swf_to_gl_matrix(gradient.matrix.clone()),
|
||||||
|
repeat_mode: gradient.spread,
|
||||||
|
focal_point: 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
flush_draw(DrawType::Gradient(gradient), &mut mesh, &mut lyon_mesh);
|
||||||
|
}
|
||||||
|
FillStyle::FocalGradient {
|
||||||
|
gradient,
|
||||||
|
focal_point,
|
||||||
|
} => {
|
||||||
|
flush_draw(DrawType::Color, &mut mesh, &mut lyon_mesh);
|
||||||
|
|
||||||
|
let mut buffers_builder = BuffersBuilder::new(
|
||||||
|
&mut lyon_mesh,
|
||||||
|
RuffleVertexCtor { color: 0xffff_ffff },
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(e) = self.fill_tess.tessellate_path(
|
||||||
|
&ruffle_path_to_lyon_path(commands, true),
|
||||||
|
&FillOptions::even_odd(),
|
||||||
|
&mut buffers_builder,
|
||||||
|
) {
|
||||||
|
// This may just be a degenerate path; skip it.
|
||||||
|
log::error!("Tessellation failure: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut colors: Vec<[f32; 4]> = Vec::with_capacity(8);
|
||||||
|
let mut ratios: Vec<f32> = Vec::with_capacity(8);
|
||||||
|
for record in &gradient.records {
|
||||||
|
colors.push([
|
||||||
|
f32::from(record.color.r) / 255.0,
|
||||||
|
f32::from(record.color.g) / 255.0,
|
||||||
|
f32::from(record.color.b) / 255.0,
|
||||||
|
f32::from(record.color.a) / 255.0,
|
||||||
|
]);
|
||||||
|
ratios.push(f32::from(record.ratio) / 255.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let gradient = Gradient {
|
||||||
|
gradient_type: GradientType::Focal,
|
||||||
|
ratios,
|
||||||
|
colors,
|
||||||
|
num_colors: gradient.records.len() as u32,
|
||||||
|
matrix: swf_to_gl_matrix(gradient.matrix.clone()),
|
||||||
|
repeat_mode: gradient.spread,
|
||||||
|
focal_point: *focal_point,
|
||||||
|
};
|
||||||
|
|
||||||
|
flush_draw(DrawType::Gradient(gradient), &mut mesh, &mut lyon_mesh);
|
||||||
|
}
|
||||||
|
FillStyle::Bitmap {
|
||||||
|
id,
|
||||||
|
matrix,
|
||||||
|
is_smoothed,
|
||||||
|
is_repeating,
|
||||||
|
} => {
|
||||||
|
flush_draw(DrawType::Color, &mut mesh, &mut lyon_mesh);
|
||||||
|
|
||||||
|
let mut buffers_builder = BuffersBuilder::new(
|
||||||
|
&mut lyon_mesh,
|
||||||
|
RuffleVertexCtor { color: 0xffff_ffff },
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Err(e) = self.fill_tess.tessellate_path(
|
||||||
|
&ruffle_path_to_lyon_path(commands, true),
|
||||||
|
&FillOptions::even_odd(),
|
||||||
|
&mut buffers_builder,
|
||||||
|
) {
|
||||||
|
// This may just be a degenerate path; skip it.
|
||||||
|
log::error!("Tessellation failure: {:?}", e);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (bitmap_width, bitmap_height) =
|
||||||
|
(get_bitmap_dimensions)(*id).unwrap_or((1, 1));
|
||||||
|
|
||||||
|
let bitmap = Bitmap {
|
||||||
|
matrix: swf_bitmap_to_gl_matrix(
|
||||||
|
matrix.clone(),
|
||||||
|
bitmap_width,
|
||||||
|
bitmap_height,
|
||||||
|
),
|
||||||
|
id: *id,
|
||||||
|
is_smoothed: *is_smoothed,
|
||||||
|
is_repeating: *is_repeating,
|
||||||
|
};
|
||||||
|
|
||||||
|
flush_draw(DrawType::Bitmap(bitmap), &mut mesh, &mut lyon_mesh);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
DrawPath::Stroke {
|
||||||
|
style,
|
||||||
|
commands,
|
||||||
|
is_closed,
|
||||||
|
} => {
|
||||||
|
let color = ((style.color.a as u32) << 24)
|
||||||
|
| ((style.color.b as u32) << 16)
|
||||||
|
| ((style.color.g as u32) << 8)
|
||||||
|
| (style.color.r as u32);
|
||||||
|
|
||||||
let mut buffers_builder =
|
let mut buffers_builder =
|
||||||
BuffersBuilder::new(&mut lyon_mesh, RuffleVertexCtor { color });
|
BuffersBuilder::new(&mut lyon_mesh, RuffleVertexCtor { color });
|
||||||
|
|
||||||
if let Err(e) = fill_tess.tessellate_path(
|
// TODO(Herschel): 0 width indicates "hairline".
|
||||||
&ruffle_path_to_lyon_path(commands, true),
|
let width = if style.width.to_pixels() >= 1.0 {
|
||||||
&FillOptions::even_odd(),
|
style.width.to_pixels() as f32
|
||||||
&mut buffers_builder,
|
} else {
|
||||||
) {
|
1.0
|
||||||
// This may just be a degenerate path; skip it.
|
|
||||||
log::error!("Tessellation failure: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
FillStyle::LinearGradient(gradient) => {
|
|
||||||
flush_draw(DrawType::Color, &mut mesh, &mut lyon_mesh);
|
|
||||||
|
|
||||||
let mut buffers_builder = BuffersBuilder::new(
|
|
||||||
&mut lyon_mesh,
|
|
||||||
RuffleVertexCtor {
|
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = fill_tess.tessellate_path(
|
|
||||||
&ruffle_path_to_lyon_path(commands, true),
|
|
||||||
&FillOptions::even_odd(),
|
|
||||||
&mut buffers_builder,
|
|
||||||
) {
|
|
||||||
// This may just be a degenerate path; skip it.
|
|
||||||
log::error!("Tessellation failure: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut colors: Vec<[f32; 4]> = Vec::with_capacity(8);
|
|
||||||
let mut ratios: Vec<f32> = Vec::with_capacity(8);
|
|
||||||
for record in &gradient.records {
|
|
||||||
colors.push([
|
|
||||||
f32::from(record.color.r) / 255.0,
|
|
||||||
f32::from(record.color.g) / 255.0,
|
|
||||||
f32::from(record.color.b) / 255.0,
|
|
||||||
f32::from(record.color.a) / 255.0,
|
|
||||||
]);
|
|
||||||
ratios.push(f32::from(record.ratio) / 255.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let gradient = Gradient {
|
|
||||||
gradient_type: 0,
|
|
||||||
ratios,
|
|
||||||
colors,
|
|
||||||
num_colors: gradient.records.len() as u32,
|
|
||||||
matrix: swf_to_gl_matrix(gradient.matrix.clone()),
|
|
||||||
repeat_mode: 0,
|
|
||||||
focal_point: 0.0,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
flush_draw(DrawType::Gradient(gradient), &mut mesh, &mut lyon_mesh);
|
let mut options = StrokeOptions::default()
|
||||||
}
|
.with_line_width(width)
|
||||||
FillStyle::RadialGradient(gradient) => {
|
.with_line_join(match style.join_style {
|
||||||
flush_draw(DrawType::Color, &mut mesh, &mut lyon_mesh);
|
swf::LineJoinStyle::Round => tessellation::LineJoin::Round,
|
||||||
|
swf::LineJoinStyle::Bevel => tessellation::LineJoin::Bevel,
|
||||||
|
swf::LineJoinStyle::Miter(_) => tessellation::LineJoin::MiterClip,
|
||||||
|
})
|
||||||
|
.with_start_cap(match style.start_cap {
|
||||||
|
swf::LineCapStyle::None => tessellation::LineCap::Butt,
|
||||||
|
swf::LineCapStyle::Round => tessellation::LineCap::Round,
|
||||||
|
swf::LineCapStyle::Square => tessellation::LineCap::Square,
|
||||||
|
})
|
||||||
|
.with_end_cap(match style.end_cap {
|
||||||
|
swf::LineCapStyle::None => tessellation::LineCap::Butt,
|
||||||
|
swf::LineCapStyle::Round => tessellation::LineCap::Round,
|
||||||
|
swf::LineCapStyle::Square => tessellation::LineCap::Square,
|
||||||
|
});
|
||||||
|
|
||||||
let mut buffers_builder = BuffersBuilder::new(
|
if let swf::LineJoinStyle::Miter(limit) = style.join_style {
|
||||||
&mut lyon_mesh,
|
options = options.with_miter_limit(limit);
|
||||||
RuffleVertexCtor {
|
}
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = fill_tess.tessellate_path(
|
if let Err(e) = self.stroke_tess.tessellate_path(
|
||||||
&ruffle_path_to_lyon_path(commands, true),
|
&ruffle_path_to_lyon_path(commands, is_closed),
|
||||||
&FillOptions::even_odd(),
|
&options,
|
||||||
&mut buffers_builder,
|
&mut buffers_builder,
|
||||||
) {
|
) {
|
||||||
// This may just be a degenerate path; skip it.
|
// This may just be a degenerate path; skip it.
|
||||||
log::error!("Tessellation failure: {:?}", e);
|
log::error!("Tessellation failure: {:?}", e);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut colors: Vec<[f32; 4]> = Vec::with_capacity(8);
|
|
||||||
let mut ratios: Vec<f32> = Vec::with_capacity(8);
|
|
||||||
for record in &gradient.records {
|
|
||||||
colors.push([
|
|
||||||
f32::from(record.color.r) / 255.0,
|
|
||||||
f32::from(record.color.g) / 255.0,
|
|
||||||
f32::from(record.color.b) / 255.0,
|
|
||||||
f32::from(record.color.a) / 255.0,
|
|
||||||
]);
|
|
||||||
ratios.push(f32::from(record.ratio) / 255.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let gradient = Gradient {
|
|
||||||
gradient_type: 1,
|
|
||||||
ratios,
|
|
||||||
colors,
|
|
||||||
num_colors: gradient.records.len() as u32,
|
|
||||||
matrix: swf_to_gl_matrix(gradient.matrix.clone()),
|
|
||||||
repeat_mode: 0,
|
|
||||||
focal_point: 0.0,
|
|
||||||
};
|
|
||||||
|
|
||||||
flush_draw(DrawType::Gradient(gradient), &mut mesh, &mut lyon_mesh);
|
|
||||||
}
|
|
||||||
FillStyle::FocalGradient {
|
|
||||||
gradient,
|
|
||||||
focal_point,
|
|
||||||
} => {
|
|
||||||
flush_draw(DrawType::Color, &mut mesh, &mut lyon_mesh);
|
|
||||||
|
|
||||||
let mut buffers_builder = BuffersBuilder::new(
|
|
||||||
&mut lyon_mesh,
|
|
||||||
RuffleVertexCtor {
|
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = fill_tess.tessellate_path(
|
|
||||||
&ruffle_path_to_lyon_path(commands, true),
|
|
||||||
&FillOptions::even_odd(),
|
|
||||||
&mut buffers_builder,
|
|
||||||
) {
|
|
||||||
// This may just be a degenerate path; skip it.
|
|
||||||
log::error!("Tessellation failure: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut colors: Vec<[f32; 4]> = Vec::with_capacity(8);
|
|
||||||
let mut ratios: Vec<f32> = Vec::with_capacity(8);
|
|
||||||
for record in &gradient.records {
|
|
||||||
colors.push([
|
|
||||||
f32::from(record.color.r) / 255.0,
|
|
||||||
f32::from(record.color.g) / 255.0,
|
|
||||||
f32::from(record.color.b) / 255.0,
|
|
||||||
f32::from(record.color.a) / 255.0,
|
|
||||||
]);
|
|
||||||
ratios.push(f32::from(record.ratio) / 255.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
let gradient = Gradient {
|
|
||||||
gradient_type: 1,
|
|
||||||
ratios,
|
|
||||||
colors,
|
|
||||||
num_colors: gradient.records.len() as u32,
|
|
||||||
matrix: swf_to_gl_matrix(gradient.matrix.clone()),
|
|
||||||
repeat_mode: 0,
|
|
||||||
focal_point: *focal_point,
|
|
||||||
};
|
|
||||||
|
|
||||||
flush_draw(DrawType::Gradient(gradient), &mut mesh, &mut lyon_mesh);
|
|
||||||
}
|
|
||||||
FillStyle::Bitmap {
|
|
||||||
id,
|
|
||||||
matrix,
|
|
||||||
is_smoothed,
|
|
||||||
is_repeating,
|
|
||||||
} => {
|
|
||||||
flush_draw(DrawType::Color, &mut mesh, &mut lyon_mesh);
|
|
||||||
|
|
||||||
let mut buffers_builder = BuffersBuilder::new(
|
|
||||||
&mut lyon_mesh,
|
|
||||||
RuffleVertexCtor {
|
|
||||||
color: [1.0, 1.0, 1.0, 1.0],
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Err(e) = fill_tess.tessellate_path(
|
|
||||||
&ruffle_path_to_lyon_path(commands, true),
|
|
||||||
&FillOptions::even_odd(),
|
|
||||||
&mut buffers_builder,
|
|
||||||
) {
|
|
||||||
// This may just be a degenerate path; skip it.
|
|
||||||
log::error!("Tessellation failure: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (bitmap_width, bitmap_height) = get_bitmap_dimenions(*id).unwrap_or((1, 1));
|
|
||||||
|
|
||||||
let bitmap = Bitmap {
|
|
||||||
matrix: swf_bitmap_to_gl_matrix(
|
|
||||||
matrix.clone(),
|
|
||||||
bitmap_width,
|
|
||||||
bitmap_height,
|
|
||||||
),
|
|
||||||
id: *id,
|
|
||||||
is_smoothed: *is_smoothed,
|
|
||||||
is_repeating: *is_repeating,
|
|
||||||
};
|
|
||||||
|
|
||||||
flush_draw(DrawType::Bitmap(bitmap), &mut mesh, &mut lyon_mesh);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
DrawPath::Stroke {
|
|
||||||
style,
|
|
||||||
commands,
|
|
||||||
is_closed,
|
|
||||||
} => {
|
|
||||||
let color = [
|
|
||||||
f32::from(style.color.r) / 255.0,
|
|
||||||
f32::from(style.color.g) / 255.0,
|
|
||||||
f32::from(style.color.b) / 255.0,
|
|
||||||
f32::from(style.color.a) / 255.0,
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut buffers_builder =
|
|
||||||
BuffersBuilder::new(&mut lyon_mesh, RuffleVertexCtor { color });
|
|
||||||
|
|
||||||
// TODO(Herschel): 0 width indicates "hairline".
|
|
||||||
let width = if style.width.to_pixels() >= 1.0 {
|
|
||||||
style.width.to_pixels() as f32
|
|
||||||
} else {
|
|
||||||
1.0
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut options = StrokeOptions::default()
|
|
||||||
.with_line_width(width)
|
|
||||||
.with_line_join(match style.join_style {
|
|
||||||
swf::LineJoinStyle::Round => tessellation::LineJoin::Round,
|
|
||||||
swf::LineJoinStyle::Bevel => tessellation::LineJoin::Bevel,
|
|
||||||
swf::LineJoinStyle::Miter(_) => tessellation::LineJoin::MiterClip,
|
|
||||||
})
|
|
||||||
.with_start_cap(match style.start_cap {
|
|
||||||
swf::LineCapStyle::None => tessellation::LineCap::Butt,
|
|
||||||
swf::LineCapStyle::Round => tessellation::LineCap::Round,
|
|
||||||
swf::LineCapStyle::Square => tessellation::LineCap::Square,
|
|
||||||
})
|
|
||||||
.with_end_cap(match style.end_cap {
|
|
||||||
swf::LineCapStyle::None => tessellation::LineCap::Butt,
|
|
||||||
swf::LineCapStyle::Round => tessellation::LineCap::Round,
|
|
||||||
swf::LineCapStyle::Square => tessellation::LineCap::Square,
|
|
||||||
});
|
|
||||||
|
|
||||||
if let swf::LineJoinStyle::Miter(limit) = style.join_style {
|
|
||||||
options = options.with_miter_limit(limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = stroke_tess.tessellate_path(
|
|
||||||
&ruffle_path_to_lyon_path(commands, is_closed),
|
|
||||||
&options,
|
|
||||||
&mut buffers_builder,
|
|
||||||
) {
|
|
||||||
// This may just be a degenerate path; skip it.
|
|
||||||
log::error!("Tessellation failure: {:?}", e);
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flush_draw(DrawType::Color, &mut mesh, &mut lyon_mesh);
|
||||||
|
|
||||||
|
mesh
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
flush_draw(DrawType::Color, &mut mesh, &mut lyon_mesh);
|
impl Default for ShapeTessellator {
|
||||||
|
fn default() -> Self {
|
||||||
mesh
|
Self::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Mesh = Vec<Draw>;
|
type Mesh = Vec<Draw>;
|
||||||
|
@ -311,18 +318,19 @@ pub enum DrawType {
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct Gradient {
|
pub struct Gradient {
|
||||||
pub matrix: [[f32; 3]; 3],
|
pub matrix: [[f32; 3]; 3],
|
||||||
pub gradient_type: i32,
|
pub gradient_type: GradientType,
|
||||||
pub ratios: Vec<f32>,
|
pub ratios: Vec<f32>,
|
||||||
pub colors: Vec<[f32; 4]>,
|
pub colors: Vec<[f32; 4]>,
|
||||||
pub num_colors: u32,
|
pub num_colors: u32,
|
||||||
pub repeat_mode: i32,
|
pub repeat_mode: GradientSpread,
|
||||||
pub focal_point: f32,
|
pub focal_point: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[repr(C)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
pub position: [f32; 2],
|
pub position: [f32; 2],
|
||||||
pub color: [f32; 4],
|
pub color: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
|
@ -411,7 +419,7 @@ fn ruffle_path_to_lyon_path(commands: Vec<DrawCommand>, is_closed: bool) -> Path
|
||||||
}
|
}
|
||||||
|
|
||||||
struct RuffleVertexCtor {
|
struct RuffleVertexCtor {
|
||||||
color: [f32; 4],
|
color: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FillVertexConstructor<Vertex> for RuffleVertexCtor {
|
impl FillVertexConstructor<Vertex> for RuffleVertexCtor {
|
||||||
|
@ -431,3 +439,12 @@ impl StrokeVertexConstructor<Vertex> for RuffleVertexCtor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub use swf::GradientSpread;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub enum GradientType {
|
||||||
|
Linear,
|
||||||
|
Radial,
|
||||||
|
Focal,
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
[package]
|
||||||
|
name = "ruffle_render_webgl"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Mike Welsh <mwelsh@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
fnv = "1.0.3"
|
||||||
|
js-sys = "0.3.25"
|
||||||
|
log = "0.4"
|
||||||
|
percent-encoding = "2.1.0"
|
||||||
|
png = "0.16.3"
|
||||||
|
ruffle_render_common_tess = { path = "../common_tess" }
|
||||||
|
ruffle_web_common = { path = "../../web/common" }
|
||||||
|
wasm-bindgen = "0.2.57"
|
||||||
|
|
||||||
|
[dependencies.jpeg-decoder]
|
||||||
|
version = "0.1.18"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.ruffle_core]
|
||||||
|
path = "../../core"
|
||||||
|
default-features = false
|
||||||
|
|
||||||
|
[dependencies.web-sys]
|
||||||
|
version = "0.3.34"
|
||||||
|
features = ["HtmlCanvasElement", "HtmlElement", "Node", "OesVertexArrayObject", "WebGlBuffer", "WebGlFramebuffer", "WebGlProgram",
|
||||||
|
"WebGlRenderbuffer", "WebGlRenderingContext", "WebGl2RenderingContext", "WebGlShader", "WebGlTexture", "WebGlUniformLocation",
|
||||||
|
"WebGlVertexArrayObject"]
|
|
@ -0,0 +1,25 @@
|
||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
uniform mat4 view_matrix;
|
||||||
|
uniform mat4 world_matrix;
|
||||||
|
uniform vec4 mult_color;
|
||||||
|
uniform vec4 add_color;
|
||||||
|
uniform mat3 u_matrix;
|
||||||
|
|
||||||
|
uniform sampler2D u_texture;
|
||||||
|
|
||||||
|
varying vec2 frag_uv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec4 color = texture2D(u_texture, frag_uv);
|
||||||
|
|
||||||
|
// Unmultiply alpha before apply color transform.
|
||||||
|
if( color.a > 0.0 ) {
|
||||||
|
color.rgb /= color.a;
|
||||||
|
color = mult_color * color + add_color;
|
||||||
|
color.rgb *= color.a;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = color;
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
uniform mat4 view_matrix;
|
||||||
|
uniform mat4 world_matrix;
|
||||||
|
uniform vec4 mult_color;
|
||||||
|
uniform vec4 add_color;
|
||||||
|
|
||||||
|
varying vec4 frag_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_FragColor = frag_color;
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
uniform mat4 view_matrix;
|
||||||
|
uniform mat4 world_matrix;
|
||||||
|
uniform vec4 mult_color;
|
||||||
|
uniform vec4 add_color;
|
||||||
|
|
||||||
|
attribute vec2 position;
|
||||||
|
attribute vec4 color;
|
||||||
|
varying vec4 frag_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
frag_color = color * mult_color + add_color;
|
||||||
|
gl_Position = view_matrix * world_matrix * vec4(position, 0.0, 1.0);
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
uniform mat4 view_matrix;
|
||||||
|
uniform mat4 world_matrix;
|
||||||
|
uniform vec4 mult_color;
|
||||||
|
uniform vec4 add_color;
|
||||||
|
uniform mat3 u_matrix;
|
||||||
|
|
||||||
|
uniform int u_gradient_type;
|
||||||
|
uniform float u_ratios[8];
|
||||||
|
uniform vec4 u_colors[8];
|
||||||
|
uniform int u_num_colors;
|
||||||
|
uniform int u_repeat_mode;
|
||||||
|
uniform float u_focal_point;
|
||||||
|
|
||||||
|
varying vec2 frag_uv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float t;
|
||||||
|
if( u_gradient_type == 0 )
|
||||||
|
{
|
||||||
|
t = frag_uv.x;
|
||||||
|
}
|
||||||
|
else if( u_gradient_type == 1 )
|
||||||
|
{
|
||||||
|
t = length(frag_uv * 2.0 - 1.0);
|
||||||
|
}
|
||||||
|
else if( u_gradient_type == 2 )
|
||||||
|
{
|
||||||
|
vec2 uv = frag_uv * 2.0 - 1.0;
|
||||||
|
vec2 d = vec2(u_focal_point, 0.0) - uv;
|
||||||
|
float l = length(d);
|
||||||
|
d /= l;
|
||||||
|
t = l / (sqrt(1.0 - u_focal_point*u_focal_point*d.y*d.y) + u_focal_point*d.x);
|
||||||
|
}
|
||||||
|
if( u_repeat_mode == 0 )
|
||||||
|
{
|
||||||
|
// Clamp
|
||||||
|
t = clamp(t, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
else if( u_repeat_mode == 1 )
|
||||||
|
{
|
||||||
|
// Repeat
|
||||||
|
t = fract(t);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Mirror
|
||||||
|
if( t < 0.0 )
|
||||||
|
{
|
||||||
|
t = -t;
|
||||||
|
}
|
||||||
|
|
||||||
|
if( int(mod(t, 2.0)) == 0 ) {
|
||||||
|
t = fract(t);
|
||||||
|
} else {
|
||||||
|
t = 1.0 - fract(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: No non-constant array access in WebGL 1, so the following is kind of painful.
|
||||||
|
// We'd probably be better off passing in the gradient as a texture and sampling from there.
|
||||||
|
vec4 color;
|
||||||
|
float a;
|
||||||
|
if( t <= u_ratios[0] ) {
|
||||||
|
color = u_colors[0];
|
||||||
|
} else if( t <= u_ratios[1] ) {
|
||||||
|
a = (t - u_ratios[0]) / (u_ratios[1] - u_ratios[0]);
|
||||||
|
color = mix(u_colors[0], u_colors[1], a);
|
||||||
|
} else if( t <= u_ratios[2] ) {
|
||||||
|
a = (t - u_ratios[1]) / (u_ratios[2] - u_ratios[1]);
|
||||||
|
color = mix(u_colors[1], u_colors[2], a);
|
||||||
|
} else if( t <= u_ratios[3] ) {
|
||||||
|
a = (t - u_ratios[2]) / (u_ratios[3] - u_ratios[2]);
|
||||||
|
color = mix(u_colors[2], u_colors[3], a);
|
||||||
|
} else if( t <= u_ratios[4] ) {
|
||||||
|
a = (t - u_ratios[3]) / (u_ratios[4] - u_ratios[3]);
|
||||||
|
color = mix(u_colors[3], u_colors[4], a);
|
||||||
|
} else if( t <= u_ratios[5] ) {
|
||||||
|
a = (t - u_ratios[4]) / (u_ratios[5] - u_ratios[4]);
|
||||||
|
color = mix(u_colors[4], u_colors[5], a);
|
||||||
|
} else if( t <= u_ratios[6] ) {
|
||||||
|
a = (t - u_ratios[5]) / (u_ratios[6] - u_ratios[5]);
|
||||||
|
color = mix(u_colors[5], u_colors[6], a);
|
||||||
|
} else if( t <= u_ratios[7] ) {
|
||||||
|
a = (t - u_ratios[6]) / (u_ratios[7] - u_ratios[6]);
|
||||||
|
color = mix(u_colors[6], u_colors[7], a);
|
||||||
|
} else {
|
||||||
|
color = u_colors[7];
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = mult_color * color + add_color;
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
#version 100
|
||||||
|
precision mediump float;
|
||||||
|
|
||||||
|
uniform mat4 view_matrix;
|
||||||
|
uniform mat4 world_matrix;
|
||||||
|
uniform vec4 mult_color;
|
||||||
|
uniform vec4 add_color;
|
||||||
|
uniform mat3 u_matrix;
|
||||||
|
|
||||||
|
attribute vec2 position;
|
||||||
|
attribute vec4 color;
|
||||||
|
|
||||||
|
varying vec2 frag_uv;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
frag_uv = vec2(u_matrix * vec3(position, 1.0));
|
||||||
|
gl_Position = view_matrix * world_matrix * vec4(position, 0.0, 1.0);
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -8,8 +8,10 @@ edition = "2018"
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
default = ["console_error_panic_hook", "console_log", "webgl"]
|
||||||
lzma = ["ruffle_core/lzma"]
|
lzma = ["ruffle_core/lzma"]
|
||||||
default = ["console_error_panic_hook", "console_log"]
|
canvas = ["ruffle_render_canvas"]
|
||||||
|
webgl = ["ruffle_render_webgl"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1.3.4"
|
byteorder = "1.3.4"
|
||||||
|
@ -19,8 +21,9 @@ fnv = "1.0.3"
|
||||||
generational-arena = "0.2.7"
|
generational-arena = "0.2.7"
|
||||||
js-sys = "0.3.25"
|
js-sys = "0.3.25"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
ruffle_render_canvas = { path = "../render/canvas" }
|
ruffle_render_canvas = { path = "../render/canvas", optional = true }
|
||||||
ruffle_web_common = { path = "common" }
|
ruffle_web_common = { path = "common" }
|
||||||
|
ruffle_render_webgl = { path = "../render/webgl", optional = true }
|
||||||
url = "2.1.1"
|
url = "2.1.1"
|
||||||
wasm-bindgen = "0.2.57"
|
wasm-bindgen = "0.2.57"
|
||||||
wasm-bindgen-futures = "0.4.4"
|
wasm-bindgen-futures = "0.4.4"
|
||||||
|
|
|
@ -5,5 +5,10 @@ authors = ["Mike Welsh <mwelsh@gmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
js-sys = "0.3.25"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
wasm-bindgen = "0.2.57"
|
wasm-bindgen = "0.2.57"
|
||||||
|
|
||||||
|
[dependencies.web-sys]
|
||||||
|
version = "0.3.34"
|
||||||
|
features = ["Window"]
|
|
@ -33,3 +33,14 @@ impl<T> JsResult<T> for Result<T, JsValue> {
|
||||||
self.map_err(|value| JsError { value })
|
self.map_err(|value| JsError { value })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Very bad way to guess if we're running on a tablet/mobile.
|
||||||
|
pub fn is_mobile_or_tablet() -> bool {
|
||||||
|
if let Some(window) = web_sys::window() {
|
||||||
|
if let Ok(val) = js_sys::Reflect::get(&window, &JsValue::from("orientation")) {
|
||||||
|
return !val.is_undefined();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@ mod navigator;
|
||||||
use crate::{audio::WebAudioBackend, input::WebInputBackend, navigator::WebNavigatorBackend};
|
use crate::{audio::WebAudioBackend, input::WebInputBackend, navigator::WebNavigatorBackend};
|
||||||
use generational_arena::{Arena, Index};
|
use generational_arena::{Arena, Index};
|
||||||
use js_sys::Uint8Array;
|
use js_sys::Uint8Array;
|
||||||
|
use ruffle_core::backend::render::RenderBackend;
|
||||||
use ruffle_core::PlayerEvent;
|
use ruffle_core::PlayerEvent;
|
||||||
use ruffle_render_canvas::WebCanvasRenderBackend;
|
|
||||||
use std::mem::drop;
|
use std::mem::drop;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::{cell::RefCell, error::Error, num::NonZeroI32};
|
use std::{cell::RefCell, error::Error, num::NonZeroI32};
|
||||||
|
@ -101,7 +101,7 @@ impl Ruffle {
|
||||||
swf_data.copy_to(&mut data[..]);
|
swf_data.copy_to(&mut data[..]);
|
||||||
|
|
||||||
let window = web_sys::window().ok_or_else(|| "Expected window")?;
|
let window = web_sys::window().ok_or_else(|| "Expected window")?;
|
||||||
let renderer = Box::new(WebCanvasRenderBackend::new(&canvas)?);
|
let renderer = create_renderer(&canvas)?;
|
||||||
let audio = Box::new(WebAudioBackend::new()?);
|
let audio = Box::new(WebAudioBackend::new()?);
|
||||||
let navigator = Box::new(WebNavigatorBackend::new());
|
let navigator = Box::new(WebNavigatorBackend::new());
|
||||||
let input = Box::new(WebInputBackend::new(&canvas));
|
let input = Box::new(WebInputBackend::new(&canvas));
|
||||||
|
@ -468,3 +468,24 @@ impl Ruffle {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn create_renderer(canvas: &HtmlCanvasElement) -> Result<Box<dyn RenderBackend>, Box<dyn Error>> {
|
||||||
|
#[cfg(not(any(feature = "canvas", feature = "webgl")))]
|
||||||
|
std::compile_error!("You must enable one of the render backend features (e.g., webgl).");
|
||||||
|
|
||||||
|
#[cfg(feature = "webgl")]
|
||||||
|
{
|
||||||
|
if let Ok(renderer) = ruffle_render_webgl::WebGlRenderBackend::new(canvas) {
|
||||||
|
return Ok(Box::new(renderer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "canvas")]
|
||||||
|
{
|
||||||
|
if let Ok(renderer) = ruffle_render_canvas::WebCanvasRenderBackend::new(canvas) {
|
||||||
|
return Ok(Box::new(renderer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err("Unable to create renderer".into())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue