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