show_image/features/
image.rs

1//! Support for the [`image`][::image] crate.
2
3use std::ops::Deref;
4
5use crate::error::ImageDataError;
6use crate::Alpha;
7use crate::AsImageView;
8use crate::BoxImage;
9use crate::Image;
10use crate::ImageInfo;
11use crate::ImageView;
12use crate::PixelFormat;
13use crate::error::UnsupportedImageFormat;
14
15impl AsImageView for image::DynamicImage {
16	fn as_image_view(&self) -> Result<ImageView, ImageDataError> {
17		let info = dynamic_image_info(self)?;
18		let data = dynamic_image_as_bytes(self);
19		Ok(ImageView::new(info, data))
20	}
21}
22
23impl AsImageView for &'_ image::DynamicImage {
24	fn as_image_view(&self) -> Result<ImageView, ImageDataError> {
25		(*self).as_image_view()
26	}
27}
28
29impl From<image::DynamicImage> for Image {
30	fn from(other: image::DynamicImage) -> Self {
31		let info = match dynamic_image_info(&other) {
32			Ok(x) => x,
33			Err(e) => return Self::Invalid(e),
34		};
35		let data = dynamic_image_into_bytes(other);
36		BoxImage::new(info, data).into()
37	}
38}
39
40impl<P, Container> AsImageView for image::ImageBuffer<P, Container>
41where
42	P: image::Pixel<Subpixel = u8> + image::PixelWithColorType,
43	Container: Deref<Target = [u8]>,
44{
45	fn as_image_view(&self) -> Result<ImageView, ImageDataError> {
46		let info = info(self)?;
47		let data = as_bytes(self);
48		Ok(ImageView::new(info, data))
49	}
50}
51
52impl<P, Container> AsImageView for &'_ image::ImageBuffer<P, Container>
53where
54	P: image::Pixel<Subpixel = u8> + image::PixelWithColorType,
55	Container: Deref<Target = [u8]>,
56{
57	fn as_image_view(&self) -> Result<ImageView, ImageDataError> {
58		(*self).as_image_view()
59	}
60}
61
62impl<P, Container> From<image::ImageBuffer<P, Container>> for Image
63where
64	P: image::Pixel<Subpixel = u8> + image::PixelWithColorType,
65	Container: Deref<Target = [u8]>,
66{
67	fn from(other: image::ImageBuffer<P, Container>) -> Self {
68		let info = match info(&other) {
69			Ok(x) => x,
70			Err(e) => return Self::Invalid(e),
71		};
72		let data = into_bytes(other);
73		BoxImage::new(info, data).into()
74	}
75}
76
77/// Consume an [`image::ImageBuffer`] and return the pixel data as boxed slice.
78fn into_bytes<P, Container>(buffer: image::ImageBuffer<P, Container>) -> Box<[u8]>
79where
80	P: image::Pixel<Subpixel = u8> + image::PixelWithColorType,
81	Container: Deref<Target = [u8]>,
82{
83	// TODO: Specialize this for Vec<u8> to avoid copying when
84	// https://github.com/rust-lang/rust/issues/31844 lands in stable.
85	Box::from(buffer.into_raw().deref())
86}
87
88fn dynamic_image_into_bytes(image: image::DynamicImage) -> Box<[u8]> {
89	match image {
90		image::DynamicImage::ImageLuma8(x) => into_bytes(x),
91		image::DynamicImage::ImageLumaA8(x) => into_bytes(x),
92		image::DynamicImage::ImageLuma16(_) => panic!("unsupported pixel format: Luma16"),
93		image::DynamicImage::ImageLumaA16(_) => panic!("unsupported pixel format: LumaA16"),
94		image::DynamicImage::ImageRgb8(x) => into_bytes(x),
95		image::DynamicImage::ImageRgba8(x) => into_bytes(x),
96		image::DynamicImage::ImageRgb16(_) => panic!("unsupported pixel format: Rgb16"),
97		image::DynamicImage::ImageRgba16(_) => panic!("unsupported pixel format: Rgba16"),
98		image::DynamicImage::ImageRgb32F(_) => panic!("unsupported pixel format: Rgb32F"),
99		image::DynamicImage::ImageRgba32F(_) => panic!("unsupported pixel format: Rgba32F"),
100		x => panic!("unsupported pixel format: {:?}", x),
101	}
102}
103
104/// Get the pixel data of an [`image::ImageBuffer`] to as a byte slice.
105fn as_bytes<P, Container>(buffer: &image::ImageBuffer<P, Container>) -> &[u8]
106where
107	P: image::Pixel<Subpixel = u8> + image::PixelWithColorType,
108	Container: Deref<Target = [u8]>,
109{
110	buffer
111}
112
113fn dynamic_image_as_bytes(image: &image::DynamicImage) -> &[u8] {
114	match image {
115		image::DynamicImage::ImageLuma8(x) => as_bytes(x),
116		image::DynamicImage::ImageLumaA8(x) => as_bytes(x),
117		image::DynamicImage::ImageLuma16(_) => panic!("unsupported pixel format: Luma16"),
118		image::DynamicImage::ImageLumaA16(_) => panic!("unsupported pixel format: LumaA16"),
119		image::DynamicImage::ImageRgb8(x) => as_bytes(x),
120		image::DynamicImage::ImageRgba8(x) => as_bytes(x),
121		image::DynamicImage::ImageRgb16(_) => panic!("unsupported pixel format: Rgb16"),
122		image::DynamicImage::ImageRgba16(_) => panic!("unsupported pixel format: Rgba16"),
123		image::DynamicImage::ImageRgb32F(_) => panic!("unsupported pixel format: Rgb32F"),
124		image::DynamicImage::ImageRgba32F(_) => panic!("unsupported pixel format: Rgba32F"),
125		x => panic!("unsupported pixel format: {:?}", x),
126	}
127}
128
129/// Extract the [`ImageInfo`] from an [`image::ImageBuffer`].
130fn info<P, C>(image: &image::ImageBuffer<P, C>) -> Result<ImageInfo, ImageDataError>
131where
132	P: image::Pixel<Subpixel = u8> + image::PixelWithColorType,
133	C: std::ops::Deref<Target = [u8]>,
134{
135	Ok(ImageInfo {
136		pixel_format: pixel_format::<P>()?,
137		size: glam::UVec2::new(image.width(), image.height()),
138		stride: glam::UVec2::new(
139			image.sample_layout().width_stride as u32,
140			image.sample_layout().height_stride as u32,
141		),
142	})
143}
144
145fn dynamic_image_info(image: &image::DynamicImage) -> Result<ImageInfo, ImageDataError> {
146	match image {
147		image::DynamicImage::ImageLuma8(x) => info(x),
148		image::DynamicImage::ImageLumaA8(x) => info(x),
149		image::DynamicImage::ImageRgb8(x) => info(x),
150		image::DynamicImage::ImageRgba8(x) => info(x),
151		x => Err(UnsupportedImageFormat { format: format!("{:?}", x) }.into()),
152	}
153}
154
155/// Extract the PixelFormat from an [`image::Pixel`].
156fn pixel_format<P: image::PixelWithColorType>() -> Result<PixelFormat, ImageDataError> {
157	match P::COLOR_TYPE {
158		image::ExtendedColorType::L8 => Ok(PixelFormat::Mono8),
159		image::ExtendedColorType::La8 => Ok(PixelFormat::MonoAlpha8(Alpha::Unpremultiplied)),
160		image::ExtendedColorType::Rgb8 => Ok(PixelFormat::Rgb8),
161		image::ExtendedColorType::Rgba8 => Ok(PixelFormat::Rgba8(Alpha::Unpremultiplied)),
162		x => Err(UnsupportedImageFormat { format: format!("{:?}", x) }.into()),
163	}
164}