mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-24 10:09:32 -06:00
Merge #2223: Composition Settings: Resolution menu with the frame size and memory at that resolution (@marcoarnulfo)
This commit is contained in:
@@ -26,9 +26,7 @@ pub(crate) fn render<'g>(e: &mut Enc<'g>, r: &Renderer, comp_id: ItemId, t: Tick
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let ctx = r.eval_ctx(comp_id, t)?;
|
let ctx = r.eval_ctx(comp_id, t)?;
|
||||||
let s = r.opts.scale;
|
let (w, h) = effectcraft_render::frame_size(ctx.comp.width, ctx.comp.height, r.opts.scale);
|
||||||
let w = ((ctx.comp.width as f64 * s).round() as u32).max(1);
|
|
||||||
let h = ((ctx.comp.height as f64 * s).round() as u32).max(1);
|
|
||||||
if r.depth() > 16 || !e.g.fits(w, h) {
|
if r.depth() > 16 || !e.g.fits(w, h) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -981,8 +981,7 @@ impl<'a> Renderer<'a> {
|
|||||||
pub fn comp_frame_cpu(&self, comp_id: ItemId, t: Tick) -> Image {
|
pub fn comp_frame_cpu(&self, comp_id: ItemId, t: Tick) -> Image {
|
||||||
let Some(comp) = self.project.comp(comp_id) else { return Image::new(1, 1) };
|
let Some(comp) = self.project.comp(comp_id) else { return Image::new(1, 1) };
|
||||||
let s = self.opts.scale;
|
let s = self.opts.scale;
|
||||||
let w = ((comp.width as f64 * s).round() as u32).max(1);
|
let (w, h) = frame_size(comp.width, comp.height, s);
|
||||||
let h = ((comp.height as f64 * s).round() as u32).max(1);
|
|
||||||
// Region of interest on a comp with 3D layers: render the frame and crop it. A region
|
// Region of interest on a comp with 3D layers: render the frame and crop it. A region
|
||||||
// reaching past the comp frame (the Extended Viewer) renders Classic 3D natively — the
|
// reaching past the comp frame (the Extended Viewer) renders Classic 3D natively — the
|
||||||
// planes project through the offset camera — and Advanced 3D as the frame, padded.
|
// planes project through the offset camera — and Advanced 3D as the frame, padded.
|
||||||
@@ -2389,6 +2388,12 @@ pub fn extended_region(w: u32, h: u32, visible: [f64; 4], margin: f64) -> Option
|
|||||||
Some([x0, y0, x1 - x0, y1 - y0])
|
Some([x0, y0, x1 - x0, y1 - y0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The size of a `width` x `height` comp frame rendered at `scale` (both compositors, and the
|
||||||
|
/// frame size Composition Settings shows for a Resolution).
|
||||||
|
pub fn frame_size(width: u32, height: u32, scale: f64) -> (u32, u32) {
|
||||||
|
(((width as f64 * scale).round() as u32).max(1), ((height as f64 * scale).round() as u32).max(1))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn render_frame(project: &Project, comp: ItemId, t: Tick, scale: f64) -> Image {
|
pub fn render_frame(project: &Project, comp: ItemId, t: Tick, scale: f64) -> Image {
|
||||||
Renderer::new(project, &NoFootage, RenderOpts { scale, ..Default::default() }).comp_frame(comp, t)
|
Renderer::new(project, &NoFootage, RenderOpts { scale, ..Default::default() }).comp_frame(comp, t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -298,6 +298,7 @@ const JAPANESE: &[(&str, &str)] = &[
|
|||||||
("Frame Aspect Ratio: {}", "フレームアスペクト比:{}"),
|
("Frame Aspect Ratio: {}", "フレームアスペクト比:{}"),
|
||||||
("Frame Blending: {}", "フレームブレンド:{}"),
|
("Frame Blending: {}", "フレームブレンド:{}"),
|
||||||
("Frame Rate:", "フレームレート:"),
|
("Frame Rate:", "フレームレート:"),
|
||||||
|
("Resolution:", "解像度:"),
|
||||||
("Frame Render Time {}ms", "フレームのレンダリング時間 {}ms"),
|
("Frame Render Time {}ms", "フレームのレンダリング時間 {}ms"),
|
||||||
("Frame Target:", "フレームターゲット:"),
|
("Frame Target:", "フレームターゲット:"),
|
||||||
("Freeze", "フリーズ"),
|
("Freeze", "フリーズ"),
|
||||||
@@ -1033,6 +1034,7 @@ const SIMPLIFIED_CHINESE: &[(&str, &str)] = &[
|
|||||||
("Frame Aspect Ratio: {}", "帧长宽比:{}"),
|
("Frame Aspect Ratio: {}", "帧长宽比:{}"),
|
||||||
("Frame Blending: {}", "帧混合:{}"),
|
("Frame Blending: {}", "帧混合:{}"),
|
||||||
("Frame Rate:", "帧速率:"),
|
("Frame Rate:", "帧速率:"),
|
||||||
|
("Resolution:", "分辨率:"),
|
||||||
("Frame Render Time {}ms", "帧渲染时间 {} 毫秒"),
|
("Frame Render Time {}ms", "帧渲染时间 {} 毫秒"),
|
||||||
("Frame Target:", "帧目标:"),
|
("Frame Target:", "帧目标:"),
|
||||||
("Freeze", "冻结"),
|
("Freeze", "冻结"),
|
||||||
@@ -1773,6 +1775,7 @@ const TRADITIONAL_CHINESE: &[(&str, &str)] = &[
|
|||||||
("Frame Aspect Ratio: {}", "影格長寬比:{}"),
|
("Frame Aspect Ratio: {}", "影格長寬比:{}"),
|
||||||
("Frame Blending: {}", "影格混合:{}"),
|
("Frame Blending: {}", "影格混合:{}"),
|
||||||
("Frame Rate:", "影格速率:"),
|
("Frame Rate:", "影格速率:"),
|
||||||
|
("Resolution:", "解析度:"),
|
||||||
("Frame Render Time {}ms", "影格算圖時間 {} 毫秒"),
|
("Frame Render Time {}ms", "影格算圖時間 {} 毫秒"),
|
||||||
("Frame Target:", "幀目標:"),
|
("Frame Target:", "幀目標:"),
|
||||||
("Freeze", "凍結"),
|
("Freeze", "凍結"),
|
||||||
@@ -2467,6 +2470,7 @@ const UKRAINIAN: &[(&str, &str)] = &[
|
|||||||
("Frame Aspect Ratio: {}", "Пропорції кадру: {}"),
|
("Frame Aspect Ratio: {}", "Пропорції кадру: {}"),
|
||||||
("Frame Blending: {}", "Змішування кадрів: {}"),
|
("Frame Blending: {}", "Змішування кадрів: {}"),
|
||||||
("Frame Rate:", "Частота кадрів:"),
|
("Frame Rate:", "Частота кадрів:"),
|
||||||
|
("Resolution:", "Роздільність:"),
|
||||||
("Frame Render Time {}ms", "Час рендерингу кадру {} мс"),
|
("Frame Render Time {}ms", "Час рендерингу кадру {} мс"),
|
||||||
("Frame Target:", "Цільовий фрейм:"),
|
("Frame Target:", "Цільовий фрейм:"),
|
||||||
("Freeze", "Заморозити"),
|
("Freeze", "Заморозити"),
|
||||||
@@ -3027,6 +3031,7 @@ const PORTUGUESE: &[(&str, &str)] = &[
|
|||||||
("Frame Aspect Ratio: {}", "Proporção do quadro: {}"),
|
("Frame Aspect Ratio: {}", "Proporção do quadro: {}"),
|
||||||
("Frame Blending: {}", "Mesclagem de quadros: {}"),
|
("Frame Blending: {}", "Mesclagem de quadros: {}"),
|
||||||
("Frame Rate:", "Taxa de quadros:"),
|
("Frame Rate:", "Taxa de quadros:"),
|
||||||
|
("Resolution:", "Resolução:"),
|
||||||
("Frame Render Time {}ms", "Tempo de renderização do quadro {}ms"),
|
("Frame Render Time {}ms", "Tempo de renderização do quadro {}ms"),
|
||||||
("Frame Target:", "Destino do quadro:"),
|
("Frame Target:", "Destino do quadro:"),
|
||||||
("Grab bag", "Lista"),
|
("Grab bag", "Lista"),
|
||||||
@@ -3485,6 +3490,7 @@ const CZECH: &[(&str, &str)] = &[
|
|||||||
("Frame Aspect Ratio: {}", "Poměr stran snímku: {}"),
|
("Frame Aspect Ratio: {}", "Poměr stran snímku: {}"),
|
||||||
("Frame Blending: {}", "Prolnutí snímků: {}"),
|
("Frame Blending: {}", "Prolnutí snímků: {}"),
|
||||||
("Frame Rate:", "Snímková frekvence:"),
|
("Frame Rate:", "Snímková frekvence:"),
|
||||||
|
("Resolution:", "Rozlišení:"),
|
||||||
("Frame Render Time {}ms", "Čas vykreslení snímku {}ms"),
|
("Frame Render Time {}ms", "Čas vykreslení snímku {}ms"),
|
||||||
("Frame Target:", "Cílový rámec:"),
|
("Frame Target:", "Cílový rámec:"),
|
||||||
("Freeze", "Zmrazit"),
|
("Freeze", "Zmrazit"),
|
||||||
@@ -4048,6 +4054,7 @@ const SPANISH: &[(&str, &str)] = &[
|
|||||||
("Frame Aspect Ratio: {}", "Relación de aspecto del fotograma: {}"),
|
("Frame Aspect Ratio: {}", "Relación de aspecto del fotograma: {}"),
|
||||||
("Frame Blending: {}", "Fusión de fotogramas: {}"),
|
("Frame Blending: {}", "Fusión de fotogramas: {}"),
|
||||||
("Frame Rate:", "Velocidad de fotogramas:"),
|
("Frame Rate:", "Velocidad de fotogramas:"),
|
||||||
|
("Resolution:", "Resolución:"),
|
||||||
("Frame Render Time {}ms", "Tiempo de procesamiento del fotograma {}ms"),
|
("Frame Render Time {}ms", "Tiempo de procesamiento del fotograma {}ms"),
|
||||||
("Frame Target:", "Marco de destino:"),
|
("Frame Target:", "Marco de destino:"),
|
||||||
("Freeze", "Congelar"),
|
("Freeze", "Congelar"),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
use crate::i18n::{tr, tr_args};
|
use crate::i18n::{tr, tr_args};
|
||||||
use crate::icons::Icon;
|
use crate::icons::Icon;
|
||||||
use effectcraft_engine::prefs::CompPreset;
|
use effectcraft_engine::prefs::CompPreset;
|
||||||
|
use effectcraft_engine::project::Resolution;
|
||||||
use effectcraft_engine::time::{FrameRate, format_timecode_frames, parse_timecode};
|
use effectcraft_engine::time::{FrameRate, format_timecode_frames, parse_timecode};
|
||||||
use egui::{Color32, vec2};
|
use egui::{Color32, vec2};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
@@ -94,6 +95,9 @@ pub struct CompDraft {
|
|||||||
pub preserve_frame_rate: bool,
|
pub preserve_frame_rate: bool,
|
||||||
pub preserve_resolution: bool,
|
pub preserve_resolution: bool,
|
||||||
pub advanced_3d: bool,
|
pub advanced_3d: bool,
|
||||||
|
/// The comp's Resolution (the Composition panel's popup): saved with the project but no undo
|
||||||
|
/// step, so OK sets it apart from `comp.new` / `comp.settings`.
|
||||||
|
pub resolution: Resolution,
|
||||||
pub tab: Tab,
|
pub tab: Tab,
|
||||||
/// Timecode text being edited (start, duration).
|
/// Timecode text being edited (start, duration).
|
||||||
pub start_tc: Option<String>,
|
pub start_tc: Option<String>,
|
||||||
@@ -122,6 +126,7 @@ impl Default for CompDraft {
|
|||||||
preserve_frame_rate: false,
|
preserve_frame_rate: false,
|
||||||
preserve_resolution: false,
|
preserve_resolution: false,
|
||||||
advanced_3d: false,
|
advanced_3d: false,
|
||||||
|
resolution: Resolution::Auto,
|
||||||
tab: Tab::Basic,
|
tab: Tab::Basic,
|
||||||
start_tc: None,
|
start_tc: None,
|
||||||
dur_tc: None,
|
dur_tc: None,
|
||||||
@@ -376,9 +381,30 @@ pub fn show(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label("");
|
// As in After Effects, the Resolution menu (the Composition panel's popup) with
|
||||||
let mb = d.width as f64 * d.height as f64 * 4.0 / (1024.0 * 1024.0);
|
// the frame it renders beside it.
|
||||||
ui.label(egui::RichText::new(tr_args("{} x {}, {}MB per 8bpc frame", &[&d.width, &d.height, &format!("{mb:.1}")])).color(Color32::GRAY));
|
ui.label(tr("Resolution:"));
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
let r = egui::ComboBox::from_id_salt("comp-resolution").width(90.0).selected_text(tr(d.resolution.label())).show_ui(ui, |ui| {
|
||||||
|
for res in Resolution::ALL {
|
||||||
|
ui.selectable_value(&mut d.resolution, res, tr(res.label()));
|
||||||
|
}
|
||||||
|
let custom = matches!(d.resolution, Resolution::Custom(_));
|
||||||
|
if ui.selectable_label(custom, tr("Custom")).clicked() && !custom {
|
||||||
|
d.resolution = Resolution::Custom(5);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
auto.add("dialog.comp.resolution", r.response.rect, "Resolution");
|
||||||
|
if let Resolution::Custom(n) = d.resolution {
|
||||||
|
let mut n = n;
|
||||||
|
let r = ui.add(crate::widgets::drag_value(egui::DragValue::new(&mut n).range(2..=40)));
|
||||||
|
auto.add("dialog.comp.resolutionFactor", r.rect, "Custom Resolution");
|
||||||
|
d.resolution = Resolution::Custom(n);
|
||||||
|
}
|
||||||
|
let (w, h) = frame_at(d.width, d.height, d.resolution);
|
||||||
|
let mb = w as f64 * h as f64 * 4.0 / (1024.0 * 1024.0);
|
||||||
|
ui.label(egui::RichText::new(tr_args("{} x {}, {}MB per 8bpc frame", &[&w, &h, &format!("{mb:.1}")])).color(Color32::GRAY));
|
||||||
|
});
|
||||||
ui.end_row();
|
ui.end_row();
|
||||||
ui.label(tr("Start Timecode:"));
|
ui.label(tr("Start Timecode:"));
|
||||||
let r = ui.horizontal(|ui| timecode_field(ui, &mut d.start_tc, &mut d.start, rate, i64::MIN / 4)).inner;
|
let r = ui.horizontal(|ui| timecode_field(ui, &mut d.start_tc, &mut d.start, rate, i64::MIN / 4)).inner;
|
||||||
@@ -486,10 +512,26 @@ pub fn params(d: &CompDraft) -> serde_json::Value {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The frame size the comp renders at `res` (Auto follows the magnification, so the dialog shows
|
||||||
|
/// the full frame for it).
|
||||||
|
pub fn frame_at(w: u32, h: u32, res: Resolution) -> (u32, u32) {
|
||||||
|
effectcraft_render::frame_size(w, h, 1.0 / f64::from(res.factor().unwrap_or(1).max(1)))
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn frame_size_follows_the_resolution() {
|
||||||
|
// The renderer's size, which rounds: 1081 / 3 = 360.33 and 1920 / 7 = 274.3.
|
||||||
|
assert_eq!(frame_at(1920, 1081, Resolution::Third), (640, 360));
|
||||||
|
assert_eq!(frame_at(1920, 1080, Resolution::Custom(7)), (274, 154));
|
||||||
|
assert_eq!(frame_at(1920, 1081, Resolution::Half), (960, 541));
|
||||||
|
assert_eq!(frame_at(1920, 1080, Resolution::Full), (1920, 1080));
|
||||||
|
assert_eq!(frame_at(1920, 1080, Resolution::Auto), (1920, 1080));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn aspect_labels_match_ae() {
|
fn aspect_labels_match_ae() {
|
||||||
assert_eq!(aspect_label(1920.0, 1080.0), "16:9 (1.78)");
|
assert_eq!(aspect_label(1920.0, 1080.0), "16:9 (1.78)");
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ pub fn open_comp_settings_for(app: &mut EffectcraftApp, cid: ItemId) -> Result<(
|
|||||||
preserve_frame_rate: c.preserve_frame_rate,
|
preserve_frame_rate: c.preserve_frame_rate,
|
||||||
preserve_resolution: c.preserve_resolution,
|
preserve_resolution: c.preserve_resolution,
|
||||||
advanced_3d: c.renderer == effectcraft_engine::project::Renderer::Advanced3D,
|
advanced_3d: c.renderer == effectcraft_engine::project::Renderer::Advanced3D,
|
||||||
|
resolution: c.resolution,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
app.dialog_state.editing_existing = true;
|
app.dialog_state.editing_existing = true;
|
||||||
@@ -410,6 +411,11 @@ fn comp_settings(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens, exis
|
|||||||
m.insert("comp".into(), json!(cid.0));
|
m.insert("comp".into(), json!(cid.0));
|
||||||
}
|
}
|
||||||
let r = if existing { app.session.execute("comp.settings", params) } else { app.session.execute("comp.new", params) };
|
let r = if existing { app.session.execute("comp.settings", params) } else { app.session.execute("comp.new", params) };
|
||||||
|
let cid = if existing { app.dialog_state.editing_comp.map(|c| c.0) } else { r.as_ref().ok().and_then(|v| v["comp"].as_u64()) };
|
||||||
|
let r = match cid {
|
||||||
|
Some(cid) if r.is_ok() => set_resolution(app, cid, app.dialog_state.comp.resolution).and(r),
|
||||||
|
_ => r,
|
||||||
|
};
|
||||||
if !existing && r.is_ok() {
|
if !existing && r.is_ok() {
|
||||||
app.dialog_state.last_new_comp = Some(app.dialog_state.comp.clone());
|
app.dialog_state.last_new_comp = Some(app.dialog_state.comp.clone());
|
||||||
}
|
}
|
||||||
@@ -422,6 +428,15 @@ fn comp_settings(app: &mut EffectcraftApp, ctx: &egui::Context, t: &Tokens, exis
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The Resolution chosen in Composition Settings, set as the Composition panel's popup sets it:
|
||||||
|
/// saved with the project but no undo step, so it stays out of `comp.new` / `comp.settings`.
|
||||||
|
fn set_resolution(app: &mut EffectcraftApp, cid: u64, r: effectcraft_engine::project::Resolution) -> effectcraft_engine::Result<Value> {
|
||||||
|
match r.factor() {
|
||||||
|
None => app.session.execute("view.res.auto", json!({"comp": cid})),
|
||||||
|
Some(n) => app.session.execute("view.res.custom", json!({"comp": cid, "factor": n})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// One frame of the open dialog with `key` pressed.
|
/// One frame of the open dialog with `key` pressed.
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) fn press_key(app: &mut EffectcraftApp, ctx: &egui::Context, key: egui::Key, modifiers: egui::Modifiers) {
|
pub(crate) fn press_key(app: &mut EffectcraftApp, ctx: &egui::Context, key: egui::Key, modifiers: egui::Modifiers) {
|
||||||
@@ -829,6 +844,59 @@ mod tests {
|
|||||||
assert_eq!(selected(&ctx), Some(0..app.dialog_state.solid_name.chars().count()));
|
assert_eq!(selected(&ctx), Some(0..app.dialog_state.solid_name.chars().count()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// One frame of Composition Settings (or New Composition) with Enter pressed: OK.
|
||||||
|
fn press_ok(app: &mut EffectcraftApp, ctx: &egui::Context, existing: bool) {
|
||||||
|
let t = app.tokens;
|
||||||
|
let mut out = ctx.run_ui(Default::default(), |ui| {
|
||||||
|
app.auto.begin_frame();
|
||||||
|
ui.input_mut(|i| {
|
||||||
|
i.events.push(egui::Event::Key { key: egui::Key::Enter, physical_key: None, pressed: true, repeat: false, modifiers: Default::default() })
|
||||||
|
});
|
||||||
|
comp_settings(app, ui.ctx(), &t, existing);
|
||||||
|
});
|
||||||
|
out.textures_delta.clear();
|
||||||
|
assert!(app.dialog.is_none(), "OK closed the dialog");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Resolution menu sets the comp's Resolution on OK. As from the Composition panel's popup
|
||||||
|
/// it is no undo step: the settings are one step, and undoing them keeps the Resolution.
|
||||||
|
#[test]
|
||||||
|
fn composition_settings_resolution_applies_on_ok_outside_the_undo_history() {
|
||||||
|
use effectcraft_engine::project::Resolution;
|
||||||
|
let (mut app, ctx) = app();
|
||||||
|
let cid = app.session.active_comp_id().unwrap();
|
||||||
|
app.session.execute("view.res.third", json!({})).unwrap();
|
||||||
|
open_comp_settings(&mut app).unwrap();
|
||||||
|
assert_eq!(app.dialog_state.comp.resolution, Resolution::Third, "the dialog opens on the comp's Resolution");
|
||||||
|
frame(&mut app, &ctx);
|
||||||
|
assert!(app.auto.find("dialog.comp.resolution").is_some());
|
||||||
|
let steps = app.session.history.undo.len();
|
||||||
|
(app.dialog_state.comp.resolution, app.dialog_state.comp.width) = (Resolution::Half, 700);
|
||||||
|
app.dialog_state.comp.lock_aspect = false;
|
||||||
|
press_ok(&mut app, &ctx, true);
|
||||||
|
let c = app.session.project.comp(cid).unwrap();
|
||||||
|
assert_eq!((c.width, c.resolution), (700, Resolution::Half));
|
||||||
|
assert_eq!(app.session.history.undo.len(), steps + 1, "Composition Settings is one undo step");
|
||||||
|
app.session.execute("edit.undo", json!({})).unwrap();
|
||||||
|
let c = app.session.project.comp(cid).unwrap();
|
||||||
|
assert_eq!((c.width, c.resolution), (640, Resolution::Half), "undo keeps the Resolution");
|
||||||
|
// Only the Resolution changed: still set, and no extra step.
|
||||||
|
open_comp_settings(&mut app).unwrap();
|
||||||
|
app.dialog_state.comp.resolution = Resolution::Custom(6);
|
||||||
|
let steps = app.session.history.undo.len();
|
||||||
|
press_ok(&mut app, &ctx, true);
|
||||||
|
assert_eq!(app.session.project.comp(cid).unwrap().resolution, Resolution::Custom(6));
|
||||||
|
assert_eq!(app.session.history.undo.len(), steps, "no undo step");
|
||||||
|
// New Composition makes its comp at the menu's Resolution; the comp the viewer showed keeps its own.
|
||||||
|
open_new_comp(&mut app);
|
||||||
|
app.dialog_state.comp.resolution = Resolution::Quarter;
|
||||||
|
press_ok(&mut app, &ctx, false);
|
||||||
|
let new = app.session.active_comp_id().unwrap();
|
||||||
|
assert_ne!(new, cid);
|
||||||
|
assert_eq!(app.session.project.comp(new).unwrap().resolution, Resolution::Quarter);
|
||||||
|
assert_eq!(app.session.project.comp(cid).unwrap().resolution, Resolution::Custom(6));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn composition_settings_register_every_control_and_new_comp_remembers() {
|
fn composition_settings_register_every_control_and_new_comp_remembers() {
|
||||||
let (mut app, ctx) = app();
|
let (mut app, ctx) = app();
|
||||||
|
|||||||
@@ -688,8 +688,10 @@ Fixes from measured reports:
|
|||||||
step (undo and redo keep it) and keeps the comp's cached frames; projects saved before open
|
step (undo and redo keep it) and keeps the comp's cached frames; projects saved before open
|
||||||
with Auto. View ▸ Resolution and the panel's popup run `view.res.full|half|third|quarter|auto`
|
with Auto. View ▸ Resolution and the panel's popup run `view.res.full|half|third|quarter|auto`
|
||||||
and `view.res.custom {factor}`, which now work headless too; `comp.info` reports `resolution`
|
and `view.res.custom {factor}`, which now work headless too; `comp.info` reports `resolution`
|
||||||
and `resolutionFactor` (#417). Not yet: the magnification is not saved per comp, and the
|
and `resolutionFactor` (#417). Composition Settings ▸ Basic has the same Resolution menu, with
|
||||||
Composition Settings dialog has no Resolution popup.
|
the frame size and memory per 8bpc frame at that resolution beside it; OK sets it as the popup
|
||||||
|
does, outside the undo step of the other settings (#2167). Not yet: the magnification is not
|
||||||
|
saved per comp.
|
||||||
|
|
||||||
### Update: plug-ins and extensions as in After Effects; ease presets become a ScriptUI panel
|
### Update: plug-ins and extensions as in After Effects; ease presets become a ScriptUI panel
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user