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 
5 use std::{fs, path};
6 
7 use log::warn;
8 
9 use crate::prelude::*;
10 
11 
12 pub struct Image {
13     pub data: ImageData,
14     pub size: ScreenSize,
15 }
16 
17 pub enum ImageData {
18     RGB(Vec<u8>),
19     RGBA(Vec<u8>),
20 }
21 
load_raster( format: usvg::ImageFormat, data: &usvg::ImageData, opt: &Options, ) -> Option<Image>22 pub fn load_raster(
23     format: usvg::ImageFormat,
24     data: &usvg::ImageData,
25     opt: &Options,
26 ) -> Option<Image> {
27     let img = _load_raster(format, data, opt);
28 
29     if img.is_none() {
30         match data {
31             usvg::ImageData::Path(ref path) => {
32                 let path = get_abs_path(path, opt);
33                 warn!("Failed to load an external image: {:?}.", path);
34             }
35             usvg::ImageData::Raw(_) => {
36                 warn!("Failed to load an embedded image.");
37             }
38         }
39     }
40 
41     img
42 }
43 
_load_raster( format: usvg::ImageFormat, data: &usvg::ImageData, opt: &Options, ) -> Option<Image>44 fn _load_raster(
45     format: usvg::ImageFormat,
46     data: &usvg::ImageData,
47     opt: &Options,
48 ) -> Option<Image> {
49     debug_assert!(format != usvg::ImageFormat::SVG);
50 
51     match data {
52         usvg::ImageData::Path(ref path) => {
53             let path = get_abs_path(path, opt);
54             let data = fs::read(path).ok()?;
55 
56             if format == usvg::ImageFormat::JPEG {
57                 read_jpeg(&data)
58             } else {
59                 read_png(&data)
60             }
61         }
62         usvg::ImageData::Raw(ref data) => {
63             if format == usvg::ImageFormat::JPEG {
64                 read_jpeg(data)
65             } else {
66                 read_png(data)
67             }
68         }
69     }
70 }
71 
read_png(data: &[u8]) -> Option<Image>72 fn read_png(data: &[u8]) -> Option<Image> {
73     let decoder = png::Decoder::new(data);
74     let (info, mut reader) = decoder.read_info().ok()?;
75 
76     match info.color_type {
77         png::ColorType::RGB | png::ColorType::RGBA => {}
78         _ => return None,
79     }
80 
81     let size = ScreenSize::new(info.width, info.height)?;
82 
83     let mut img_data = vec![0; info.buffer_size()];
84     reader.next_frame(&mut img_data).ok()?;
85 
86     let data = match info.color_type {
87         png::ColorType::RGB => ImageData::RGB(img_data),
88         png::ColorType::RGBA => ImageData::RGBA(img_data),
89         png::ColorType::Grayscale => {
90             let mut rgb_data = 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             ImageData::RGB(rgb_data)
98         }
99         png::ColorType::GrayscaleAlpha => {
100             let mut rgba_data = Vec::with_capacity(img_data.len() * 2);
101             for slice in img_data.chunks(2) {
102                 let gray = slice[0];
103                 let alpha = slice[1];
104                 rgba_data.push(gray);
105                 rgba_data.push(gray);
106                 rgba_data.push(gray);
107                 rgba_data.push(alpha);
108             }
109 
110             ImageData::RGBA(rgba_data)
111         }
112         _ => return None,
113     };
114 
115     Some(Image {
116         data,
117         size,
118     })
119 }
120 
read_jpeg(data: &[u8]) -> Option<Image>121 fn read_jpeg(data: &[u8]) -> Option<Image> {
122     let mut decoder = jpeg_decoder::Decoder::new(data);
123     let img_data = decoder.decode().ok()?;
124     let info = decoder.info()?;
125 
126     let size = ScreenSize::new(info.width as u32, info.height as u32)?;
127 
128     let data = match info.pixel_format {
129         jpeg_decoder::PixelFormat::RGB24 => ImageData::RGB(img_data),
130         jpeg_decoder::PixelFormat::L8 => {
131             let mut rgb_data = Vec::with_capacity(img_data.len() * 3);
132             for gray in img_data {
133                 rgb_data.push(gray);
134                 rgb_data.push(gray);
135                 rgb_data.push(gray);
136             }
137 
138             ImageData::RGB(rgb_data)
139         }
140         _ => return None,
141     };
142 
143     Some(Image {
144         data,
145         size,
146     })
147 }
148 
load_sub_svg( data: &usvg::ImageData, opt: &Options, ) -> Option<(usvg::Tree, Options)>149 pub fn load_sub_svg(
150     data: &usvg::ImageData,
151     opt: &Options,
152 ) -> Option<(usvg::Tree, Options)> {
153     let mut sub_opt = Options {
154         usvg: usvg::Options {
155             path: None,
156             dpi: opt.usvg.dpi,
157             font_family: opt.usvg.font_family.clone(),
158             font_size: opt.usvg.font_size,
159             languages: opt.usvg.languages.clone(),
160             shape_rendering: opt.usvg.shape_rendering,
161             text_rendering: opt.usvg.text_rendering,
162             image_rendering: opt.usvg.image_rendering,
163             keep_named_groups: false,
164         },
165         fit_to: FitTo::Original,
166         background: None,
167     };
168 
169     let tree = match data {
170         usvg::ImageData::Path(ref path) => {
171             let path = get_abs_path(path, opt);
172             sub_opt.usvg.path = Some(path.clone());
173             usvg::Tree::from_file(path, &sub_opt.usvg).ok()?
174         }
175         usvg::ImageData::Raw(ref data) => {
176             usvg::Tree::from_data(data, &sub_opt.usvg).ok()?
177         }
178     };
179 
180     sanitize_sub_svg(&tree);
181 
182     Some((tree, sub_opt))
183 }
184 
sanitize_sub_svg( tree: &usvg::Tree, )185 fn sanitize_sub_svg(
186     tree: &usvg::Tree,
187 ) {
188     // Remove all Image nodes.
189     //
190     // The referenced SVG image cannot have any 'image' elements by itself.
191     // Not only recursive. Any. Don't know why.
192 
193     // TODO: implement drain or something to the rctree.
194     let mut changed = true;
195     while changed {
196         changed = false;
197 
198         for mut node in tree.root().descendants() {
199             let mut rm = false;
200             if let usvg::NodeKind::Image(_) = *node.borrow() {
201                 rm = true;
202             };
203 
204             if rm {
205                 node.detach();
206                 changed = true;
207                 break;
208             }
209         }
210     }
211 }
212 
prepare_sub_svg_geom( view_box: usvg::ViewBox, img_size: ScreenSize, ) -> (usvg::Transform, Option<Rect>)213 pub fn prepare_sub_svg_geom(
214     view_box: usvg::ViewBox,
215     img_size: ScreenSize,
216 ) -> (usvg::Transform, Option<Rect>) {
217     let r = view_box.rect;
218 
219     let new_size = utils::apply_view_box(&view_box, img_size);
220 
221     let (tx, ty, clip) = if view_box.aspect.slice {
222         let (dx, dy) = utils::aligned_pos(
223             view_box.aspect.align,
224             0.0, 0.0, new_size.width() as f64 - r.width(), new_size.height() as f64 - r.height(),
225         );
226 
227         (r.x() - dx, r.y() - dy, Some(r))
228     } else {
229         let (dx, dy) = utils::aligned_pos(
230             view_box.aspect.align,
231             r.x(), r.y(), r.width() - new_size.width() as f64, r.height() - new_size.height() as f64,
232         );
233 
234         (dx, dy, None)
235     };
236 
237     let sx = new_size.width() as f64 / img_size.width() as f64;
238     let sy = new_size.height() as f64 / img_size.height() as f64;
239     let ts = usvg::Transform::new(sx, 0.0, 0.0, sy, tx, ty);
240 
241     (ts, clip)
242 }
243 
image_rect( view_box: &usvg::ViewBox, img_size: ScreenSize, ) -> Rect244 pub fn image_rect(
245     view_box: &usvg::ViewBox,
246     img_size: ScreenSize,
247 ) -> Rect {
248     let new_size = utils::apply_view_box(view_box, img_size);
249     let (x, y) = utils::aligned_pos(
250         view_box.aspect.align,
251         view_box.rect.x(),
252         view_box.rect.y(),
253         view_box.rect.width() - new_size.width() as f64,
254         view_box.rect.height() - new_size.height() as f64,
255     );
256 
257     new_size.to_size().to_rect(x, y)
258 }
259 
get_abs_path( rel_path: &path::Path, opt: &Options, ) -> path::PathBuf260 fn get_abs_path(
261     rel_path: &path::Path,
262     opt: &Options,
263 ) -> path::PathBuf {
264     match opt.usvg.path {
265         Some(ref path) => path.parent().unwrap().join(rel_path),
266         None => rel_path.into(),
267     }
268 }
269