/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::fs::File;
use std::path::{Path, PathBuf};
use api::{ExternalImageData, ImageDescriptor};
#[cfg(feature =
"png")]
use api::ImageFormat;
use api::units::TexelRect;
#[cfg(feature =
"png")]
use api::units::DeviceIntSize;
#[cfg(feature =
"capture")]
use crate::print_tree::{PrintableTree, PrintTree};
use crate::render_api::CaptureBits;
#[derive(Clone)]
pub struct CaptureConfig {
pub root: PathBuf,
pub bits: CaptureBits,
/// Scene sequence ID when capturing multiple frames. Zero for a single frame capture.
pub scene_id: u32,
/// Frame sequence ID when capturing multiple frames. Zero for a single frame capture.
pub frame_id: u32,
/// Resource sequence ID when capturing multiple frames. Zero for a single frame capture.
pub resource_id: u32,
#[cfg(feature =
"capture")]
pretty: ron::ser::PrettyConfig,
}
impl CaptureConfig {
#[cfg(any(feature =
"capture", feature =
"replay"))]
pub fn new(root: PathBuf, bits: CaptureBits) ->
Self {
CaptureConfig {
root,
bits,
scene_id:
0,
frame_id:
0,
resource_id:
0,
#[cfg(feature =
"capture")]
pretty: ron::ser::PrettyConfig::new()
.enumerate_arrays(
true)
.indentor(
" ".to_string()),
}
}
#[cfg(feature =
"capture")]
pub fn prepare_scene(&
mut self) {
use std::fs::create_dir_all;
self.scene_id +=
1;
let _ = create_dir_all(&
self.scene_root());
}
#[cfg(feature =
"capture")]
pub fn prepare_frame(&
mut self) {
use std::fs::create_dir_all;
self.frame_id +=
1;
let _ = create_dir_all(&
self.frame_root());
}
#[cfg(feature =
"capture")]
pub fn prepare_resource(&
mut self) {
use std::fs::create_dir_all;
self.resource_id +=
1;
let _ = create_dir_all(&
self.resource_root());
}
#[cfg(any(feature =
"capture", feature =
"replay"))]
pub fn scene_root(&
self) -> PathBuf {
if self.scene_id >
0 {
let path = format!(
"scenes/{:05}",
self.scene_id);
self.root.join(path)
}
else {
self.root.clone()
}
}
#[cfg(any(feature =
"capture", feature =
"replay"))]
pub fn frame_root(&
self) -> PathBuf {
if self.frame_id >
0 {
let path = format!(
"frames/{:05}",
self.frame_id);
self.scene_root().join(path)
}
else {
self.root.clone()
}
}
#[cfg(any(feature =
"capture", feature =
"replay"))]
pub fn resource_root(&
self) -> PathBuf {
if self.resource_id >
0 {
let path = format!(
"resources/{:05}",
self.resource_id);
self.root.join(path)
}
else {
self.root.clone()
}
}
#[cfg(feature =
"capture")]
pub fn serialize_for_scene<T, P>(&
self, data: &T, name: P)
where
T: serde::Serialize,
P: AsRef<Path>,
{
self.serialize(data,
self.scene_root(), name)
}
#[cfg(feature =
"capture")]
pub fn serialize_for_frame<T, P>(&
self, data: &T, name: P)
where
T: serde::Serialize,
P: AsRef<Path>,
{
self.serialize(data,
self.frame_root(), name)
}
#[cfg(feature =
"capture")]
pub fn serialize_for_resource<T, P>(&
self, data: &T, name: P)
where
T: serde::Serialize,
P: AsRef<Path>,
{
self.serialize(data,
self.resource_root(), name)
}
#[cfg(feature =
"capture")]
pub fn file_path_for_frame<P>(&
self, name: P, ext: &str) -> PathBuf
where P: AsRef<Path> {
self.frame_root().join(name).with_extension(ext)
}
#[cfg(feature =
"capture")]
fn serialize<T, P>(&
self, data: &T, path: PathBuf, name: P)
where
T: serde::Serialize,
P: AsRef<Path>,
{
use std::io::Write;
let ron = ron::ser::to_string_pretty(data,
self.pretty.clone())
.unwrap();
let mut file = File::create(path.join(name).with_extension(
"ron"))
.unwrap();
write!(file,
"{}\n", ron)
.unwrap();
}
#[cfg(feature =
"capture")]
fn serialize_tree<T, P>(data: &T, root: PathBuf, name: P)
where
T: PrintableTree,
P: AsRef<Path>
{
let path = root
.join(name)
.with_extension(
"tree");
let file = File::create(path)
.unwrap();
let mut pt = PrintTree::new_with_sink(
"", file);
data.print_with(&
mut pt);
}
#[cfg(feature =
"capture")]
pub fn serialize_tree_for_frame<T, P>(&
self, data: &T, name: P)
where
T: PrintableTree,
P: AsRef<Path>
{
Self::serialize_tree(data,
self.frame_root(), name)
}
#[cfg(feature =
"replay")]
fn deserialize<T, P>(root: &PathBuf, name: P) -> Option<T>
where
T:
for<
'a> serde::Deserialize<'a>,
P: AsRef<Path>,
{
use std::io::Read;
let mut string = String::new();
let path = root
.join(name.as_ref())
.with_extension(
"ron");
File::open(path)
.ok()?
.read_to_string(&
mut string)
.unwrap();
match ron::de::from_str(&string) {
Ok(out) => Some(out),
Err(e) => panic!(
"File {:?} deserialization failed: {:?}", name.as_ref(), e),
}
}
#[cfg(feature =
"replay")]
pub fn deserialize_for_scene<T, P>(&
self, name: P) -> Option<T>
where
T:
for<
'a> serde::Deserialize<'a>,
P: AsRef<Path>,
{
Self::deserialize(&
self.scene_root(), name)
}
#[cfg(feature =
"replay")]
pub fn deserialize_for_frame<T, P>(&
self, name: P) -> Option<T>
where
T:
for<
'a> serde::Deserialize<'a>,
P: AsRef<Path>,
{
Self::deserialize(&
self.frame_root(), name)
}
#[cfg(feature =
"replay")]
pub fn deserialize_for_resource<T, P>(&
self, name: P) -> Option<T>
where
T:
for<
'a> serde::Deserialize<'a>,
P: AsRef<Path>,
{
Self::deserialize(&
self.resource_root(), name)
}
#[cfg(feature =
"png")]
pub fn save_png(
path: PathBuf, size: DeviceIntSize, format: ImageFormat, stride: Option<i32>, data: &[u8],
) {
use png::{BitDepth, ColorType, Encoder};
use std::io::BufWriter;
use std::borrow::Cow;
// `png` expects
let data =
match stride {
Some(stride)
if stride != format.bytes_per_pixel() * size.width => {
let mut unstrided = Vec::new();
for y
in 0..size.height {
let start = (y * stride)
as usize;
unstrided.extend_from_slice(&data[start..start+(size.width * format.bytes_per_pixel())
as usize]);
}
Cow::from(unstrided)
}
_ => Cow::from(data),
};
let color_type =
match format {
ImageFormat::RGBA8 => ColorType::RGBA,
ImageFormat::BGRA8 => {
warn!(
"Unable to swizzle PNG of BGRA8 type");
ColorType::RGBA
},
ImageFormat::R8 => ColorType::Grayscale,
ImageFormat::RG8 => ColorType::GrayscaleAlpha,
_ => {
error!(
"Unable to save PNG of {:?}", format);
return;
}
};
let w = BufWriter::new(File::create(path).unwrap());
let mut enc = Encoder::new(w, size.width
as u32, size.height
as u32);
enc.set_color(color_type);
enc.set_depth(BitDepth::Eight);
enc
.write_header()
.unwrap()
.write_image_data(&*data)
.unwrap();
}
}
/// An image that `ResourceCache` is unable to resolve during a capture.
/// The image has to be transferred to `Renderer` and locked with the
/// external image handler to get the actual contents and serialize them.
#[derive(Deserialize, Serialize)]
pub struct ExternalCaptureImage {
pub short_path: String,
pub descriptor: ImageDescriptor,
pub external: ExternalImageData,
}
/// A short description of an external image to be saved separately as
/// "externals/XX.ron", redirecting into a specific texture/blob with
/// the corresponding UV rectangle.
#[derive(Deserialize, Serialize)]
pub struct PlainExternalImage {
/// Path to the RON file describing the texel data.
pub data: String,
/// External image data source.
pub external: ExternalImageData,
/// UV sub-rectangle of the image.
pub uv: TexelRect,
}