mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-30 21:16:52 -06:00
Export: Targa (.tga) sequence output, 24-bit RGB or 32-bit RGB + Alpha
After Effects lists Targa among its still-image output formats; EffectCraft imported Targa (#745) but could not render to it. - OutputFormat::TargaSequence ("Targa Sequence", `.tga`, numbered like the other sequences; `tga` / `targa` as a format name or output extension). - Written by the `image` crate's Targa encoder (its `tga` feature, already enabled in effectcraft-media; no new crates, Cargo.lock unchanged), with run-length compression off: an 18-byte header, top-left origin, 24-bit BGR for RGB, 32-bit BGRA with 8 alpha bits in the image descriptor for RGB + Alpha (straight or premultiplied), 8-bit grey for Alpha. - The render queue's size estimate counts the uncompressed bytes. - CLI usage, the renderQueue command specs, README, architecture.md and file-format-parity.md name the new format. Tests: each Targa frame holds exactly the pixels of the PNG sequence of the same job, with the expected header, numbering and size; the frames read back through the Targa importer as the same picture (straight, and premultiplied when interpreted so); the image-sequence round trip, the format parser and the renderQueue commands cover it too.
This commit is contained in:
@@ -509,6 +509,9 @@ pub enum OutputFormat {
|
||||
TiffSequence,
|
||||
/// OpenEXR sequence (32-bit float, linear light).
|
||||
ExrSequence,
|
||||
/// Targa sequence: uncompressed 24-bit RGB or 32-bit RGB + Alpha (8 alpha bits), top-left
|
||||
/// origin.
|
||||
TargaSequence,
|
||||
/// Animated GIF.
|
||||
Gif,
|
||||
/// WebM: VP9 video (alpha with RGB + Alpha) + Opus audio.
|
||||
@@ -528,7 +531,7 @@ pub enum OutputFormat {
|
||||
}
|
||||
|
||||
impl OutputFormat {
|
||||
pub const ALL: [OutputFormat; 13] = [
|
||||
pub const ALL: [OutputFormat; 14] = [
|
||||
OutputFormat::H264,
|
||||
OutputFormat::Hevc,
|
||||
OutputFormat::Av1,
|
||||
@@ -539,6 +542,7 @@ impl OutputFormat {
|
||||
OutputFormat::JpegSequence,
|
||||
OutputFormat::TiffSequence,
|
||||
OutputFormat::ExrSequence,
|
||||
OutputFormat::TargaSequence,
|
||||
OutputFormat::Gif,
|
||||
OutputFormat::Wav,
|
||||
OutputFormat::Aiff,
|
||||
@@ -552,6 +556,7 @@ impl OutputFormat {
|
||||
OutputFormat::JpegSequence => "JPEG Sequence",
|
||||
OutputFormat::TiffSequence => "TIFF Sequence",
|
||||
OutputFormat::ExrSequence => "OpenEXR Sequence",
|
||||
OutputFormat::TargaSequence => "Targa Sequence",
|
||||
OutputFormat::Gif => "Animated GIF",
|
||||
OutputFormat::WebM => "WebM (VP9 + Opus)",
|
||||
OutputFormat::Wav => "WAV",
|
||||
@@ -569,6 +574,7 @@ impl OutputFormat {
|
||||
OutputFormat::JpegSequence => "jpg",
|
||||
OutputFormat::TiffSequence => "tif",
|
||||
OutputFormat::ExrSequence => "exr",
|
||||
OutputFormat::TargaSequence => "tga",
|
||||
OutputFormat::Gif => "gif",
|
||||
OutputFormat::WebM => "webm",
|
||||
OutputFormat::Wav => "wav",
|
||||
@@ -577,7 +583,10 @@ impl OutputFormat {
|
||||
}
|
||||
}
|
||||
pub fn is_sequence(self) -> bool {
|
||||
matches!(self, OutputFormat::PngSequence | OutputFormat::JpegSequence | OutputFormat::TiffSequence | OutputFormat::ExrSequence)
|
||||
matches!(
|
||||
self,
|
||||
OutputFormat::PngSequence | OutputFormat::JpegSequence | OutputFormat::TiffSequence | OutputFormat::ExrSequence | OutputFormat::TargaSequence
|
||||
)
|
||||
}
|
||||
pub fn is_movie(self) -> bool {
|
||||
matches!(self, OutputFormat::H264 | OutputFormat::ProRes | OutputFormat::Uncompressed | OutputFormat::WebM | OutputFormat::Hevc | OutputFormat::Av1)
|
||||
@@ -603,7 +612,7 @@ impl OutputFormat {
|
||||
if matches!(self, OutputFormat::H264 | OutputFormat::Hevc) { (even(w), even(h)) } else { (w, h) }
|
||||
}
|
||||
/// Parse a format name: `h264`, `mp4`, `prores`, `mov`, `uncompressed`, `png`, `jpeg`/`jpg`,
|
||||
/// `tiff`/`tif`, `exr`, `gif`, or a label.
|
||||
/// `tiff`/`tif`, `exr`, `tga`/`targa`, `gif`, or a label.
|
||||
pub fn from_name(s: &str) -> Option<OutputFormat> {
|
||||
let n = s.trim().to_ascii_lowercase().replace([' ', '.', '-', '_'], "");
|
||||
Some(match n.as_str() {
|
||||
@@ -616,6 +625,7 @@ impl OutputFormat {
|
||||
"jpg" | "jpeg" | "jpegsequence" => OutputFormat::JpegSequence,
|
||||
"tif" | "tiff" | "tiffsequence" => OutputFormat::TiffSequence,
|
||||
"exr" | "openexr" | "openexrsequence" => OutputFormat::ExrSequence,
|
||||
"tga" | "targa" | "targasequence" => OutputFormat::TargaSequence,
|
||||
"gif" | "animatedgif" => OutputFormat::Gif,
|
||||
"webm" | "vp9" | "webmvp9opus" => OutputFormat::WebM,
|
||||
"wav" | "wave" => OutputFormat::Wav,
|
||||
@@ -1682,6 +1692,28 @@ mod tests {
|
||||
assert_eq!(serde_json::from_str::<OutputModule>(&json).unwrap().format, f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn targa_sequence_format() {
|
||||
let f = OutputFormat::from_name("tga").unwrap();
|
||||
assert_eq!(f, OutputFormat::TargaSequence);
|
||||
assert_eq!(OutputFormat::from_name(&format!("{f:?}")), Some(f));
|
||||
assert_eq!(OutputFormat::from_name("Targa"), Some(f));
|
||||
assert_eq!(OutputFormat::from_name("Targa Sequence"), Some(f));
|
||||
assert_eq!(OutputFormat::from_path("/out/shot_00001.TGA"), Some(f));
|
||||
assert!(OutputFormat::ALL.contains(&f));
|
||||
assert!(f.is_sequence() && f.supports_alpha() && !f.supports_audio() && !f.is_movie());
|
||||
assert_eq!((f.label(), f.extension()), ("Targa Sequence", "tga"));
|
||||
assert_eq!(f.coded_size(3, 5), (3, 5));
|
||||
// A movie name becomes a numbered sequence; RGB + Alpha survives the switch.
|
||||
let mut m = OutputModule::for_format(OutputFormat::ProRes);
|
||||
m.output = "/out/Comp 1.mov".into();
|
||||
m.channels = Channels::Rgba;
|
||||
m.set_format(f);
|
||||
assert_eq!((m.output.as_str(), m.channels), ("/out/Comp 1_[#####].tga", Channels::Rgba));
|
||||
let json = serde_json::to_string(&m).unwrap();
|
||||
assert_eq!(serde_json::from_str::<OutputModule>(&json).unwrap().format, f);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hevc_av1_codec_options() {
|
||||
assert_eq!(OutputFormat::from_name("H.265"), Some(OutputFormat::Hevc));
|
||||
|
||||
Reference in New Issue
Block a user