1 use std::fs::File;
2 use std::io::{BufRead, BufReader, BufWriter, Seek};
3 use std::path::Path;
4 use std::u32;
5
6 #[cfg(feature = "bmp")]
7 use crate::codecs::bmp;
8 #[cfg(feature = "gif")]
9 use crate::codecs::gif;
10 #[cfg(feature = "hdr")]
11 use crate::codecs::hdr;
12 #[cfg(feature = "ico")]
13 use crate::codecs::ico;
14 #[cfg(feature = "jpeg")]
15 use crate::codecs::jpeg;
16 #[cfg(feature = "png")]
17 use crate::codecs::png;
18 #[cfg(feature = "pnm")]
19 use crate::codecs::pnm;
20 #[cfg(feature = "tga")]
21 use crate::codecs::tga;
22 #[cfg(feature = "dds")]
23 use crate::codecs::dds;
24 #[cfg(feature = "tiff")]
25 use crate::codecs::tiff;
26 #[cfg(feature = "webp")]
27 use crate::codecs::webp;
28 #[cfg(feature = "farbfeld")]
29 use crate::codecs::farbfeld;
30 #[cfg(any(feature = "avif-encoder", feature = "avif-decoder"))]
31 use crate::codecs::avif;
32
33 use crate::color;
34 use crate::image;
35 use crate::dynimage::DynamicImage;
36 use crate::error::{ImageError, ImageFormatHint, ImageResult};
37 use crate::image::ImageFormat;
38 #[allow(unused_imports)] // When no features are supported
39 use crate::image::{ImageDecoder, ImageEncoder};
40
open_impl(path: &Path) -> ImageResult<DynamicImage>41 pub(crate) fn open_impl(path: &Path) -> ImageResult<DynamicImage> {
42 let fin = match File::open(path) {
43 Ok(f) => f,
44 Err(err) => return Err(ImageError::IoError(err)),
45 };
46 let fin = BufReader::new(fin);
47
48 load(fin, ImageFormat::from_path(path)?)
49 }
50
51 /// Create a new image from a Reader
52 ///
53 /// Try [`io::Reader`] for more advanced uses.
54 ///
55 /// [`io::Reader`]: io/struct.Reader.html
56 #[allow(unused_variables)]
57 // r is unused if no features are supported.
load<R: BufRead + Seek>(r: R, format: ImageFormat) -> ImageResult<DynamicImage>58 pub fn load<R: BufRead + Seek>(r: R, format: ImageFormat) -> ImageResult<DynamicImage> {
59 #[allow(unreachable_patterns)]
60 // Default is unreachable if all features are supported.
61 match format {
62 #[cfg(feature = "avif-decoder")]
63 image::ImageFormat::Avif => DynamicImage::from_decoder(avif::AvifDecoder::new(r)?),
64 #[cfg(feature = "png")]
65 image::ImageFormat::Png => DynamicImage::from_decoder(png::PngDecoder::new(r)?),
66 #[cfg(feature = "gif")]
67 image::ImageFormat::Gif => DynamicImage::from_decoder(gif::GifDecoder::new(r)?),
68 #[cfg(feature = "jpeg")]
69 image::ImageFormat::Jpeg => DynamicImage::from_decoder(jpeg::JpegDecoder::new(r)?),
70 #[cfg(feature = "webp")]
71 image::ImageFormat::WebP => DynamicImage::from_decoder(webp::WebPDecoder::new(r)?),
72 #[cfg(feature = "tiff")]
73 image::ImageFormat::Tiff => DynamicImage::from_decoder(tiff::TiffDecoder::new(r)?),
74 #[cfg(feature = "tga")]
75 image::ImageFormat::Tga => DynamicImage::from_decoder(tga::TgaDecoder::new(r)?),
76 #[cfg(feature = "dds")]
77 image::ImageFormat::Dds => DynamicImage::from_decoder(dds::DdsDecoder::new(r)?),
78 #[cfg(feature = "bmp")]
79 image::ImageFormat::Bmp => DynamicImage::from_decoder(bmp::BmpDecoder::new(r)?),
80 #[cfg(feature = "ico")]
81 image::ImageFormat::Ico => DynamicImage::from_decoder(ico::IcoDecoder::new(r)?),
82 #[cfg(feature = "hdr")]
83 image::ImageFormat::Hdr => DynamicImage::from_decoder(hdr::HdrAdapter::new(BufReader::new(r))?),
84 #[cfg(feature = "pnm")]
85 image::ImageFormat::Pnm => DynamicImage::from_decoder(pnm::PnmDecoder::new(BufReader::new(r))?),
86 #[cfg(feature = "farbfeld")]
87 image::ImageFormat::Farbfeld => DynamicImage::from_decoder(farbfeld::FarbfeldDecoder::new(r)?),
88 _ => Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
89 }
90 }
91
image_dimensions_impl(path: &Path) -> ImageResult<(u32, u32)>92 pub(crate) fn image_dimensions_impl(path: &Path) -> ImageResult<(u32, u32)> {
93 let format = image::ImageFormat::from_path(path)?;
94
95 let fin = File::open(path)?;
96 let fin = BufReader::new(fin);
97
98 image_dimensions_with_format_impl(fin, format)
99 }
100
101 #[allow(unused_variables)]
102 // fin is unused if no features are supported.
image_dimensions_with_format_impl<R: BufRead + Seek>(fin: R, format: ImageFormat) -> ImageResult<(u32, u32)>103 pub(crate) fn image_dimensions_with_format_impl<R: BufRead + Seek>(fin: R, format: ImageFormat)
104 -> ImageResult<(u32, u32)>
105 {
106 #[allow(unreachable_patterns,unreachable_code)]
107 // Default is unreachable if all features are supported.
108 // Code after the match is unreachable if none are.
109 Ok(match format {
110 #[cfg(feature = "avif-decoder")]
111 image::ImageFormat::Avif => avif::AvifDecoder::new(fin)?.dimensions(),
112 #[cfg(feature = "jpeg")]
113 image::ImageFormat::Jpeg => jpeg::JpegDecoder::new(fin)?.dimensions(),
114 #[cfg(feature = "png")]
115 image::ImageFormat::Png => png::PngDecoder::new(fin)?.dimensions(),
116 #[cfg(feature = "gif")]
117 image::ImageFormat::Gif => gif::GifDecoder::new(fin)?.dimensions(),
118 #[cfg(feature = "webp")]
119 image::ImageFormat::WebP => webp::WebPDecoder::new(fin)?.dimensions(),
120 #[cfg(feature = "tiff")]
121 image::ImageFormat::Tiff => tiff::TiffDecoder::new(fin)?.dimensions(),
122 #[cfg(feature = "tga")]
123 image::ImageFormat::Tga => tga::TgaDecoder::new(fin)?.dimensions(),
124 #[cfg(feature = "dds")]
125 image::ImageFormat::Dds => dds::DdsDecoder::new(fin)?.dimensions(),
126 #[cfg(feature = "bmp")]
127 image::ImageFormat::Bmp => bmp::BmpDecoder::new(fin)?.dimensions(),
128 #[cfg(feature = "ico")]
129 image::ImageFormat::Ico => ico::IcoDecoder::new(fin)?.dimensions(),
130 #[cfg(feature = "hdr")]
131 image::ImageFormat::Hdr => hdr::HdrAdapter::new(fin)?.dimensions(),
132 #[cfg(feature = "pnm")]
133 image::ImageFormat::Pnm => {
134 pnm::PnmDecoder::new(fin)?.dimensions()
135 }
136 format => return Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
137 })
138 }
139
140 #[allow(unused_variables)]
141 // Most variables when no features are supported
save_buffer_impl( path: &Path, buf: &[u8], width: u32, height: u32, color: color::ColorType, ) -> ImageResult<()>142 pub(crate) fn save_buffer_impl(
143 path: &Path,
144 buf: &[u8],
145 width: u32,
146 height: u32,
147 color: color::ColorType,
148 ) -> ImageResult<()> {
149 let fout = &mut BufWriter::new(File::create(path)?);
150 let format = ImageFormat::from_path(path)?;
151 save_buffer_with_format_impl(path, buf, width, height, color, format)
152 }
153
154 #[allow(unused_variables)]
155 // Most variables when no features are supported
save_buffer_with_format_impl( path: &Path, buf: &[u8], width: u32, height: u32, color: color::ColorType, format: ImageFormat, ) -> ImageResult<()>156 pub(crate) fn save_buffer_with_format_impl(
157 path: &Path,
158 buf: &[u8],
159 width: u32,
160 height: u32,
161 color: color::ColorType,
162 format: ImageFormat,
163 ) -> ImageResult<()> {
164 let fout = &mut BufWriter::new(File::create(path)?);
165
166 match format {
167 #[cfg(feature = "gif")]
168 image::ImageFormat::Gif => gif::GifEncoder::new(fout).encode(buf, width, height, color),
169 #[cfg(feature = "ico")]
170 image::ImageFormat::Ico => ico::IcoEncoder::new(fout).write_image(buf, width, height, color),
171 #[cfg(feature = "jpeg")]
172 image::ImageFormat::Jpeg => jpeg::JpegEncoder::new(fout).write_image(buf, width, height, color),
173 #[cfg(feature = "png")]
174 image::ImageFormat::Png => png::PngEncoder::new(fout).write_image(buf, width, height, color),
175 #[cfg(feature = "pnm")]
176 image::ImageFormat::Pnm => {
177 let ext = path.extension()
178 .and_then(|s| s.to_str())
179 .map_or("".to_string(), |s| s.to_ascii_lowercase());
180 match &*ext {
181 "pbm" => pnm::PnmEncoder::new(fout)
182 .with_subtype(pnm::PNMSubtype::Bitmap(pnm::SampleEncoding::Binary))
183 .write_image(buf, width, height, color),
184 "pgm" => pnm::PnmEncoder::new(fout)
185 .with_subtype(pnm::PNMSubtype::Graymap(pnm::SampleEncoding::Binary))
186 .write_image(buf, width, height, color),
187 "ppm" => pnm::PnmEncoder::new(fout)
188 .with_subtype(pnm::PNMSubtype::Pixmap(pnm::SampleEncoding::Binary))
189 .write_image(buf, width, height, color),
190 "pam" => pnm::PnmEncoder::new(fout).write_image(buf, width, height, color),
191 _ => Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())), // Unsupported Pnm subtype.
192 }
193 },
194 #[cfg(feature = "farbfeld")]
195 image::ImageFormat::Farbfeld => farbfeld::FarbfeldEncoder::new(fout).write_image(buf, width, height, color),
196 #[cfg(feature = "avif-encoder")]
197 image::ImageFormat::Avif => avif::AvifEncoder::new(fout).write_image(buf, width, height, color),
198 // #[cfg(feature = "hdr")]
199 // image::ImageFormat::Hdr => hdr::HdrEncoder::new(fout).encode(&[Rgb<f32>], width, height), // usize
200 #[cfg(feature = "bmp")]
201 image::ImageFormat::Bmp => bmp::BmpEncoder::new(fout).write_image(buf, width, height, color),
202 #[cfg(feature = "tiff")]
203 image::ImageFormat::Tiff => tiff::TiffEncoder::new(fout)
204 .write_image(buf, width, height, color),
205 #[cfg(feature = "tga")]
206 image::ImageFormat::Tga => tga::TgaEncoder::new(fout).write_image(buf, width, height, color),
207 format => Err(ImageError::Unsupported(ImageFormatHint::Exact(format).into())),
208 }
209 }
210
211 static MAGIC_BYTES: [(&[u8], ImageFormat); 20] = [
212 (b"\x89PNG\r\n\x1a\n", ImageFormat::Png),
213 (&[0xff, 0xd8, 0xff], ImageFormat::Jpeg),
214 (b"GIF89a", ImageFormat::Gif),
215 (b"GIF87a", ImageFormat::Gif),
216 (b"RIFF", ImageFormat::WebP), // TODO: better magic byte detection, see https://github.com/image-rs/image/issues/660
217 (b"MM\x00*", ImageFormat::Tiff),
218 (b"II*\x00", ImageFormat::Tiff),
219 (b"DDS ", ImageFormat::Dds),
220 (b"BM", ImageFormat::Bmp),
221 (&[0, 0, 1, 0], ImageFormat::Ico),
222 (b"#?RADIANCE", ImageFormat::Hdr),
223 (b"P1", ImageFormat::Pnm),
224 (b"P2", ImageFormat::Pnm),
225 (b"P3", ImageFormat::Pnm),
226 (b"P4", ImageFormat::Pnm),
227 (b"P5", ImageFormat::Pnm),
228 (b"P6", ImageFormat::Pnm),
229 (b"P7", ImageFormat::Pnm),
230 (b"farbfeld", ImageFormat::Farbfeld),
231 (b"\0\0\0 ftypavif", ImageFormat::Avif),
232 ];
233
234 /// Guess image format from memory block
235 ///
236 /// Makes an educated guess about the image format based on the Magic Bytes at the beginning.
237 /// TGA is not supported by this function.
238 /// This is not to be trusted on the validity of the whole memory block
guess_format(buffer: &[u8]) -> ImageResult<ImageFormat>239 pub fn guess_format(buffer: &[u8]) -> ImageResult<ImageFormat> {
240 match guess_format_impl(buffer) {
241 Some(format) => Ok(format),
242 None => Err(ImageError::Unsupported(ImageFormatHint::Unknown.into())),
243 }
244 }
245
guess_format_impl(buffer: &[u8]) -> Option<ImageFormat>246 pub(crate) fn guess_format_impl(buffer: &[u8]) -> Option<ImageFormat> {
247 for &(signature, format) in &MAGIC_BYTES {
248 if buffer.starts_with(signature) {
249 return Some(format);
250 }
251 }
252
253 None
254 }
255