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)] use crate::image::{ImageDecoder, ImageEncoder};
14
15pub 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)]
28pub(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)]
41pub(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)?); write_buffer_impl(buffered_file_write, buf, width, height, color, format)
52}
53
54#[allow(unused_variables)]
55pub(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), (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
158pub 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}