Add screenshot functionality.
This commit is contained in:
parent
aef7dd1353
commit
f6af769486
|
@ -504,6 +504,7 @@ version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
"jemallocator 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
"nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"pathfinder_geometry 0.3.0",
|
"pathfinder_geometry 0.3.0",
|
||||||
|
|
|
@ -13,6 +13,11 @@ rayon = "1.0"
|
||||||
sdl2 = "0.32"
|
sdl2 = "0.32"
|
||||||
usvg = "0.4"
|
usvg = "0.4"
|
||||||
|
|
||||||
|
[dependencies.image]
|
||||||
|
version = "0.21"
|
||||||
|
default-features = false
|
||||||
|
features = ["png_codec"]
|
||||||
|
|
||||||
[dependencies.pathfinder_geometry]
|
[dependencies.pathfinder_geometry]
|
||||||
path = "../../geometry"
|
path = "../../geometry"
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,8 @@
|
||||||
|
|
||||||
use crate::ui::{DemoUI, UIAction, UIEvent};
|
use crate::ui::{DemoUI, UIAction, UIEvent};
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
use gl::types::GLsizei;
|
use gl::types::{GLsizei, GLvoid};
|
||||||
|
use image::ColorType;
|
||||||
use jemallocator;
|
use jemallocator;
|
||||||
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32, Point3DF32};
|
use pathfinder_geometry::basic::point::{Point2DF32, Point2DI32, Point3DF32};
|
||||||
use pathfinder_geometry::basic::rect::RectF32;
|
use pathfinder_geometry::basic::rect::RectF32;
|
||||||
|
@ -89,6 +90,7 @@ pub struct DemoApp {
|
||||||
camera: Camera,
|
camera: Camera,
|
||||||
frame_counter: u32,
|
frame_counter: u32,
|
||||||
events: Vec<Event>,
|
events: Vec<Event>,
|
||||||
|
pending_screenshot_path: Option<PathBuf>,
|
||||||
exit: bool,
|
exit: bool,
|
||||||
mouselook_enabled: bool,
|
mouselook_enabled: bool,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
|
@ -164,6 +166,7 @@ impl DemoApp {
|
||||||
|
|
||||||
camera,
|
camera,
|
||||||
frame_counter: 0,
|
frame_counter: 0,
|
||||||
|
pending_screenshot_path: None,
|
||||||
events: vec![],
|
events: vec![],
|
||||||
exit: false,
|
exit: false,
|
||||||
mouselook_enabled: false,
|
mouselook_enabled: false,
|
||||||
|
@ -342,6 +345,10 @@ impl DemoApp {
|
||||||
self.draw_environment(&render_transform);
|
self.draw_environment(&render_transform);
|
||||||
self.render_vector_scene(&built_scene);
|
self.render_vector_scene(&built_scene);
|
||||||
|
|
||||||
|
if self.pending_screenshot_path.is_some() {
|
||||||
|
self.take_screenshot();
|
||||||
|
}
|
||||||
|
|
||||||
let rendering_time = self.renderer.shift_timer_query();
|
let rendering_time = self.renderer.shift_timer_query();
|
||||||
self.renderer.debug_ui.add_sample(tile_time, rendering_time);
|
self.renderer.debug_ui.add_sample(tile_time, rendering_time);
|
||||||
self.renderer.debug_ui.draw();
|
self.renderer.debug_ui.draw();
|
||||||
|
@ -499,6 +506,11 @@ impl DemoApp {
|
||||||
self.dirty = true;
|
self.dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UIAction::TakeScreenshot(ref path) => {
|
||||||
|
self.pending_screenshot_path = Some((*path).clone());
|
||||||
|
self.dirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
UIAction::ZoomIn => {
|
UIAction::ZoomIn => {
|
||||||
if let Camera::TwoD(ref mut transform) = self.camera {
|
if let Camera::TwoD(ref mut transform) = self.camera {
|
||||||
let scale = Point2DF32::splat(1.0 + CAMERA_ZOOM_AMOUNT_2D);
|
let scale = Point2DF32::splat(1.0 + CAMERA_ZOOM_AMOUNT_2D);
|
||||||
|
@ -531,6 +543,17 @@ impl DemoApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn take_screenshot(&mut self) {
|
||||||
|
let screenshot_path = self.pending_screenshot_path.take().unwrap();
|
||||||
|
let (drawable_width, drawable_height) = self.window.drawable_size();
|
||||||
|
let pixels = self.device.readback_pixels(drawable_width, drawable_height);
|
||||||
|
image::save_buffer(screenshot_path,
|
||||||
|
&pixels,
|
||||||
|
drawable_width,
|
||||||
|
drawable_height,
|
||||||
|
ColorType::RGBA(8)).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
fn background_color(&self) -> ColorU {
|
fn background_color(&self) -> ColorU {
|
||||||
if self.ui.dark_background_enabled { DARK_BG_COLOR } else { LIGHT_BG_COLOR }
|
if self.ui.dark_background_enabled { DARK_BG_COLOR } else { LIGHT_BG_COLOR }
|
||||||
}
|
}
|
||||||
|
@ -817,6 +840,29 @@ impl DemoDevice {
|
||||||
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
|
gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT | gl::STENCIL_BUFFER_BIT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn readback_pixels(&self, width: u32, height: u32) -> Vec<u8> {
|
||||||
|
let mut pixels = vec![0; width as usize * height as usize * 4];
|
||||||
|
unsafe {
|
||||||
|
gl::BindFramebuffer(gl::FRAMEBUFFER, 0);
|
||||||
|
gl::ReadPixels(0, 0,
|
||||||
|
width as GLsizei, height as GLsizei,
|
||||||
|
gl::RGBA,
|
||||||
|
gl::UNSIGNED_BYTE,
|
||||||
|
pixels.as_mut_ptr() as *mut GLvoid);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flip right-side-up.
|
||||||
|
let stride = width as usize * 4;
|
||||||
|
for y in 0..(height as usize / 2) {
|
||||||
|
let (index_a, index_b) = (y * stride, (height as usize - y - 1) * stride);
|
||||||
|
for offset in 0..stride {
|
||||||
|
pixels.swap(index_a + offset, index_b + offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pixels
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct GroundProgram {
|
struct GroundProgram {
|
||||||
|
|
|
@ -30,19 +30,23 @@ const SLIDER_KNOB_HEIGHT: i32 = 48;
|
||||||
const EFFECTS_PANEL_WIDTH: i32 = 550;
|
const EFFECTS_PANEL_WIDTH: i32 = 550;
|
||||||
const EFFECTS_PANEL_HEIGHT: i32 = BUTTON_HEIGHT * 3 + PADDING * 4;
|
const EFFECTS_PANEL_HEIGHT: i32 = BUTTON_HEIGHT * 3 + PADDING * 4;
|
||||||
|
|
||||||
const BACKGROUND_SWITCH_X: i32 = PADDING + (BUTTON_WIDTH + PADDING) * 2 + PADDING + SWITCH_SIZE;
|
const OPEN_BUTTON_X: i32 = PADDING + (BUTTON_WIDTH + PADDING) * 1;
|
||||||
|
const SCREENSHOT_BUTTON_X: i32 = PADDING + (BUTTON_WIDTH + PADDING) * 2;
|
||||||
|
const THREE_D_SWITCH_X: i32 = PADDING + (BUTTON_WIDTH + PADDING) * 3;
|
||||||
|
const BACKGROUND_SWITCH_X: i32 = PADDING + (BUTTON_WIDTH + PADDING) * 3 + PADDING + SWITCH_SIZE;
|
||||||
|
|
||||||
const ROTATE_PANEL_X: i32 = PADDING + (BUTTON_WIDTH + PADDING) * 2 + (PADDING + SWITCH_SIZE) * 2;
|
const ROTATE_PANEL_X: i32 = PADDING + (BUTTON_WIDTH + PADDING) * 3 + (PADDING + SWITCH_SIZE) * 2;
|
||||||
const ROTATE_PANEL_WIDTH: i32 = SLIDER_WIDTH + PADDING * 2;
|
const ROTATE_PANEL_WIDTH: i32 = SLIDER_WIDTH + PADDING * 2;
|
||||||
const ROTATE_PANEL_HEIGHT: i32 = PADDING * 2 + SLIDER_HEIGHT;
|
const ROTATE_PANEL_HEIGHT: i32 = PADDING * 2 + SLIDER_HEIGHT;
|
||||||
|
|
||||||
static EFFECTS_PNG_NAME: &'static str = "demo-effects";
|
static EFFECTS_PNG_NAME: &'static str = "demo-effects";
|
||||||
static OPEN_PNG_NAME: &'static str = "demo-open";
|
static OPEN_PNG_NAME: &'static str = "demo-open";
|
||||||
static ROTATE_PNG_NAME: &'static str = "demo-rotate";
|
static ROTATE_PNG_NAME: &'static str = "demo-rotate";
|
||||||
static ZOOM_IN_PNG_NAME: &'static str = "demo-zoom-in";
|
static ZOOM_IN_PNG_NAME: &'static str = "demo-zoom-in";
|
||||||
static ZOOM_OUT_PNG_NAME: &'static str = "demo-zoom-out";
|
static ZOOM_OUT_PNG_NAME: &'static str = "demo-zoom-out";
|
||||||
static BG_LIGHT_PNG_NAME: &'static str = "demo-bg-light";
|
static BG_LIGHT_PNG_NAME: &'static str = "demo-bg-light";
|
||||||
static BG_DARK_PNG_NAME: &'static str = "demo-bg-dark";
|
static BG_DARK_PNG_NAME: &'static str = "demo-bg-dark";
|
||||||
|
static SCREENSHOT_PNG_NAME: &'static str = "demo-screenshot";
|
||||||
|
|
||||||
pub struct DemoUI {
|
pub struct DemoUI {
|
||||||
effects_texture: Texture,
|
effects_texture: Texture,
|
||||||
|
@ -52,6 +56,7 @@ pub struct DemoUI {
|
||||||
zoom_out_texture: Texture,
|
zoom_out_texture: Texture,
|
||||||
bg_light_texture: Texture,
|
bg_light_texture: Texture,
|
||||||
bg_dark_texture: Texture,
|
bg_dark_texture: Texture,
|
||||||
|
screenshot_texture: Texture,
|
||||||
|
|
||||||
effects_panel_visible: bool,
|
effects_panel_visible: bool,
|
||||||
rotate_panel_visible: bool,
|
rotate_panel_visible: bool,
|
||||||
|
@ -73,6 +78,7 @@ impl DemoUI {
|
||||||
let zoom_out_texture = device.create_texture_from_png(ZOOM_OUT_PNG_NAME);
|
let zoom_out_texture = device.create_texture_from_png(ZOOM_OUT_PNG_NAME);
|
||||||
let bg_light_texture = device.create_texture_from_png(BG_LIGHT_PNG_NAME);
|
let bg_light_texture = device.create_texture_from_png(BG_LIGHT_PNG_NAME);
|
||||||
let bg_dark_texture = device.create_texture_from_png(BG_DARK_PNG_NAME);
|
let bg_dark_texture = device.create_texture_from_png(BG_DARK_PNG_NAME);
|
||||||
|
let screenshot_texture = device.create_texture_from_png(SCREENSHOT_PNG_NAME);
|
||||||
|
|
||||||
DemoUI {
|
DemoUI {
|
||||||
effects_texture,
|
effects_texture,
|
||||||
|
@ -82,6 +88,7 @@ impl DemoUI {
|
||||||
zoom_out_texture,
|
zoom_out_texture,
|
||||||
bg_light_texture,
|
bg_light_texture,
|
||||||
bg_dark_texture,
|
bg_dark_texture,
|
||||||
|
screenshot_texture,
|
||||||
three_d_enabled: options.three_d,
|
three_d_enabled: options.three_d,
|
||||||
dark_background_enabled: true,
|
dark_background_enabled: true,
|
||||||
effects_panel_visible: false,
|
effects_panel_visible: false,
|
||||||
|
@ -107,27 +114,36 @@ impl DemoUI {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw open button.
|
// Draw open button.
|
||||||
let open_button_x = PADDING + BUTTON_WIDTH + PADDING;
|
let lower_button_y = bottom - BUTTON_HEIGHT;
|
||||||
let open_button_y = bottom - BUTTON_HEIGHT;
|
let open_button_position = Point2DI32::new(OPEN_BUTTON_X, lower_button_y);
|
||||||
let open_button_position = Point2DI32::new(open_button_x, open_button_y);
|
|
||||||
if self.draw_button(debug_ui, event, open_button_position, &self.open_texture) {
|
if self.draw_button(debug_ui, event, open_button_position, &self.open_texture) {
|
||||||
if let Ok(Response::Okay(file)) = nfd::open_file_dialog(Some("svg"), None) {
|
if let Ok(Response::Okay(file)) = nfd::open_file_dialog(Some("svg"), None) {
|
||||||
*action = UIAction::OpenFile(PathBuf::from(file));
|
*action = UIAction::OpenFile(PathBuf::from(file));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Draw screenshot button.
|
||||||
|
let screenshot_button_position = Point2DI32::new(SCREENSHOT_BUTTON_X, lower_button_y);
|
||||||
|
if self.draw_button(debug_ui,
|
||||||
|
event,
|
||||||
|
screenshot_button_position,
|
||||||
|
&self.screenshot_texture) {
|
||||||
|
if let Ok(Response::Okay(file)) = nfd::open_save_dialog(Some("png"), None) {
|
||||||
|
*action = UIAction::TakeScreenshot(PathBuf::from(file));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Draw 3D switch.
|
// Draw 3D switch.
|
||||||
let threed_switch_x = PADDING + (BUTTON_WIDTH + PADDING) * 2;
|
let three_d_switch_origin = Point2DI32::new(THREE_D_SWITCH_X, lower_button_y);
|
||||||
let threed_switch_origin = Point2DI32::new(threed_switch_x, open_button_y);
|
|
||||||
self.three_d_enabled = self.draw_text_switch(debug_ui,
|
self.three_d_enabled = self.draw_text_switch(debug_ui,
|
||||||
event,
|
event,
|
||||||
threed_switch_origin,
|
three_d_switch_origin,
|
||||||
"2D",
|
"2D",
|
||||||
"3D",
|
"3D",
|
||||||
self.three_d_enabled);
|
self.three_d_enabled);
|
||||||
|
|
||||||
// Draw background switch.
|
// Draw background switch.
|
||||||
let background_switch_origin = Point2DI32::new(BACKGROUND_SWITCH_X, open_button_y);
|
let background_switch_origin = Point2DI32::new(BACKGROUND_SWITCH_X, lower_button_y);
|
||||||
self.dark_background_enabled = self.draw_image_switch(debug_ui,
|
self.dark_background_enabled = self.draw_image_switch(debug_ui,
|
||||||
event,
|
event,
|
||||||
background_switch_origin,
|
background_switch_origin,
|
||||||
|
@ -354,6 +370,7 @@ impl DemoUI {
|
||||||
pub enum UIAction {
|
pub enum UIAction {
|
||||||
None,
|
None,
|
||||||
OpenFile(PathBuf),
|
OpenFile(PathBuf),
|
||||||
|
TakeScreenshot(PathBuf),
|
||||||
ZoomIn,
|
ZoomIn,
|
||||||
ZoomOut,
|
ZoomOut,
|
||||||
Rotate(f32),
|
Rotate(f32),
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 885 B |
Loading…
Reference in New Issue