use std::borrow::Borrow;
use std::marker;
use std::mem;
use wasm_bindgen::JsCast;
use web_sys::{window, CanvasRenderingContext2d, HtmlCanvasElement, HtmlImageElement};
pub struct Image2DSource<D, T> {
pub(crate) internal: Image2DSourceInternal<D>,
_marker: marker::PhantomData<[T]>,
}
pub(crate) enum Image2DSourceInternal<D> {
PixelData {
data: D,
row_length: u32,
image_height: u32,
alignment: Alignment,
},
}
impl<D, T> Image2DSource<D, T>
where
D: Borrow<[T]>,
{
pub fn from_pixels(pixels: D, width: u32, height: u32) -> Result<Self, FromPixelsError> {
let len = pixels.borrow().len();
let expected_len = width * height;
if len < expected_len as usize {
return Err(FromPixelsError::NotEnoughPixels(len, expected_len));
}
let alignment = match mem::align_of::<T>() {
1 => Alignment::Byte,
2 => Alignment::Byte2,
4 => Alignment::Byte4,
8 => Alignment::Byte8,
a => return Err(FromPixelsError::UnsupportedAlignment(a)),
};
Ok(Image2DSource {
internal: Image2DSourceInternal::PixelData {
data: pixels,
row_length: width,
image_height: height,
alignment,
},
_marker: marker::PhantomData,
})
}
}
impl Image2DSource<Vec<[u8; 4]>, [u8; 4]> {
pub fn from_image_element(image_element: &HtmlImageElement) -> Self {
if !image_element.complete() {
panic!("Incomplete image.");
}
let document = window().unwrap().document().unwrap();
let width = image_element.natural_width();
let height = image_element.natural_height();
let canvas = document
.create_element("canvas")
.unwrap()
.dyn_into::<HtmlCanvasElement>()
.unwrap();
canvas.set_width(width);
canvas.set_height(height);
let context = canvas
.get_context("2d")
.unwrap()
.unwrap()
.dyn_into::<CanvasRenderingContext2d>()
.unwrap();
context
.draw_image_with_html_image_element(&image_element, 0.0, 0.0)
.unwrap();
let mut image_data = context
.get_image_data(0.0, 0.0, width as f64, height as f64)
.unwrap()
.data();
let len = image_data.len();
let capacity = image_data.capacity();
let ptr = image_data.as_mut_ptr();
mem::forget(image_data);
let pixels = unsafe { Vec::from_raw_parts(mem::transmute(ptr), len / 4, capacity / 4) };
Image2DSource {
internal: Image2DSourceInternal::PixelData {
data: pixels,
row_length: width,
image_height: height,
alignment: Alignment::Byte4,
},
_marker: marker::PhantomData,
}
}
}
pub struct LayeredImageSource<D, T> {
pub(crate) internal: LayeredImageSourceInternal<D>,
_marker: marker::PhantomData<[T]>,
}
pub(crate) enum LayeredImageSourceInternal<D> {
PixelData {
data: D,
row_length: u32,
image_height: u32,
image_count: u32,
alignment: Alignment,
},
}
impl<D, T> LayeredImageSource<D, T>
where
D: Borrow<[T]>,
{
pub fn from_pixels(
pixels: D,
width: u32,
height: u32,
depth: u32,
) -> Result<Self, FromPixelsError> {
let len = pixels.borrow().len();
let expected_len = width * height * depth;
if len < expected_len as usize {
return Err(FromPixelsError::NotEnoughPixels(len, expected_len));
}
let alignment = match mem::align_of::<T>() {
1 => Alignment::Byte,
2 => Alignment::Byte2,
4 => Alignment::Byte4,
8 => Alignment::Byte8,
a => return Err(FromPixelsError::UnsupportedAlignment(a)),
};
Ok(LayeredImageSource {
internal: LayeredImageSourceInternal::PixelData {
data: pixels,
row_length: width,
image_height: height,
image_count: depth,
alignment,
},
_marker: marker::PhantomData,
})
}
}
#[derive(Debug)]
pub enum FromPixelsError {
NotEnoughPixels(usize, u32),
UnsupportedAlignment(usize),
}
#[derive(Clone, Copy, PartialEq, Debug)]
pub(crate) enum Alignment {
Byte,
Byte2,
Byte4,
Byte8,
}
impl Into<i32> for Alignment {
fn into(self) -> i32 {
match self {
Alignment::Byte => 1,
Alignment::Byte2 => 2,
Alignment::Byte4 => 4,
Alignment::Byte8 => 8,
}
}
}