use super::{WebDocument, WebElement};
use std::io;
use wasm_bindgen::{Clamped, JsCast};
use web_sys::{CanvasRenderingContext2d, HtmlCanvasElement, ImageData};
pub use crate::canvas_validation::{CanvasSize, RgbaFrame};
pub struct WebCanvas {
canvas: HtmlCanvasElement,
context: CanvasRenderingContext2d,
}
impl WebCanvas {
pub fn from_element(element: &WebElement) -> io::Result<Self> {
let canvas = element
.element
.clone()
.dyn_into::<HtmlCanvasElement>()
.map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "DOM element is not a canvas")
})?;
let context = canvas
.get_context("2d")
.map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "canvas context lookup failed")
})?
.ok_or_else(|| {
io::Error::new(io::ErrorKind::InvalidInput, "canvas has no 2-D context")
})?
.dyn_into::<CanvasRenderingContext2d>()
.map_err(|_| {
io::Error::new(io::ErrorKind::InvalidInput, "canvas context is not 2-D")
})?;
Ok(Self { canvas, context })
}
#[must_use]
pub fn id(&self) -> String {
self.canvas.id()
}
pub fn present(&self, frame: RgbaFrame<'_>) -> io::Result<()> {
let size = frame.size();
if !size.matches_dimensions(self.canvas.width(), self.canvas.height()) {
self.canvas.set_width(size.width());
self.canvas.set_height(size.height());
}
let image = ImageData::new_with_u8_clamped_array_and_sh(
Clamped(frame.pixels()),
size.width(),
size.height(),
)
.map_err(|_| io::Error::new(io::ErrorKind::InvalidInput, "browser rejected RGBA image"))?;
self.context.put_image_data(&image, 0.0, 0.0).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"browser rejected canvas upload",
)
})
}
}
impl WebDocument {
pub fn canvas_by_id(&self, id: &str) -> io::Result<WebCanvas> {
let element = self.get_element_by_id(id).ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
"canvas element identifier is absent",
)
})?;
WebCanvas::from_element(&element)
}
}