Skip to main content

pixels/
decoding.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::io::Cursor;
6use std::sync::Arc;
7use std::time::Duration;
8use std::{cmp, fmt, vec};
9
10use image::codecs::{bmp, gif, ico, jpeg, png, webp};
11use image::error::ImageFormatHint;
12use image::metadata::LoopCount;
13use image::{
14    AnimationDecoder, DynamicImage, ImageDecoder, ImageError, ImageFormat, ImageResult, Limits,
15};
16use log::debug;
17
18use crate::{
19    CorsStatus, ImageFrame, ImageMetadata, PixelFormat, RasterImage, Repeat,
20    rgba8_premultiply_inplace,
21};
22
23enum GenericImageDecoder<'a> {
24    Apng(Box<png::ApngDecoder<Cursor<&'a [u8]>>>),
25    Png(Box<png::PngDecoder<Cursor<&'a [u8]>>>),
26    Gif(Box<gif::GifDecoder<Cursor<&'a [u8]>>>),
27    Webp(Box<webp::WebPDecoder<Cursor<&'a [u8]>>>),
28    Jpeg(Box<jpeg::JpegDecoder<Cursor<&'a [u8]>>>),
29    Bmp(Box<bmp::BmpDecoder<Cursor<&'a [u8]>>>),
30    Ico(Box<ico::IcoDecoder<Cursor<&'a [u8]>>>),
31}
32
33impl<'a> std::fmt::Debug for GenericImageDecoder<'a> {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        match self {
36            Self::Apng(_) => f.debug_tuple("Apng").finish(),
37            Self::Png(_) => f.debug_tuple("Png").finish(),
38            Self::Gif(_) => f.debug_tuple("Gif").finish(),
39            Self::Webp(_) => f.debug_tuple("Webp").finish(),
40            Self::Jpeg(_) => f.debug_tuple("Jpeg").finish(),
41            Self::Bmp(_) => f.debug_tuple("Bmp").finish(),
42            Self::Ico(_) => f.debug_tuple("Ico").finish(),
43        }
44    }
45}
46
47/// Notice that we implement methods that are implemented by default. However, these methods are necessary as the default implementation
48/// will return not correct defaults.
49impl<'a> image::ImageDecoder for GenericImageDecoder<'a> {
50    fn dimensions(&self) -> (u32, u32) {
51        match self {
52            GenericImageDecoder::Apng(_) => {
53                unreachable!("Animated image should never go into non-animated values")
54            },
55            GenericImageDecoder::Png(d) => d.dimensions(),
56            GenericImageDecoder::Gif(d) => d.dimensions(),
57            GenericImageDecoder::Webp(d) => d.dimensions(),
58            GenericImageDecoder::Jpeg(d) => d.dimensions(),
59            GenericImageDecoder::Bmp(d) => d.dimensions(),
60            GenericImageDecoder::Ico(d) => d.dimensions(),
61        }
62    }
63
64    fn color_type(&self) -> image::ColorType {
65        match self {
66            GenericImageDecoder::Apng(_) => {
67                unreachable!("Animated image should never go into non-animated values")
68            },
69            GenericImageDecoder::Png(d) => d.color_type(),
70            GenericImageDecoder::Gif(d) => d.color_type(),
71            GenericImageDecoder::Webp(d) => d.color_type(),
72            GenericImageDecoder::Jpeg(d) => d.color_type(),
73            GenericImageDecoder::Bmp(d) => d.color_type(),
74            GenericImageDecoder::Ico(d) => d.color_type(),
75        }
76    }
77
78    fn read_image(self, buf: &mut [u8]) -> ImageResult<()>
79    where
80        Self: Sized,
81    {
82        match self {
83            GenericImageDecoder::Apng(_) => {
84                unreachable!("Animated image should never go into non-animated values")
85            },
86            GenericImageDecoder::Png(d) => d.read_image(buf),
87            GenericImageDecoder::Gif(d) => d.read_image(buf),
88            GenericImageDecoder::Webp(d) => d.read_image(buf),
89            GenericImageDecoder::Jpeg(d) => d.read_image(buf),
90            GenericImageDecoder::Bmp(d) => d.read_image(buf),
91            GenericImageDecoder::Ico(d) => d.read_image(buf),
92        }
93    }
94
95    fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
96        match *self {
97            GenericImageDecoder::Apng(_) => {
98                unreachable!("Animated image should never go into non-animated values")
99            },
100            GenericImageDecoder::Png(d) => d.read_image_boxed(buf),
101            GenericImageDecoder::Gif(d) => d.read_image_boxed(buf),
102            GenericImageDecoder::Webp(d) => d.read_image_boxed(buf),
103            GenericImageDecoder::Jpeg(d) => d.read_image_boxed(buf),
104            GenericImageDecoder::Bmp(d) => d.read_image_boxed(buf),
105            GenericImageDecoder::Ico(d) => d.read_image_boxed(buf),
106        }
107    }
108
109    fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
110        match self {
111            GenericImageDecoder::Apng(_) => {
112                unreachable!("Animated image should never go into non-animated values")
113            },
114            GenericImageDecoder::Png(d) => d.icc_profile(),
115            GenericImageDecoder::Gif(d) => d.icc_profile(),
116            GenericImageDecoder::Webp(d) => d.icc_profile(),
117            GenericImageDecoder::Jpeg(d) => d.icc_profile(),
118            GenericImageDecoder::Bmp(d) => d.icc_profile(),
119            GenericImageDecoder::Ico(d) => d.icc_profile(),
120        }
121    }
122
123    fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
124        match self {
125            GenericImageDecoder::Apng(_) => {
126                unreachable!("Animated image should never go into non-animated values")
127            },
128            GenericImageDecoder::Png(d) => d.exif_metadata(),
129            GenericImageDecoder::Gif(d) => d.exif_metadata(),
130            GenericImageDecoder::Webp(d) => d.exif_metadata(),
131            GenericImageDecoder::Jpeg(d) => d.exif_metadata(),
132            GenericImageDecoder::Bmp(d) => d.exif_metadata(),
133            GenericImageDecoder::Ico(d) => d.exif_metadata(),
134        }
135    }
136
137    fn xmp_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
138        match self {
139            GenericImageDecoder::Apng(_) => {
140                unreachable!("Animated image should never go into non-animated values")
141            },
142            GenericImageDecoder::Png(d) => d.xmp_metadata(),
143            GenericImageDecoder::Gif(d) => d.xmp_metadata(),
144            GenericImageDecoder::Webp(d) => d.xmp_metadata(),
145            GenericImageDecoder::Jpeg(d) => d.xmp_metadata(),
146            GenericImageDecoder::Bmp(d) => d.xmp_metadata(),
147            GenericImageDecoder::Ico(d) => d.xmp_metadata(),
148        }
149    }
150
151    fn iptc_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
152        match self {
153            GenericImageDecoder::Apng(_) => {
154                unreachable!("Animated image should never go into non-animated values")
155            },
156            GenericImageDecoder::Png(d) => d.iptc_metadata(),
157            GenericImageDecoder::Gif(d) => d.iptc_metadata(),
158            GenericImageDecoder::Webp(d) => d.iptc_metadata(),
159            GenericImageDecoder::Jpeg(d) => d.iptc_metadata(),
160            GenericImageDecoder::Bmp(d) => d.iptc_metadata(),
161            GenericImageDecoder::Ico(d) => d.iptc_metadata(),
162        }
163    }
164}
165
166/// See documentation on impl `image::ImageDecoder`
167impl<'a> AnimationDecoder<'a> for GenericImageDecoder<'a> {
168    fn into_frames(self) -> image::Frames<'a> {
169        match self {
170            GenericImageDecoder::Apng(decoder) => decoder.into_frames(),
171            GenericImageDecoder::Gif(decoder) => decoder.into_frames(),
172            GenericImageDecoder::Webp(decoder) => decoder.into_frames(),
173            _ => unreachable!("Should never decode these images with animated decoder"),
174        }
175    }
176
177    fn loop_count(&self) -> LoopCount {
178        match self {
179            GenericImageDecoder::Apng(decoder) => decoder.loop_count(),
180            GenericImageDecoder::Gif(decoder) => decoder.loop_count(),
181            GenericImageDecoder::Webp(decoder) => decoder.loop_count(),
182            _ => unreachable!("Should never decode these images with animated decoder"),
183        }
184    }
185}
186
187#[derive(Debug)]
188/// Servo Default Image decoder using image-rs for decoding.
189pub(crate) struct DefaultImageDecoder<'a> {
190    decoder: GenericImageDecoder<'a>,
191}
192
193/// Main Image decoder trait.
194pub(crate) trait ServoImageDecoder<'a>: Sized + std::fmt::Debug {
195    /// Create a decoder for a `format` from a `buffer`.
196    fn make_decoder(format: ImageFormat, buffer: &'a [u8]) -> ImageResult<Self>;
197    fn is_animated(&self) -> bool;
198    /// Return the created decoder in `impl ImageDecoder`
199    fn decoder(self) -> impl ImageDecoder;
200    /// Return the created decoder in `impl AnimationDecoder` if animated.
201    fn animated_decoder(self) -> impl AnimationDecoder<'a>;
202}
203
204impl<'a> ServoImageDecoder<'a> for DefaultImageDecoder<'a> {
205    fn make_decoder(format: ImageFormat, buffer: &'a [u8]) -> ImageResult<Self> {
206        let reader = Cursor::new(buffer);
207        let decoder = match format {
208            ImageFormat::Png => {
209                let limits = Limits::default();
210                let png_decoder = png::PngDecoder::with_limits(reader, limits)?;
211                if png_decoder.is_apng().unwrap_or_default() {
212                    let decoder = png_decoder.apng()?;
213                    GenericImageDecoder::Apng(Box::new(decoder))
214                } else {
215                    GenericImageDecoder::Png(Box::new(png_decoder))
216                }
217            },
218            ImageFormat::Gif => GenericImageDecoder::Gif(Box::new(gif::GifDecoder::new(reader)?)),
219            ImageFormat::WebP => {
220                GenericImageDecoder::Webp(Box::new(webp::WebPDecoder::new(reader)?))
221            },
222            ImageFormat::Jpeg => {
223                GenericImageDecoder::Jpeg(Box::new(jpeg::JpegDecoder::new(reader)?))
224            },
225            ImageFormat::Bmp => GenericImageDecoder::Bmp(Box::new(bmp::BmpDecoder::new(reader)?)),
226            ImageFormat::Ico => GenericImageDecoder::Ico(Box::new(ico::IcoDecoder::new(reader)?)),
227            _ => {
228                return Err(ImageError::Unsupported(
229                    ImageFormatHint::Exact(format).into(),
230                ));
231            },
232        };
233        Ok(DefaultImageDecoder { decoder })
234    }
235
236    fn is_animated(&self) -> bool {
237        match &self.decoder {
238            GenericImageDecoder::Apng(_) | GenericImageDecoder::Gif(_) => true,
239            GenericImageDecoder::Webp(decoder) => decoder.has_animation(),
240            GenericImageDecoder::Png(_) |
241            GenericImageDecoder::Jpeg(_) |
242            GenericImageDecoder::Bmp(_) |
243            GenericImageDecoder::Ico(_) => false,
244        }
245    }
246
247    fn decoder(self) -> impl ImageDecoder {
248        self.decoder
249    }
250
251    fn animated_decoder(self) -> impl AnimationDecoder<'a> {
252        self.decoder
253    }
254}
255
256pub(crate) fn decode_static_image(
257    cors_status: CorsStatus,
258    mut image_decoder: impl ImageDecoder,
259) -> Option<RasterImage> {
260    let orientation = image_decoder.orientation();
261
262    let Ok(mut dynamic_image) = DynamicImage::from_decoder(image_decoder) else {
263        debug!("Image decoding error");
264        return None;
265    };
266
267    if let Ok(orientation) = orientation {
268        dynamic_image.apply_orientation(orientation);
269    }
270
271    let mut rgba = dynamic_image.into_rgba8();
272
273    // Store pre-multiplied data as that prevents having to do conversions of the data at later
274    // times. This does cause an issue with some canvas APIs. See:
275    // https://github.com/servo/servo/issues/40257
276    let is_opaque = rgba8_premultiply_inplace(&mut rgba);
277
278    let frame = ImageFrame {
279        delay: None,
280        byte_range: 0..rgba.len(),
281        width: rgba.width(),
282        height: rgba.height(),
283    };
284    Some(RasterImage {
285        metadata: ImageMetadata {
286            width: rgba.width(),
287            height: rgba.height(),
288        },
289        format: PixelFormat::RGBA8,
290        frames: vec![frame],
291        bytes: Arc::new(rgba.into_vec()),
292        id: None,
293        cors_status,
294        is_opaque,
295        loop_count: None,
296    })
297}
298
299pub(crate) fn decode_animated_image<'a, T>(
300    cors_status: CorsStatus,
301    animated_image_decoder: T,
302) -> Option<RasterImage>
303where
304    T: AnimationDecoder<'a>,
305{
306    let mut width = 0;
307    let mut height = 0;
308
309    // This uses `map_while`, because the first non-decodable frame seems to
310    // send the frame iterator into an infinite loop. See
311    // <https://github.com/image-rs/image/issues/2442>.
312    let mut frame_data = vec![];
313    let mut total_number_of_bytes = 0;
314    let mut is_opaque = true;
315    let loop_count = match animated_image_decoder.loop_count() {
316        LoopCount::Finite(repeat_time) => Repeat::Finite(repeat_time),
317        LoopCount::Infinite => Repeat::Infinite,
318    };
319    let frames: Vec<ImageFrame> = animated_image_decoder
320        .into_frames()
321        .map_while(|decoded_frame| {
322            let mut animated_frame = match decoded_frame {
323                Ok(decoded_frame) => decoded_frame,
324                Err(error) => {
325                    debug!("decode Animated frame error: {error}");
326                    return None;
327                },
328            };
329
330            // Store pre-multiplied data as that prevents having to do conversions of the data at later
331            // times. This does cause an issue with some canvas APIs. See:
332            // https://github.com/servo/servo/issues/40257
333            is_opaque = rgba8_premultiply_inplace(animated_frame.buffer_mut()) && is_opaque;
334
335            let frame_start = total_number_of_bytes;
336            total_number_of_bytes += animated_frame.buffer().len();
337
338            // The image size should be at least as large as the largest frame.
339            let frame_width = animated_frame.buffer().width();
340            let frame_height = animated_frame.buffer().height();
341            width = cmp::max(width, frame_width);
342            height = cmp::max(height, frame_height);
343
344            let frame = ImageFrame {
345                byte_range: frame_start..total_number_of_bytes,
346                delay: Some(Duration::from(animated_frame.delay())),
347                width: frame_width,
348                height: frame_height,
349            };
350
351            frame_data.push(animated_frame);
352
353            Some(frame)
354        })
355        .collect();
356
357    if frames.is_empty() {
358        debug!("Animated Image decoding error");
359        return None;
360    }
361
362    // Coalesce the frame data into one single shared memory region.
363    let mut bytes = Vec::with_capacity(total_number_of_bytes);
364    for frame in frame_data {
365        bytes.extend_from_slice(frame.buffer());
366    }
367
368    Some(RasterImage {
369        metadata: ImageMetadata { width, height },
370        cors_status,
371        frames,
372        id: None,
373        format: PixelFormat::RGBA8,
374        bytes: Arc::new(bytes),
375        is_opaque,
376        loop_count: Some(loop_count),
377    })
378}