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 crate::{WindowWrapper, NotifierEvent};
6 use image::png::PNGEncoder;
7 use image::{self, ColorType, GenericImageView};
8 use std::fs::File;
9 use std::path::{Path, PathBuf};
10 use std::sync::mpsc::Receiver;
11 use webrender::api::units::*;
12 use crate::wrench::{Wrench, WrenchThing};
13 use crate::yaml_frame_reader::YamlFrameReader;
14 
15 pub enum ReadSurface {
16     Screen,
17     GpuCache,
18 }
19 
20 pub struct SaveSettings {
21     pub flip_vertical: bool,
22     pub try_crop: bool,
23 }
24 
save<P: Clone + AsRef<Path>>( path: P, orig_pixels: Vec<u8>, size: DeviceIntSize, settings: SaveSettings )25 pub fn save<P: Clone + AsRef<Path>>(
26     path: P,
27     orig_pixels: Vec<u8>,
28     size: DeviceIntSize,
29     settings: SaveSettings
30 ) {
31     let mut width = size.width as u32;
32     let mut height = size.height as u32;
33     let mut buffer = image::RgbaImage::from_raw(
34         width,
35         height,
36         orig_pixels,
37     ).expect("bug: unable to construct image buffer");
38 
39     if settings.flip_vertical {
40         // flip image vertically (texture is upside down)
41         buffer = image::imageops::flip_vertical(&buffer);
42     }
43 
44     if settings.try_crop {
45         if let Ok(existing_image) = image::open(path.clone()) {
46             let old_dims = existing_image.dimensions();
47             println!("Crop from {:?} to {:?}", size, old_dims);
48             width = old_dims.0;
49             height = old_dims.1;
50             buffer = image::imageops::crop(
51                 &mut buffer,
52                 0,
53                 0,
54                 width,
55                 height
56             ).to_image();
57         }
58     }
59 
60     let encoder = PNGEncoder::new(File::create(path).unwrap());
61     encoder
62         .encode(&buffer, width, height, ColorType::Rgba8)
63         .expect("Unable to encode PNG!");
64 }
65 
save_flipped<P: Clone + AsRef<Path>>( path: P, orig_pixels: Vec<u8>, size: DeviceIntSize, )66 pub fn save_flipped<P: Clone + AsRef<Path>>(
67     path: P,
68     orig_pixels: Vec<u8>,
69     size: DeviceIntSize,
70 ) {
71     save(path, orig_pixels, size, SaveSettings {
72         flip_vertical: true,
73         try_crop: true,
74     })
75 }
76 
png( wrench: &mut Wrench, surface: ReadSurface, window: &mut WindowWrapper, mut reader: YamlFrameReader, rx: Receiver<NotifierEvent>, out_path: Option<PathBuf>, )77 pub fn png(
78     wrench: &mut Wrench,
79     surface: ReadSurface,
80     window: &mut WindowWrapper,
81     mut reader: YamlFrameReader,
82     rx: Receiver<NotifierEvent>,
83     out_path: Option<PathBuf>,
84 ) {
85     reader.do_frame(wrench);
86 
87     // wait for the frame
88     rx.recv().unwrap();
89     wrench.render();
90 
91     let (fb_size, data, settings) = match surface {
92         ReadSurface::Screen => {
93             let dim = window.get_inner_size();
94             let rect = FramebufferIntSize::new(dim.width, dim.height).into();
95             let data = wrench.renderer.read_pixels_rgba8(rect);
96             (dim, data, SaveSettings {
97                 flip_vertical: true,
98                 try_crop: true,
99             })
100         }
101         ReadSurface::GpuCache => {
102             let (size, data) = wrench.renderer
103                 .read_gpu_cache();
104             (size, data, SaveSettings {
105                 flip_vertical: false,
106                 try_crop: false,
107             })
108         }
109     };
110 
111     let out_path = out_path.unwrap_or_else(|| {
112         let mut path = reader.yaml_path().clone();
113         path.set_extension("png");
114         path
115     });
116 
117     save(out_path, data, fb_size, settings);
118 }
119