image/image_reader/
free_functions.rs

1use std::fs::File;
2use std::io::{BufRead, BufWriter, Seek};
3use std::path::Path;
4
5use crate::{codecs::*, ExtendedColorType, ImageReader};
6
7use crate::dynimage::DynamicImage;
8use crate::error::{ImageError, ImageFormatHint, ImageResult};
9use crate::error::{UnsupportedError, UnsupportedErrorKind};
10use crate::image::ImageFormat;
11#[allow(unused_imports)] // When no features are supported
12use crate::image::{ImageDecoder, ImageEncoder};
13
14/// Create a new image from a Reader.
15///
16/// Assumes the reader is already buffered. For optimal performance,
17/// consider wrapping the reader with a `BufReader::new()`.
18///
19/// Try [`ImageReader`] for more advanced uses.
20pub fn load<R: BufRead + Seek>(r: R, format: ImageFormat) -> ImageResult<DynamicImage> {
21    let mut reader = ImageReader::new(r);
22    reader.set_format(format);
23    reader.decode()
24}
25
26#[allow(unused_variables)]
27// Most variables when no features are supported
28pub(crate) fn save_buffer_impl(
29    path: &Path,
30    buf: &[u8],
31    width: u32,
32    height: u32,
33    color: ExtendedColorType,
34) -> ImageResult<()> {
35    let format = ImageFormat::from_path(path)?;
36    save_buffer_with_format_impl(path, buf, width, height, color, format)
37}
38
39#[allow(unused_variables)]
40// Most variables when no features are supported
41pub(crate) fn save_buffer_with_format_impl(
42    path: &Path,
43    buf: &[u8],
44    width: u32,
45    height: u32,
46    color: ExtendedColorType,
47    format: ImageFormat,
48) -> ImageResult<()> {
49    let buffered_file_write = &mut BufWriter::new(File::create(path)?); // always seekable
50    write_buffer_impl(buffered_file_write, buf, width, height, color, format)
51}
52
53#[allow(unused_variables)]
54// Most variables when no features are supported
55pub(crate) fn write_buffer_impl<W: std::io::Write + Seek>(
56    buffered_write: &mut W,
57    buf: &[u8],
58    width: u32,
59    height: u32,
60    color: ExtendedColorType,
61    format: ImageFormat,
62) -> ImageResult<()> {
63    match format {
64        #[cfg(feature = "png")]
65        ImageFormat::Png => {
66            png::PngEncoder::new(buffered_write).write_image(buf, width, height, color)
67        }
68        #[cfg(feature = "jpeg")]
69        ImageFormat::Jpeg => {
70            jpeg::JpegEncoder::new(buffered_write).write_image(buf, width, height, color)
71        }
72        #[cfg(feature = "pnm")]
73        ImageFormat::Pnm => {
74            pnm::PnmEncoder::new(buffered_write).write_image(buf, width, height, color)
75        }
76        #[cfg(feature = "gif")]
77        ImageFormat::Gif => gif::GifEncoder::new(buffered_write).encode(buf, width, height, color),
78        #[cfg(feature = "ico")]
79        ImageFormat::Ico => {
80            ico::IcoEncoder::new(buffered_write).write_image(buf, width, height, color)
81        }
82        #[cfg(feature = "bmp")]
83        ImageFormat::Bmp => {
84            bmp::BmpEncoder::new(buffered_write).write_image(buf, width, height, color)
85        }
86        #[cfg(feature = "ff")]
87        ImageFormat::Farbfeld => {
88            farbfeld::FarbfeldEncoder::new(buffered_write).write_image(buf, width, height, color)
89        }
90        #[cfg(feature = "tga")]
91        ImageFormat::Tga => {
92            tga::TgaEncoder::new(buffered_write).write_image(buf, width, height, color)
93        }
94        #[cfg(feature = "exr")]
95        ImageFormat::OpenExr => {
96            openexr::OpenExrEncoder::new(buffered_write).write_image(buf, width, height, color)
97        }
98        #[cfg(feature = "tiff")]
99        ImageFormat::Tiff => {
100            tiff::TiffEncoder::new(buffered_write).write_image(buf, width, height, color)
101        }
102        #[cfg(feature = "avif")]
103        ImageFormat::Avif => {
104            avif::AvifEncoder::new(buffered_write).write_image(buf, width, height, color)
105        }
106        #[cfg(feature = "qoi")]
107        ImageFormat::Qoi => {
108            qoi::QoiEncoder::new(buffered_write).write_image(buf, width, height, color)
109        }
110        #[cfg(feature = "webp")]
111        ImageFormat::WebP => {
112            webp::WebPEncoder::new_lossless(buffered_write).write_image(buf, width, height, color)
113        }
114        #[cfg(feature = "hdr")]
115        ImageFormat::Hdr => {
116            hdr::HdrEncoder::new(buffered_write).write_image(buf, width, height, color)
117        }
118        _ => Err(ImageError::Unsupported(
119            UnsupportedError::from_format_and_kind(
120                ImageFormatHint::Unknown,
121                UnsupportedErrorKind::Format(ImageFormatHint::Name(format!("{format:?}"))),
122            ),
123        )),
124    }
125}
126
127static MAGIC_BYTES: [(&[u8], ImageFormat); 25] = [
128    (b"\x89PNG\r\n\x1a\n", ImageFormat::Png),
129    (&[0xff, 0xd8, 0xff], ImageFormat::Jpeg),
130    (b"GIF89a", ImageFormat::Gif),
131    (b"GIF87a", ImageFormat::Gif),
132    (b"RIFF", ImageFormat::WebP), // TODO: better magic byte detection, see https://github.com/image-rs/image/issues/660
133    (b"MM\x00*", ImageFormat::Tiff),
134    (b"II*\x00", ImageFormat::Tiff),
135    (b"DDS ", ImageFormat::Dds),
136    (b"BM", ImageFormat::Bmp),
137    (&[0, 0, 1, 0], ImageFormat::Ico),
138    (b"#?RADIANCE", ImageFormat::Hdr),
139    (b"P1", ImageFormat::Pnm),
140    (b"P2", ImageFormat::Pnm),
141    (b"P3", ImageFormat::Pnm),
142    (b"P4", ImageFormat::Pnm),
143    (b"P5", ImageFormat::Pnm),
144    (b"P6", ImageFormat::Pnm),
145    (b"P7", ImageFormat::Pnm),
146    (b"farbfeld", ImageFormat::Farbfeld),
147    (b"\0\0\0 ftypavif", ImageFormat::Avif),
148    (b"\0\0\0\x1cftypavif", ImageFormat::Avif),
149    (&[0x76, 0x2f, 0x31, 0x01], ImageFormat::OpenExr), // = &exr::meta::magic_number::BYTES
150    (b"qoif", ImageFormat::Qoi),
151    (&[0x0a, 0x02], ImageFormat::Pcx),
152    (&[0x0a, 0x05], ImageFormat::Pcx),
153];
154
155/// Guess image format from memory block
156///
157/// Makes an educated guess about the image format based on the Magic Bytes at the beginning.
158/// TGA is not supported by this function.
159/// This is not to be trusted on the validity of the whole memory block
160pub fn guess_format(buffer: &[u8]) -> ImageResult<ImageFormat> {
161    match guess_format_impl(buffer) {
162        Some(format) => Ok(format),
163        None => Err(ImageError::Unsupported(ImageFormatHint::Unknown.into())),
164    }
165}
166
167pub(crate) fn guess_format_impl(buffer: &[u8]) -> Option<ImageFormat> {
168    for &(signature, format) in &MAGIC_BYTES {
169        if buffer.starts_with(signature) {
170            return Some(format);
171        }
172    }
173
174    None
175}