image/image_reader/
free_functions.rs

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