mirror of
https://github.com/storytold/effectcraft.git
synced 2026-11-02 08:40:44 +00:00
fix(engine): propagate transparency setting to queued frame saves
This commit is contained in:
2 files changed
+27
-5
No files matched your search
@@ -5,7 +5,7 @@ use effectcraft_project::Marker;
|
||||
use effectcraft_time::Tick;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
use super::{CommandSpec, always, bad, comp_id, f_p, has_comp, has_layers, layers_p, str_p};
|
||||
use super::{CommandSpec, always, b_p, bad, comp_id, f_p, has_comp, has_layers, layers_p, str_p};
|
||||
use crate::prefs::CompPreset;
|
||||
use crate::{EngineError, Result, Session, cmd, query};
|
||||
|
||||
@@ -82,7 +82,7 @@ pub(crate) fn encode_png(rgba: &[u8], w: u32, h: u32) -> std::result::Result<Vec
|
||||
/// Save Frame As ▸ File with `queue`: a Render Queue item for the current frame, from the Frame
|
||||
/// Default templates (Edit ▸ Templates).
|
||||
fn queue_frame(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
use effectcraft_project::render_queue::{RenderQueueItem, TimeSpan};
|
||||
use effectcraft_project::render_queue::{Channels, RenderQueueItem, TimeSpan};
|
||||
use effectcraft_project::render_templates::TemplateSlot;
|
||||
let cid = comp_id(s, p)?;
|
||||
let comp = s.project.comp(cid).ok_or(EngineError::NoComp)?;
|
||||
@@ -91,6 +91,15 @@ fn queue_frame(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
it.settings = s.project.render_templates.default_render_settings(TemplateSlot::Still);
|
||||
it.settings.time_span = TimeSpan::Custom { start: t, end: t + comp.frame_duration() };
|
||||
it.output = s.project.render_templates.default_output_module(TemplateSlot::Still);
|
||||
if let Some(transparent) = b_p(p, "transparent") {
|
||||
if !transparent {
|
||||
it.output.channels = Channels::Rgb;
|
||||
} else if it.output.supports_alpha() {
|
||||
it.output.channels = Channels::Rgba;
|
||||
} else {
|
||||
return Err(bad("comp.saveFrameAs", format!("{} doesn't support an alpha channel", it.output.format.label())));
|
||||
}
|
||||
}
|
||||
if let Some(path) = str_p(p, "path") {
|
||||
it.output.output = path.to_string();
|
||||
} else if !it.output.format.is_sequence() {
|
||||
@@ -107,7 +116,7 @@ fn queue_frame(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
}
|
||||
|
||||
fn save_frame(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
if p.get("queue").and_then(Value::as_bool) == Some(true) {
|
||||
if b_p(p, "queue") == Some(true) {
|
||||
return queue_frame(s, p);
|
||||
}
|
||||
let path = str_p(p, "path").ok_or_else(|| bad("comp.saveFrameAs", "missing `path` (.png)"))?.to_string();
|
||||
@@ -117,7 +126,7 @@ fn save_frame(s: &mut Session, p: &Value) -> Result<Value> {
|
||||
let scale = f_p(p, "scale").unwrap_or(1.0).clamp(0.01, 4.0);
|
||||
let bg = comp.background;
|
||||
let img = s.render(cid, t, effectcraft_render::RenderOpts { scale, backend: effectcraft_render::Backend::Auto, ..Default::default() });
|
||||
let rgba = if p.get("transparent").and_then(Value::as_bool) == Some(false) { img.to_rgba8_over(bg) } else { img.to_rgba8() };
|
||||
let rgba = if b_p(p, "transparent") == Some(false) { img.to_rgba8_over(bg) } else { img.to_rgba8() };
|
||||
let png = encode_png(&rgba, img.width, img.height).map_err(|e| EngineError::Other(format!("PNG encoding failed: {e}")))?;
|
||||
s.services.write_file(&path, &png).map_err(|e| EngineError::Other(format!("cannot write {path}: {e}")))?;
|
||||
s.toast(format!("Saved frame to {path}"));
|
||||
|
||||
@@ -194,11 +194,24 @@ fn save_frame_as_png_preserves_alpha_channel() {
|
||||
fn save_frame_as_defaults_to_png_sequence_with_alpha() {
|
||||
let mut s = Session::default();
|
||||
s.execute("comp.new", json!({"name": "Comp", "width": 32, "height": 32, "frameRate": 24, "duration": 1})).unwrap();
|
||||
let r = s.execute("comp.saveFrameAs", json!({"queue": true})).unwrap();
|
||||
let r = s.execute_checked("comp.saveFrameAs", json!({"queue": true})).unwrap();
|
||||
assert_eq!(r["queued"], true);
|
||||
let it = s.project.render_queue.last().unwrap();
|
||||
assert_eq!(it.output.name, "PNG Sequence with Alpha");
|
||||
assert_eq!(it.output.channels, effectcraft_project::render_queue::Channels::Rgba);
|
||||
|
||||
// Queued save with transparent: false sets Channels::Rgb (composited over background).
|
||||
s.execute_checked("comp.saveFrameAs", json!({"queue": true, "transparent": false})).unwrap();
|
||||
let it = s.project.render_queue.last().unwrap();
|
||||
assert_eq!(it.output.channels, effectcraft_project::render_queue::Channels::Rgb);
|
||||
|
||||
// Queued save with transparent: true preserves Channels::Rgba.
|
||||
s.execute_checked("comp.saveFrameAs", json!({"queue": true, "transparent": true})).unwrap();
|
||||
let it = s.project.render_queue.last().unwrap();
|
||||
assert_eq!(it.output.channels, effectcraft_project::render_queue::Channels::Rgba);
|
||||
|
||||
s.execute("edit.undo", json!({})).unwrap();
|
||||
s.execute("edit.undo", json!({})).unwrap();
|
||||
s.execute("edit.undo", json!({})).unwrap();
|
||||
assert!(s.project.render_queue.is_empty());
|
||||
s.execute("edit.redo", json!({})).unwrap();
|
||||
|
||||
Reference in new issue
Block a user