1// Copyright 2018 the Resvg Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4pub fn render(
5 image: &usvg::Image,
6 transform: tiny_skia::Transform,
7 pixmap: &mut tiny_skia::PixmapMut,
8) {
9 if !image.is_visible() {
10 return;
11 }
12
13 render_inner(image.kind(), transform, image.rendering_mode(), pixmap);
14}
15
16pub fn render_inner(
17 image_kind: &usvg::ImageKind,
18 transform: tiny_skia::Transform,
19 #[allow(unused_variables)] rendering_mode: usvg::ImageRendering,
20 pixmap: &mut tiny_skia::PixmapMut,
21) {
22 match image_kind {
23 usvg::ImageKind::SVG(ref tree: &Tree) => {
24 render_vector(tree, transform, pixmap);
25 }
26 #[cfg(feature = "raster-images")]
27 _ => {
28 raster_images::render_raster(image_kind, transform, rendering_mode, pixmap);
29 }
30 #[cfg(not(feature = "raster-images"))]
31 _ => {
32 log::warn!("Images decoding was disabled by a build feature.");
33 }
34 }
35}
36
37fn render_vector(
38 tree: &usvg::Tree,
39 transform: tiny_skia::Transform,
40 pixmap: &mut tiny_skia::PixmapMut,
41) -> Option<()> {
42 let mut sub_pixmap: Pixmap = tiny_skia::Pixmap::new(pixmap.width(), pixmap.height()).unwrap();
43 crate::render(tree, transform, &mut sub_pixmap.as_mut());
44 pixmap.draw_pixmap(
45 x:0,
46 y:0,
47 sub_pixmap.as_ref(),
48 &tiny_skia::PixmapPaint::default(),
49 tiny_skia::Transform::default(),
50 mask:None,
51 );
52
53 Some(())
54}
55
56#[cfg(feature = "raster-images")]
57mod raster_images {
58 use crate::OptionLog;
59 use usvg::ImageRendering;
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 usvg::ImageKind::WEBP(ref data) => {
74 decode_webp(data).log_none(|| log::warn!("Failed to decode a WebP image."))
75 }
76 }
77 }
78
79 fn decode_png(data: &[u8]) -> Option<tiny_skia::Pixmap> {
80 tiny_skia::Pixmap::decode_png(data).ok()
81 }
82
83 fn decode_jpeg(data: &[u8]) -> Option<tiny_skia::Pixmap> {
84 use zune_jpeg::zune_core::colorspace::ColorSpace;
85 use zune_jpeg::zune_core::options::DecoderOptions;
86
87 let options = DecoderOptions::default().jpeg_set_out_colorspace(ColorSpace::RGBA);
88 let mut decoder = zune_jpeg::JpegDecoder::new_with_options(data, options);
89 decoder.decode_headers().ok()?;
90 let output_cs = decoder.get_output_colorspace()?;
91
92 let img_data = {
93 let data = decoder.decode().ok()?;
94 match output_cs {
95 ColorSpace::RGBA => data,
96 // `set_output_color_space` is not guaranteed to actually always set the output space
97 // to RGBA (its docs say "we do not guarantee the decoder can convert to all colorspaces").
98 // In particular, it seems like it doesn't work for luma JPEGs,
99 // so we convert them manually.
100 ColorSpace::Luma => data
101 .into_iter()
102 .flat_map(|p| [p, p, p, 255])
103 .collect::<Vec<_>>(),
104 _ => return None,
105 }
106 };
107
108 let info = decoder.info()?;
109
110 let size = tiny_skia::IntSize::from_wh(info.width as u32, info.height as u32)?;
111 tiny_skia::Pixmap::from_vec(img_data, size)
112 }
113
114 fn decode_gif(data: &[u8]) -> Option<tiny_skia::Pixmap> {
115 let mut decoder = gif::DecodeOptions::new();
116 decoder.set_color_output(gif::ColorOutput::RGBA);
117 let mut decoder = decoder.read_info(data).ok()?;
118 let first_frame = decoder.read_next_frame().ok()??;
119
120 let size = tiny_skia::IntSize::from_wh(
121 u32::from(first_frame.width),
122 u32::from(first_frame.height),
123 )?;
124
125 let (w, h) = size.dimensions();
126 let mut pixmap = tiny_skia::Pixmap::new(w, h)?;
127 rgba_to_pixmap(&first_frame.buffer, &mut pixmap);
128 Some(pixmap)
129 }
130
131 fn decode_webp(data: &[u8]) -> Option<tiny_skia::Pixmap> {
132 let mut decoder = image_webp::WebPDecoder::new(std::io::Cursor::new(data)).ok()?;
133 let mut first_frame = vec![0; decoder.output_buffer_size()?];
134 decoder.read_image(&mut first_frame).ok()?;
135
136 let (w, h) = decoder.dimensions();
137 let mut pixmap = tiny_skia::Pixmap::new(w, h)?;
138
139 if decoder.has_alpha() {
140 rgba_to_pixmap(&first_frame, &mut pixmap);
141 } else {
142 rgb_to_pixmap(&first_frame, &mut pixmap);
143 }
144
145 Some(pixmap)
146 }
147
148 fn rgb_to_pixmap(data: &[u8], pixmap: &mut tiny_skia::Pixmap) {
149 use rgb::FromSlice;
150
151 let mut i = 0;
152 let dst = pixmap.data_mut();
153 for p in data.as_rgb() {
154 dst[i + 0] = p.r;
155 dst[i + 1] = p.g;
156 dst[i + 2] = p.b;
157 dst[i + 3] = 255;
158
159 i += tiny_skia::BYTES_PER_PIXEL;
160 }
161 }
162
163 fn rgba_to_pixmap(data: &[u8], pixmap: &mut tiny_skia::Pixmap) {
164 use rgb::FromSlice;
165
166 let mut i = 0;
167 let dst = pixmap.data_mut();
168 for p in data.as_rgba() {
169 let a = p.a as f64 / 255.0;
170 dst[i + 0] = (p.r as f64 * a + 0.5) as u8;
171 dst[i + 1] = (p.g as f64 * a + 0.5) as u8;
172 dst[i + 2] = (p.b as f64 * a + 0.5) as u8;
173 dst[i + 3] = p.a;
174
175 i += tiny_skia::BYTES_PER_PIXEL;
176 }
177 }
178
179 pub(crate) fn render_raster(
180 image: &usvg::ImageKind,
181 transform: tiny_skia::Transform,
182 rendering_mode: usvg::ImageRendering,
183 pixmap: &mut tiny_skia::PixmapMut,
184 ) -> Option<()> {
185 let raster = decode_raster(image)?;
186
187 let rect = tiny_skia::Size::from_wh(raster.width() as f32, raster.height() as f32)?
188 .to_rect(0.0, 0.0)?;
189
190 let quality = match rendering_mode {
191 ImageRendering::OptimizeQuality => tiny_skia::FilterQuality::Bicubic,
192 ImageRendering::OptimizeSpeed => tiny_skia::FilterQuality::Nearest,
193 ImageRendering::Smooth => tiny_skia::FilterQuality::Bilinear,
194 ImageRendering::HighQuality => tiny_skia::FilterQuality::Bicubic,
195 ImageRendering::CrispEdges => tiny_skia::FilterQuality::Nearest,
196 ImageRendering::Pixelated => tiny_skia::FilterQuality::Nearest,
197 };
198
199 let pattern = tiny_skia::Pattern::new(
200 raster.as_ref(),
201 tiny_skia::SpreadMode::Pad,
202 quality,
203 1.0,
204 tiny_skia::Transform::default(),
205 );
206 let mut paint = tiny_skia::Paint::default();
207 paint.shader = pattern;
208
209 pixmap.fill_rect(rect, &paint, transform, None);
210
211 Some(())
212 }
213}
214