From 236013815dffb5300bb484e1110dd395e1951b12 Mon Sep 17 00:00:00 2001 From: Toniman575 Date: Tue, 28 Feb 2023 14:16:48 +0100 Subject: [PATCH 1/4] Added `from_canvas` methods to `Surface` --- Cargo.toml | 2 +- src/lib.rs | 3 ++ src/web.rs | 139 ++++++++++++++++++++++++++++++++++++++++++++++------- 3 files changed, 125 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index c6da900a..88d13462 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ wasm-bindgen = "0.2.78" [target.'cfg(target_arch = "wasm32")'.dependencies.web-sys] version = "0.3.55" -features = ["CanvasRenderingContext2d", "Document", "Element", "HtmlCanvasElement", "ImageData", "Window"] +features = ["CanvasRenderingContext2d", "Document", "Element", "HtmlCanvasElement", "ImageData", "OffscreenCanvas", "Window"] [target.'cfg(target_os = "redox")'.dependencies] redox_syscall = "0.3" diff --git a/src/lib.rs b/src/lib.rs index f7dadf63..01927df7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,6 +35,9 @@ use raw_window_handle::{ HasRawDisplayHandle, HasRawWindowHandle, RawDisplayHandle, RawWindowHandle, }; +#[cfg(target_arch = "wasm32")] +pub use self::web::SurfaceExtWeb; + /// An instance of this struct contains the platform-specific data that must be managed in order to /// write to a window on that platform. pub struct Context { diff --git a/src/web.rs b/src/web.rs index c8286d12..63827bfb 100644 --- a/src/web.rs +++ b/src/web.rs @@ -2,15 +2,20 @@ #![allow(clippy::uninlined_format_args)] +use js_sys::Object; use raw_window_handle::WebWindowHandle; +use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsCast; +use wasm_bindgen::JsValue; use web_sys::CanvasRenderingContext2d; use web_sys::HtmlCanvasElement; use web_sys::ImageData; +use web_sys::OffscreenCanvas; use crate::error::SwResultExt; use crate::SoftBufferError; use std::convert::TryInto; +use std::marker::PhantomData; use std::num::NonZeroU32; /// Display implementation for the web platform. @@ -31,12 +36,23 @@ impl WebDisplayImpl { } } -pub struct WebImpl { - /// The handle to the canvas that we're drawing to. - canvas: HtmlCanvasElement, +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(js_name = OffscreenCanvasRenderingContext2D)] + pub type OffscreenCanvasRenderingContext2d; + + #[wasm_bindgen(catch, method, structural, js_class = "OffscreenCanvasRenderingContext2D", js_name = putImageData)] + fn put_image_data( + this: &OffscreenCanvasRenderingContext2d, + imagedata: &ImageData, + dx: f64, + dy: f64, + ) -> Result<(), JsValue>; +} - /// The 2D rendering context for the canvas. - ctx: CanvasRenderingContext2d, +pub struct WebImpl { + /// The handle and context to the canvas that we're drawing to. + canvas: Canvas, /// The buffer that we're drawing to. buffer: Vec, @@ -45,6 +61,17 @@ pub struct WebImpl { width: u32, } +pub enum Canvas { + Canvas { + canvas: HtmlCanvasElement, + ctx: CanvasRenderingContext2d, + }, + OffscreenCanvas { + canvas: OffscreenCanvas, + ctx: OffscreenCanvasRenderingContext2d, + }, +} + impl WebImpl { pub fn new(display: &WebDisplayImpl, handle: WebWindowHandle) -> Result { let canvas: HtmlCanvasElement = display @@ -56,22 +83,45 @@ impl WebImpl { // We already made sure this was a canvas in `querySelector`. .unchecked_into(); - let ctx = canvas - .get_context("2d") - .ok() + Self::from_canvas(canvas) + } + + fn from_canvas(canvas: HtmlCanvasElement) -> Result { + let ctx = Self::resolve_ctx(canvas.get_context("2d").ok(), "CanvasRenderingContext2d")?; + + Ok(Self { + canvas: Canvas::Canvas { canvas, ctx }, + buffer: Vec::new(), + width: 0, + }) + } + + fn from_offscreen_canvas(canvas: OffscreenCanvas) -> Result { + let ctx = Self::resolve_ctx( + canvas.get_context("2d").ok(), + "OffscreenCanvasRenderingContext2d", + )?; + + Ok(Self { + canvas: Canvas::OffscreenCanvas { canvas, ctx }, + buffer: Vec::new(), + width: 0, + }) + } + + fn resolve_ctx( + result: Option>, + name: &str, + ) -> Result { + let ctx = result .swbuf_err("Canvas already controlled using `OffscreenCanvas`")? .swbuf_err( "A canvas context other than `CanvasRenderingContext2d` was already created", )? .dyn_into() - .expect("`getContext(\"2d\") didn't return a `CanvasRenderingContext2d`"); + .unwrap_or_else(|_| panic!("`getContext(\"2d\") didn't return a `{name}`")); - Ok(Self { - canvas, - ctx, - buffer: Vec::new(), - width: 0, - }) + Ok(ctx) } /// Resize the canvas to the given dimensions. @@ -96,6 +146,58 @@ impl WebImpl { } } +/// Extension methods for the Wasm target on [`Surface`](crate::Surface). +pub trait SurfaceExtWeb: Sized { + /// Creates a new instance of this struct, using the provided [`HtmlCanvasElement`]. + fn from_canvas(canvas: HtmlCanvasElement) -> Result; + + /// Creates a new instance of this struct, using the provided [`HtmlCanvasElement`]. + fn from_offscreen_canvas(offscreen_canvas: OffscreenCanvas) -> Result; +} + +impl SurfaceExtWeb for crate::Surface { + fn from_canvas(canvas: HtmlCanvasElement) -> Result { + let imple = crate::SurfaceDispatch::Web(WebImpl::from_canvas(canvas)?); + + Ok(Self { + surface_impl: Box::new(imple), + _marker: PhantomData, + }) + } + + fn from_offscreen_canvas(offscreen_canvas: OffscreenCanvas) -> Result { + let imple = crate::SurfaceDispatch::Web(WebImpl::from_offscreen_canvas(offscreen_canvas)?); + + Ok(Self { + surface_impl: Box::new(imple), + _marker: PhantomData, + }) + } +} + +impl Canvas { + fn set_width(&self, width: u32) { + match self { + Self::Canvas { canvas, .. } => canvas.set_width(width), + Self::OffscreenCanvas { canvas, .. } => canvas.set_width(width), + } + } + + fn set_height(&self, height: u32) { + match self { + Self::Canvas { canvas, .. } => canvas.set_height(height), + Self::OffscreenCanvas { canvas, .. } => canvas.set_height(height), + } + } + + fn put_image_data(&self, imagedata: &ImageData, dx: f64, dy: f64) -> Result<(), JsValue> { + match self { + Self::Canvas { ctx, .. } => ctx.put_image_data(imagedata, dx, dy), + Self::OffscreenCanvas { ctx, .. } => ctx.put_image_data(imagedata, dx, dy), + } + } +} + pub struct BufferImpl<'a> { imp: &'a mut WebImpl, } @@ -123,8 +225,6 @@ impl<'a> BufferImpl<'a> { #[cfg(target_feature = "atomics")] let result = { use js_sys::{Uint8Array, Uint8ClampedArray}; - use wasm_bindgen::prelude::wasm_bindgen; - use wasm_bindgen::JsValue; #[wasm_bindgen] extern "C" { @@ -149,7 +249,10 @@ impl<'a> BufferImpl<'a> { let image_data = result.unwrap(); // This can only throw an error if `data` is detached, which is impossible. - self.imp.ctx.put_image_data(&image_data, 0.0, 0.0).unwrap(); + self.imp + .canvas + .put_image_data(&image_data, 0.0, 0.0) + .unwrap(); Ok(()) } From 206bd17d61597c4d87ffe30d9af39b7abd73381c Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Mon, 8 May 2023 12:06:49 +0200 Subject: [PATCH 2/4] Remove `OffscreenCanvas` support --- Cargo.toml | 2 +- src/web.rs | 93 ++++++------------------------------------------------ 2 files changed, 11 insertions(+), 84 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 88d13462..c6da900a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,7 +57,7 @@ wasm-bindgen = "0.2.78" [target.'cfg(target_arch = "wasm32")'.dependencies.web-sys] version = "0.3.55" -features = ["CanvasRenderingContext2d", "Document", "Element", "HtmlCanvasElement", "ImageData", "OffscreenCanvas", "Window"] +features = ["CanvasRenderingContext2d", "Document", "Element", "HtmlCanvasElement", "ImageData", "Window"] [target.'cfg(target_os = "redox")'.dependencies] redox_syscall = "0.3" diff --git a/src/web.rs b/src/web.rs index 63827bfb..f7bf1abf 100644 --- a/src/web.rs +++ b/src/web.rs @@ -4,13 +4,10 @@ use js_sys::Object; use raw_window_handle::WebWindowHandle; -use wasm_bindgen::prelude::wasm_bindgen; use wasm_bindgen::JsCast; -use wasm_bindgen::JsValue; use web_sys::CanvasRenderingContext2d; use web_sys::HtmlCanvasElement; use web_sys::ImageData; -use web_sys::OffscreenCanvas; use crate::error::SwResultExt; use crate::SoftBufferError; @@ -36,23 +33,12 @@ impl WebDisplayImpl { } } -#[wasm_bindgen] -extern "C" { - #[wasm_bindgen(js_name = OffscreenCanvasRenderingContext2D)] - pub type OffscreenCanvasRenderingContext2d; - - #[wasm_bindgen(catch, method, structural, js_class = "OffscreenCanvasRenderingContext2D", js_name = putImageData)] - fn put_image_data( - this: &OffscreenCanvasRenderingContext2d, - imagedata: &ImageData, - dx: f64, - dy: f64, - ) -> Result<(), JsValue>; -} - pub struct WebImpl { - /// The handle and context to the canvas that we're drawing to. - canvas: Canvas, + /// The handle to the canvas that we're drawing to. + canvas: HtmlCanvasElement, + + /// The 2D rendering context for the canvas. + ctx: CanvasRenderingContext2d, /// The buffer that we're drawing to. buffer: Vec, @@ -61,17 +47,6 @@ pub struct WebImpl { width: u32, } -pub enum Canvas { - Canvas { - canvas: HtmlCanvasElement, - ctx: CanvasRenderingContext2d, - }, - OffscreenCanvas { - canvas: OffscreenCanvas, - ctx: OffscreenCanvasRenderingContext2d, - }, -} - impl WebImpl { pub fn new(display: &WebDisplayImpl, handle: WebWindowHandle) -> Result { let canvas: HtmlCanvasElement = display @@ -90,20 +65,8 @@ impl WebImpl { let ctx = Self::resolve_ctx(canvas.get_context("2d").ok(), "CanvasRenderingContext2d")?; Ok(Self { - canvas: Canvas::Canvas { canvas, ctx }, - buffer: Vec::new(), - width: 0, - }) - } - - fn from_offscreen_canvas(canvas: OffscreenCanvas) -> Result { - let ctx = Self::resolve_ctx( - canvas.get_context("2d").ok(), - "OffscreenCanvasRenderingContext2d", - )?; - - Ok(Self { - canvas: Canvas::OffscreenCanvas { canvas, ctx }, + canvas, + ctx, buffer: Vec::new(), width: 0, }) @@ -150,9 +113,6 @@ impl WebImpl { pub trait SurfaceExtWeb: Sized { /// Creates a new instance of this struct, using the provided [`HtmlCanvasElement`]. fn from_canvas(canvas: HtmlCanvasElement) -> Result; - - /// Creates a new instance of this struct, using the provided [`HtmlCanvasElement`]. - fn from_offscreen_canvas(offscreen_canvas: OffscreenCanvas) -> Result; } impl SurfaceExtWeb for crate::Surface { @@ -164,38 +124,6 @@ impl SurfaceExtWeb for crate::Surface { _marker: PhantomData, }) } - - fn from_offscreen_canvas(offscreen_canvas: OffscreenCanvas) -> Result { - let imple = crate::SurfaceDispatch::Web(WebImpl::from_offscreen_canvas(offscreen_canvas)?); - - Ok(Self { - surface_impl: Box::new(imple), - _marker: PhantomData, - }) - } -} - -impl Canvas { - fn set_width(&self, width: u32) { - match self { - Self::Canvas { canvas, .. } => canvas.set_width(width), - Self::OffscreenCanvas { canvas, .. } => canvas.set_width(width), - } - } - - fn set_height(&self, height: u32) { - match self { - Self::Canvas { canvas, .. } => canvas.set_height(height), - Self::OffscreenCanvas { canvas, .. } => canvas.set_height(height), - } - } - - fn put_image_data(&self, imagedata: &ImageData, dx: f64, dy: f64) -> Result<(), JsValue> { - match self { - Self::Canvas { ctx, .. } => ctx.put_image_data(imagedata, dx, dy), - Self::OffscreenCanvas { ctx, .. } => ctx.put_image_data(imagedata, dx, dy), - } - } } pub struct BufferImpl<'a> { @@ -225,6 +153,8 @@ impl<'a> BufferImpl<'a> { #[cfg(target_feature = "atomics")] let result = { use js_sys::{Uint8Array, Uint8ClampedArray}; + use wasm_bindgen::prelude::wasm_bindgen; + use wasm_bindgen::JsValue; #[wasm_bindgen] extern "C" { @@ -249,10 +179,7 @@ impl<'a> BufferImpl<'a> { let image_data = result.unwrap(); // This can only throw an error if `data` is detached, which is impossible. - self.imp - .canvas - .put_image_data(&image_data, 0.0, 0.0) - .unwrap(); + self.imp.ctx.put_image_data(&image_data, 0.0, 0.0).unwrap(); Ok(()) } From d862add44c34f4e5c2fc39a7ae973486b757de96 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Mon, 8 May 2023 12:12:34 +0200 Subject: [PATCH 3/4] Document Wasm on docs.rs --- .github/workflows/ci.yml | 11 +++++++++++ Cargo.toml | 5 +++++ src/lib.rs | 1 + 3 files changed, 17 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 83568fc2..d8fed1d5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -118,3 +118,14 @@ jobs: !contains(matrix.platform.target, 'freebsd') && !contains(matrix.platform.target, 'netbsd') run: cargo clippy --all-targets --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES -- -Dwarnings + + - name: Lint with rustdoc + shell: bash + if: > + (matrix.rust_version == 'stable') && + !contains(matrix.platform.options, '--no-default-features') && + !((matrix.platform.os == 'ubuntu-latest') && contains(matrix.platform.target, 'i686')) && + !contains(matrix.platform.target, 'redox') && + !contains(matrix.platform.target, 'freebsd') && + !contains(matrix.platform.target, 'netbsd') + run: cargo doc --no-deps --target ${{ matrix.platform.target }} $OPTIONS --features $FEATURES --document-private-items diff --git a/Cargo.toml b/Cargo.toml index c6da900a..63d2f8c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -85,3 +85,8 @@ rayon = "1.5.1" members = [ "run-wasm", ] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] +targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] diff --git a/src/lib.rs b/src/lib.rs index 01927df7..b7064d3c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,7 @@ #![doc = include_str!("../README.md")] #![deny(unsafe_op_in_unsafe_fn)] #![warn(missing_docs)] +#![cfg_attr(docsrs, feature(doc_auto_cfg))] #[cfg(target_os = "macos")] #[macro_use] From ce20d555f724ee74337c561bc763408aa6be4776 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Mon, 8 May 2023 15:31:48 +0200 Subject: [PATCH 4/4] Address review --- Cargo.toml | 8 +++++++- src/web.rs | 26 +++++++++----------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 63d2f8c5..7ad9739c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,4 +89,10 @@ members = [ [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] -targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] +default-target = "x86_64-unknown-linux-gnu" +targets = [ + "x86_64-pc-windows-msvc", + "x86_64-apple-darwin", + "x86_64-unknown-linux-gnu", + "wasm32-unknown-unknown", +] diff --git a/src/web.rs b/src/web.rs index f7bf1abf..920b4437 100644 --- a/src/web.rs +++ b/src/web.rs @@ -2,7 +2,6 @@ #![allow(clippy::uninlined_format_args)] -use js_sys::Object; use raw_window_handle::WebWindowHandle; use wasm_bindgen::JsCast; use web_sys::CanvasRenderingContext2d; @@ -62,7 +61,15 @@ impl WebImpl { } fn from_canvas(canvas: HtmlCanvasElement) -> Result { - let ctx = Self::resolve_ctx(canvas.get_context("2d").ok(), "CanvasRenderingContext2d")?; + let ctx = canvas + .get_context("2d") + .ok() + .swbuf_err("Canvas already controlled using `OffscreenCanvas`")? + .swbuf_err( + "A canvas context other than `CanvasRenderingContext2d` was already created", + )? + .dyn_into() + .expect("`getContext(\"2d\") didn't return a `CanvasRenderingContext2d`"); Ok(Self { canvas, @@ -72,21 +79,6 @@ impl WebImpl { }) } - fn resolve_ctx( - result: Option>, - name: &str, - ) -> Result { - let ctx = result - .swbuf_err("Canvas already controlled using `OffscreenCanvas`")? - .swbuf_err( - "A canvas context other than `CanvasRenderingContext2d` was already created", - )? - .dyn_into() - .unwrap_or_else(|_| panic!("`getContext(\"2d\") didn't return a `{name}`")); - - Ok(ctx) - } - /// Resize the canvas to the given dimensions. pub(crate) fn resize( &mut self,