#[cfg(feature = "image")]
use std::error::Error;
#[cfg(feature = "image")]
use std::path::Path;
use std::sync::Arc;
use crate::kurbo::Size;
use crate::util::unpremul;
use crate::{Color, ImageFormat, RenderContext};
pub trait Image: Clone {
fn size(&self) -> Size;
}
#[derive(Clone)]
pub struct ImageBuf {
pixels: Arc<[u8]>,
width: usize,
height: usize,
format: ImageFormat,
}
impl ImageBuf {
pub fn empty() -> Self {
ImageBuf {
pixels: Arc::new([]),
width: 0,
height: 0,
format: ImageFormat::RgbaSeparate,
}
}
pub fn from_raw(
pixels: impl Into<Arc<[u8]>>,
format: ImageFormat,
width: usize,
height: usize,
) -> ImageBuf {
let pixels = pixels.into();
assert_eq!(pixels.len(), width * height * format.bytes_per_pixel());
ImageBuf {
pixels,
width,
height,
format,
}
}
pub fn raw_pixels(&self) -> &[u8] {
&self.pixels[..]
}
pub fn raw_pixels_shared(&self) -> Arc<[u8]> {
Arc::clone(&self.pixels)
}
pub fn format(&self) -> ImageFormat {
self.format
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn size(&self) -> Size {
Size::new(self.width() as f64, self.height() as f64)
}
pub fn pixel_colors(&self) -> impl Iterator<Item = impl Iterator<Item = Color> + '_> {
let format = self.format;
let bytes_per_pixel = format.bytes_per_pixel();
self.pixels
.chunks_exact(self.width * bytes_per_pixel)
.map(move |row| {
row.chunks_exact(bytes_per_pixel)
.map(move |p| match format {
ImageFormat::Grayscale => Color::grey8(p[0]),
ImageFormat::Rgb => Color::rgb8(p[0], p[1], p[2]),
ImageFormat::RgbaSeparate => Color::rgba8(p[0], p[1], p[2], p[3]),
ImageFormat::RgbaPremul => {
let a = p[3];
Color::rgba8(unpremul(p[0], a), unpremul(p[1], a), unpremul(p[2], a), a)
}
})
})
}
pub fn to_image<Ctx: RenderContext>(&self, ctx: &mut Ctx) -> Ctx::Image {
ctx.make_image(self.width(), self.height(), &self.pixels, self.format)
.unwrap()
}
pub fn ptr_eq(&self, other: &ImageBuf) -> bool {
Arc::ptr_eq(&self.raw_pixels_shared(), &other.raw_pixels_shared())
}
}
impl Default for ImageBuf {
fn default() -> Self {
ImageBuf::empty()
}
}
#[cfg(feature = "image")]
#[cfg_attr(docsrs, doc(cfg(feature = "image")))]
impl ImageBuf {
pub fn from_dynamic_image(image_data: image::DynamicImage) -> ImageBuf {
fn has_alpha_channel(color: image::ColorType) -> bool {
use image::ColorType::*;
matches!(color, La8 | Rgba8 | La16 | Rgba16)
}
if has_alpha_channel(image_data.color()) {
ImageBuf::from_dynamic_image_with_alpha(image_data)
} else {
ImageBuf::from_dynamic_image_without_alpha(image_data)
}
}
pub fn from_dynamic_image_with_alpha(image_data: image::DynamicImage) -> ImageBuf {
let rgba_image = image_data.to_rgba8();
let sizeofimage = rgba_image.dimensions();
ImageBuf::from_raw(
rgba_image.to_vec(),
ImageFormat::RgbaSeparate,
sizeofimage.0 as usize,
sizeofimage.1 as usize,
)
}
pub fn from_dynamic_image_without_alpha(image_data: image::DynamicImage) -> ImageBuf {
let rgb_image = image_data.to_rgb8();
let sizeofimage = rgb_image.dimensions();
ImageBuf::from_raw(
rgb_image.to_vec(),
ImageFormat::Rgb,
sizeofimage.0 as usize,
sizeofimage.1 as usize,
)
}
pub fn from_data(raw_image: &[u8]) -> Result<ImageBuf, Box<dyn Error + Send + Sync>> {
let image_data = image::load_from_memory(raw_image)?;
Ok(ImageBuf::from_dynamic_image(image_data))
}
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<ImageBuf, Box<dyn Error + Send + Sync>> {
let image_data = image::open(path)?;
Ok(ImageBuf::from_dynamic_image(image_data))
}
}
impl std::fmt::Debug for ImageBuf {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
f.debug_struct("ImageBuf")
.field("size", &self.pixels.len())
.field("width", &self.width)
.field("height", &self.height)
.field("format", &format_args!("{:?}", self.format))
.finish()
}
}