resvg/
image.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 http://mozilla.org/MPL/2.0/.
4
5pub fn render(
6    image: &usvg::Image,
7    transform: tiny_skia::Transform,
8    pixmap: &mut tiny_skia::PixmapMut,
9) {
10    if !image.is_visible() {
11        return;
12    }
13
14    render_inner(image.kind(), transform, image.rendering_mode(), pixmap);
15}
16
17pub fn render_inner(
18    image_kind: &usvg::ImageKind,
19    transform: tiny_skia::Transform,
20    #[allow(unused_variables)] rendering_mode: usvg::ImageRendering,
21    pixmap: &mut tiny_skia::PixmapMut,
22) {
23    match image_kind {
24        usvg::ImageKind::SVG(ref tree) => {
25            render_vector(tree, transform, pixmap);
26        }
27        #[cfg(feature = "raster-images")]
28        _ => {
29            raster_images::render_raster(image_kind, transform, rendering_mode, pixmap);
30        }
31        #[cfg(not(feature = "raster-images"))]
32        _ => {
33            log::warn!("Images decoding was disabled by a build feature.");
34        }
35    }
36}
37
38fn render_vector(
39    tree: &usvg::Tree,
40    transform: tiny_skia::Transform,
41    pixmap: &mut tiny_skia::PixmapMut,
42) -> Option<()> {
43    let mut sub_pixmap = tiny_skia::Pixmap::new(pixmap.width(), pixmap.height()).unwrap();
44    crate::render(tree, transform, &mut sub_pixmap.as_mut());
45    pixmap.draw_pixmap(
46        0,
47        0,
48        sub_pixmap.as_ref(),
49        &tiny_skia::PixmapPaint::default(),
50        tiny_skia::Transform::default(),
51        None,
52    );
53
54    Some(())
55}
56
57#[cfg(feature = "raster-images")]
58mod raster_images {
59    use crate::OptionLog;
60
61    fn decode_raster(image: &usvg::ImageKind) -> Option<tiny_skia::Pixmap> {
62        match image {
63            usvg::ImageKind::SVG(_) => None,
64            usvg::ImageKind::JPEG(ref data) => {
65                decode_jpeg(data).log_none(|| log::warn!("Failed to decode a JPEG image."))
66            }
67            usvg::ImageKind::PNG(ref data) => {
68                decode_png(data).log_none(|| log::warn!("Failed to decode a PNG image."))
69            }
70            usvg::ImageKind::GIF(ref data) => {
71                decode_gif(data).log_none(|| log::warn!("Failed to decode a GIF image."))
72            }
73        }
74    }
75
76    fn decode_png(data: &[u8]) -> Option<tiny_skia::Pixmap> {
77        tiny_skia::Pixmap::decode_png(data).ok()
78    }
79
80    fn decode_jpeg(data: &[u8]) -> Option<tiny_skia::Pixmap> {
81        let mut decoder = jpeg_decoder::Decoder::new(data);
82        let img_data = decoder.decode().ok()?;
83        let info = decoder.info()?;
84
85        let size = tiny_skia::IntSize::from_wh(info.width as u32, info.height as u32)?;
86
87        let data = match info.pixel_format {
88            jpeg_decoder::PixelFormat::RGB24 => img_data,
89            jpeg_decoder::PixelFormat::L8 => {
90                let mut rgb_data: Vec<u8> = Vec::with_capacity(img_data.len() * 3);
91                for gray in img_data {
92                    rgb_data.push(gray);
93                    rgb_data.push(gray);
94                    rgb_data.push(gray);
95                }
96
97                rgb_data
98            }
99            _ => return None,
100        };
101
102        let (w, h) = size.dimensions();
103        let mut pixmap = tiny_skia::Pixmap::new(w, h)?;
104        rgb_to_pixmap(&data, &mut pixmap);
105        Some(pixmap)
106    }
107
108    fn decode_gif(data: &[u8]) -> Option<tiny_skia::Pixmap> {
109        let mut decoder = gif::DecodeOptions::new();
110        decoder.set_color_output(gif::ColorOutput::RGBA);
111        let mut decoder = decoder.read_info(data).ok()?;
112        let first_frame = decoder.read_next_frame().ok()??;
113
114        let size = tiny_skia::IntSize::from_wh(
115            u32::from(first_frame.width),
116            u32::from(first_frame.height),
117        )?;
118
119        let (w, h) = size.dimensions();
120        let mut pixmap = tiny_skia::Pixmap::new(w, h)?;
121        rgba_to_pixmap(&first_frame.buffer, &mut pixmap);
122        Some(pixmap)
123    }
124
125    fn rgb_to_pixmap(data: &[u8], pixmap: &mut tiny_skia::Pixmap) {
126        use rgb::FromSlice;
127
128        let mut i = 0;
129        let dst = pixmap.data_mut();
130        for p in data.as_rgb() {
131            dst[i + 0] = p.r;
132            dst[i + 1] = p.g;
133            dst[i + 2] = p.b;
134            dst[i + 3] = 255;
135
136            i += tiny_skia::BYTES_PER_PIXEL;
137        }
138    }
139
140    fn rgba_to_pixmap(data: &[u8], pixmap: &mut tiny_skia::Pixmap) {
141        use rgb::FromSlice;
142
143        let mut i = 0;
144        let dst = pixmap.data_mut();
145        for p in data.as_rgba() {
146            let a = p.a as f64 / 255.0;
147            dst[i + 0] = (p.r as f64 * a + 0.5) as u8;
148            dst[i + 1] = (p.g as f64 * a + 0.5) as u8;
149            dst[i + 2] = (p.b as f64 * a + 0.5) as u8;
150            dst[i + 3] = p.a;
151
152            i += tiny_skia::BYTES_PER_PIXEL;
153        }
154    }
155
156    pub(crate) fn render_raster(
157        image: &usvg::ImageKind,
158        transform: tiny_skia::Transform,
159        rendering_mode: usvg::ImageRendering,
160        pixmap: &mut tiny_skia::PixmapMut,
161    ) -> Option<()> {
162        let raster = decode_raster(image)?;
163
164        let rect = tiny_skia::Size::from_wh(raster.width() as f32, raster.height() as f32)?
165            .to_rect(0.0, 0.0)?;
166
167        let mut quality = tiny_skia::FilterQuality::Bicubic;
168        if rendering_mode == usvg::ImageRendering::OptimizeSpeed {
169            quality = tiny_skia::FilterQuality::Nearest;
170        }
171
172        let pattern = tiny_skia::Pattern::new(
173            raster.as_ref(),
174            tiny_skia::SpreadMode::Pad,
175            quality,
176            1.0,
177            tiny_skia::Transform::default(),
178        );
179        let mut paint = tiny_skia::Paint::default();
180        paint.shader = pattern;
181
182        pixmap.fill_rect(rect, &paint, transform, None);
183
184        Some(())
185    }
186}