Start a new `web_canvas` crate, for usage in the browser as a `<canvas>` replacement

This commit is contained in:
Patrick Walton 2020-07-04 09:33:22 -07:00
parent e04f1b7950
commit e54e0e4760
9 changed files with 320 additions and 49 deletions

70
Cargo.lock generated
View File

@ -202,24 +202,6 @@ dependencies = [
"nix 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "canvas-webgl-minimal"
version = "0.1.0"
dependencies = [
"pathfinder_canvas 0.5.0",
"pathfinder_color 0.5.0",
"pathfinder_content 0.5.0",
"pathfinder_geometry 0.5.1",
"pathfinder_gl 0.5.0",
"pathfinder_gpu 0.5.0",
"pathfinder_renderer 0.5.0",
"pathfinder_resources 0.5.0",
"pathfinder_webgl 0.1.0",
"wasm-bindgen 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-test 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"web-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "canvas_glutin_minimal"
version = "0.1.0"
@ -335,6 +317,24 @@ dependencies = [
"sdl2-sys 0.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "canvas_webgl_minimal"
version = "0.1.0"
dependencies = [
"pathfinder_canvas 0.5.0",
"pathfinder_color 0.5.0",
"pathfinder_content 0.5.0",
"pathfinder_geometry 0.5.1",
"pathfinder_gl 0.5.0",
"pathfinder_gpu 0.5.0",
"pathfinder_renderer 0.5.0",
"pathfinder_resources 0.5.0",
"pathfinder_webgl 0.1.0",
"wasm-bindgen 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-test 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"web-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "cbindgen"
version = "0.13.2"
@ -671,6 +671,14 @@ dependencies = [
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "css-color-parser"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "data-url"
version = "0.1.0"
@ -1299,6 +1307,11 @@ dependencies = [
"arrayvec 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lazy_static"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "lazy_static"
version = "1.4.0"
@ -2015,6 +2028,25 @@ dependencies = [
"serde_json 1.0.51 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pathfinder_web_canvas"
version = "0.1.0"
dependencies = [
"css-color-parser 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
"pathfinder_canvas 0.5.0",
"pathfinder_color 0.5.0",
"pathfinder_content 0.5.0",
"pathfinder_geometry 0.5.1",
"pathfinder_gl 0.5.0",
"pathfinder_gpu 0.5.0",
"pathfinder_renderer 0.5.0",
"pathfinder_resources 0.5.0",
"pathfinder_webgl 0.1.0",
"wasm-bindgen 0.2.64 (registry+https://github.com/rust-lang/crates.io-index)",
"wasm-bindgen-test 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)",
"web-sys 0.3.37 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "pathfinder_webgl"
version = "0.1.0"
@ -3216,6 +3248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum crossbeam-epoch 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "058ed274caafc1f60c4997b5fc07bf7dc7cca454af7c6e81edffe5f33f70dace"
"checksum crossbeam-queue 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
"checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
"checksum css-color-parser 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9ccb6ce7ef97e6dc6e575e51b596c9889a5cc88a307b5ef177d215c61fd7581d"
"checksum data-url 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d33fe99ccedd6e84bc035f1931bb2e6be79739d6242bd895e7311c886c50dc9c"
"checksum deflate 0.8.4 (registry+https://github.com/rust-lang/crates.io-index)" = "e7e5d2a2273fed52a7f947ee55b092c4057025d7a3e04e5ecdbd25d6c3fb1bd7"
"checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3"
@ -3283,6 +3316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum khronos 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c0711aaa80e6ba6eb1fa8978f1f46bfcb38ceb2f3f33f3736efbff39dac89f50"
"checksum khronos_api 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
"checksum kurbo 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2520c9c9010461ec2b4573599bca458272319a314fd0b9476cacfcb9b6e5adc8"
"checksum lazy_static 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417"
"checksum lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
"checksum lazycell 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b294d6fa9ee409a054354afc4352b0b9ef7ca222c69b8812cbea9e7d2bf3783f"
"checksum leak 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "bd100e01f1154f2908dfa7d02219aeab25d0b9c7fa955164192e3245255a0c73"

View File

@ -34,6 +34,7 @@ members = [
"utils/gamma-lut",
"utils/svg-to-skia",
"utils/convert",
"web_canvas",
"webgl",
]

View File

@ -89,6 +89,15 @@ impl Canvas {
Canvas { scene }
}
/// Returns the inner scene, replacing it with a blank scene.
#[inline]
pub fn take_scene(&mut self) -> Scene {
let view_box = self.scene.view_box();
let mut new_scene = Scene::new();
new_scene.set_view_box(view_box);
mem::replace(&mut self.scene, new_scene)
}
#[inline]
pub fn into_scene(self) -> Scene {
self.scene
@ -131,6 +140,11 @@ impl CanvasRenderingContext2D {
&self.canvas
}
#[inline]
pub fn canvas_mut(&mut self) -> &mut Canvas {
&mut self.canvas
}
#[inline]
pub fn into_canvas(self) -> Canvas {
self.canvas

View File

@ -446,29 +446,6 @@ impl IntoFontCollection for Vec<FontFamily> {
}
}
/*
impl IntoFontCollection for Handle {
#[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Arc<FontCollection> {
self.load().expect("Failed to load the font!").into_font_collection(context)
}
}
impl<'a> IntoFontCollection for &'a [Handle] {
#[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Arc<FontCollection> {
let mut font_collection = FontCollection::new();
for handle in self {
let postscript_name = handle.postscript_name();
let font = handle.load().expect("Failed to load the font!");
font_collection.add_family(FontFamily::new_from_font(font));
}
Arc::new(font_collection)
}
}
*/
impl IntoFontCollection for Font {
#[inline]
fn into_font_collection(self, context: &CanvasFontContext) -> Arc<FontCollection> {

View File

@ -1,5 +1,5 @@
[package]
name = "canvas-webgl-minimal"
name = "canvas_webgl_minimal"
version = "0.1.0"
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018"
@ -11,7 +11,7 @@ crate-type = ["cdylib", "rlib"]
default = []
[dependencies]
wasm-bindgen = "0.2.63"
wasm-bindgen = "0.2"
[dependencies.pathfinder_canvas]
path = "../../canvas"
@ -45,7 +45,7 @@ version = "0.3"
features = ["Window"]
[dev-dependencies]
wasm-bindgen-test = "0.3.13"
wasm-bindgen-test = "0.3"
[profile.release]
# Tell `rustc` to optimize for small code size.

View File

@ -24,11 +24,6 @@ use web_sys::{self, HtmlCanvasElement, WebGl2RenderingContext};
mod utils;
#[wasm_bindgen]
extern {
fn alert(s: &str);
}
#[wasm_bindgen]
pub fn rust_main() {
utils::set_panic_hook();

View File

@ -290,6 +290,10 @@ impl<D> Renderer<D> where D: Device {
}
}
pub fn destroy(self) -> D {
self.core.device
}
pub fn begin_scene(&mut self) {
self.core.framebuffer_flags = FramebufferFlags::empty();

50
web_canvas/Cargo.toml Normal file
View File

@ -0,0 +1,50 @@
[package]
name = "pathfinder_web_canvas"
version = "0.1.0"
authors = ["Patrick Walton <pcwalton@mimiga.net>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
css-color-parser = "0.1"
wasm-bindgen = "0.2"
[dependencies.pathfinder_canvas]
path = "../canvas"
[dependencies.pathfinder_color]
path = "../color"
[dependencies.pathfinder_content]
path = "../content"
[dependencies.pathfinder_geometry]
path = "../geometry"
[dependencies.pathfinder_gl]
path = "../gl"
[dependencies.pathfinder_gpu]
path = "../gpu"
[dependencies.pathfinder_renderer]
path = "../renderer"
[dependencies.pathfinder_resources]
path = "../resources"
[dependencies.pathfinder_webgl]
path = "../webgl"
[dependencies.web-sys]
version = "0.3"
features = ["Window", "console"]
[dev-dependencies]
wasm-bindgen-test = "0.3"
[profile.release]
# Tell `rustc` to optimize for small code size.
opt-level = "s"

196
web_canvas/src/lib.rs Normal file
View File

@ -0,0 +1,196 @@
// pathfinder/web_canvas/src/lib.rs
//
// Copyright © 2020 The Pathfinder Project Developers.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use css_color_parser::Color;
use pathfinder_canvas::{Canvas, CanvasFontContext, CanvasRenderingContext2D, FillRule, Path2D};
use pathfinder_color::{ColorF, ColorU};
use pathfinder_geometry::rect::RectF;
use pathfinder_geometry::transform2d::Transform2F;
use pathfinder_geometry::vector::{Vector2I, vec2f, vec2i};
use pathfinder_renderer::concurrent::executor::SequentialExecutor;
use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererMode, RendererOptions};
use pathfinder_renderer::gpu::renderer::Renderer;
use pathfinder_renderer::options::BuildOptions;
use pathfinder_resources::embedded::EmbeddedResourceLoader;
use pathfinder_webgl::WebGlDevice;
use std::mem;
use std::str::FromStr;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use web_sys::{self, console, HtmlCanvasElement, WebGl2RenderingContext};
#[wasm_bindgen]
pub struct PFCanvasRenderingContext2D {
html_canvas: HtmlCanvasElement,
context: CanvasRenderingContext2D,
resource_loader: EmbeddedResourceLoader,
renderer: Option<Renderer<WebGlDevice>>,
default_path: Path2D,
// FIXME(pcwalton): Remove this once renderers are resizable.
prev_framebuffer_size: Vector2I,
}
#[wasm_bindgen(js_name = "createContext")]
pub fn create_context(html_canvas: HtmlCanvasElement) -> PFCanvasRenderingContext2D {
let context = html_canvas.get_context("webgl2")
.unwrap()
.unwrap()
.dyn_into::<WebGl2RenderingContext>()
.unwrap();
// Get the real size of the window, taking HiDPI into account.
let framebuffer_size = vec2i(html_canvas.width() as i32, html_canvas.height() as i32);
// Create a Pathfinder GL device.
let pathfinder_device = WebGlDevice::new(context);
// Create a Pathfinder renderer.
let mode = RendererMode::default_for_device(&pathfinder_device);
let options = RendererOptions {
dest: DestFramebuffer::full_window(framebuffer_size),
background_color: Some(ColorF::white()),
..RendererOptions::default()
};
let resource_loader = EmbeddedResourceLoader::new();
let renderer = Renderer::new(pathfinder_device, &resource_loader, mode, options);
// Make a canvas.
let font_context = CanvasFontContext::from_system_source();
let context = Canvas::new(framebuffer_size.to_f32()).get_context_2d(font_context);
PFCanvasRenderingContext2D {
html_canvas,
context,
resource_loader,
renderer: Some(renderer),
default_path: Path2D::new(),
prev_framebuffer_size: framebuffer_size,
}
}
#[wasm_bindgen]
impl PFCanvasRenderingContext2D {
pub fn flush(&mut self) {
// Update framebuffer size.
let framebuffer_size = vec2i(self.html_canvas.width() as i32,
self.html_canvas.height() as i32);
if self.prev_framebuffer_size != framebuffer_size {
// Recreate the Pathfinder renderer.
//
// FIXME(pcwalton): This shouldn't be necessary!
let pathfinder_device = self.renderer.take().unwrap().destroy();
let mode = RendererMode::default_for_device(&pathfinder_device);
let options = RendererOptions {
dest: DestFramebuffer::full_window(framebuffer_size),
background_color: Some(ColorF::white()),
..RendererOptions::default()
};
self.renderer = Some(Renderer::new(pathfinder_device,
&self.resource_loader,
mode,
options));
self.prev_framebuffer_size = framebuffer_size;
}
let mut scene = self.context.canvas_mut().take_scene();
scene.build_and_render(self.renderer.as_mut().unwrap(),
BuildOptions::default(),
SequentialExecutor);
}
#[wasm_bindgen(getter)]
pub fn canvas(&self) -> HtmlCanvasElement {
self.html_canvas.clone()
}
#[wasm_bindgen(js_name = "fillStyle")]
#[wasm_bindgen(setter)]
pub fn set_fill_style(&mut self, new_style: &str) {
let css_color = match Color::from_str(new_style) {
Err(_) => return,
Ok(css_color) => css_color,
};
let color = ColorU::new(css_color.r,
css_color.g,
css_color.b,
(css_color.a * 255.0).round() as u8);
self.context.set_fill_style(color);
}
pub fn save(&mut self) {
self.context.save();
}
pub fn restore(&mut self) {
self.context.restore();
}
#[wasm_bindgen(js_name = "fillRect")]
pub fn fill_rect(&mut self, x: f32, y: f32, width: f32, height: f32) {
self.context.fill_rect(RectF::new(vec2f(x, y), vec2f(width, height)));
}
pub fn transform(&mut self, a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) {
let new_transform = self.context.transform() * Transform2F::row_major(a, c, e, b, d, f);
self.context.set_transform(&new_transform)
}
pub fn translate(&mut self, x: f32, y: f32) {
self.context.translate(vec2f(x, y))
}
pub fn scale(&mut self, x: f32, y: f32) {
self.context.scale(vec2f(x, y))
}
pub fn rotate(&mut self, angle: f32) {
self.context.rotate(angle)
}
#[wasm_bindgen(js_name = "beginPath")]
pub fn begin_path(&mut self) {
self.default_path = Path2D::new();
}
#[wasm_bindgen(js_name = "moveTo")]
pub fn move_to(&mut self, x: f32, y: f32) {
self.default_path.move_to(vec2f(x, y))
}
#[wasm_bindgen(js_name = "lineTo")]
pub fn line_to(&mut self, x: f32, y: f32) {
self.default_path.line_to(vec2f(x, y))
}
#[wasm_bindgen(js_name = "bezierCurveTo")]
pub fn bezier_curve_to(&mut self, cp1x: f32, cp1y: f32, cp2x: f32, cp2y: f32, x: f32, y: f32) {
self.default_path.bezier_curve_to(vec2f(cp1x, cp1y), vec2f(cp2x, cp2y), vec2f(x, y))
}
#[wasm_bindgen(js_name = "quadraticCurveTo")]
pub fn quadratic_curve_to(&mut self, cpx: f32, cpy: f32, x: f32, y: f32) {
self.default_path.quadratic_curve_to(vec2f(cpx, cpy), vec2f(x, y))
}
#[wasm_bindgen(js_name = "closePath")]
pub fn close_path(&mut self) {
self.default_path.close_path();
}
pub fn fill(&mut self) {
let path = self.default_path.clone();
self.context.fill_path(path, FillRule::Winding);
}
pub fn stroke(&mut self) {
let path = self.default_path.clone();
self.context.stroke_path(path);
}
}