Merge pull request #619 from MarekSpodBiedry/issue-546

This commit is contained in:
Claude committed 2026-10-10 18:49:51 +00:00
commit 6538fb175f
2 files changed
+86 -1

No files matched your search

+5 -1
View File
@@ -522,10 +522,14 @@ fn locked_pane(app: &mut EffectcraftApp, ui: &mut egui::Ui, full: Rect, bg: Colo
let cam = (lv.view != View3D::ActiveCamera && comp.has_3d())
.then(|| app.session.state.views3d.get(&lv.comp).cloned().unwrap_or_default().cam(lv.view, comp.width as f64, comp.height as f64).state());
let dc = effectcraft_engine::viewer::DisplayColor::of(&app.session);
// The display conversion's inputs, keyed as in the main viewer's texture caches.
let sim = app.session.state.viewer.simulation;
let custom = if sim.profile == effectcraft_engine::viewer::SimProfile::MyCustom { format!("{:?}", app.session.prefs.custom_rgb) } else { String::new() };
let dc_key = format!("{cam:?}{}{sim:?}{}{custom}", dc.is_some(), app.session.prefs.previews.display_profile);
let key = {
use std::hash::{Hash, Hasher};
let mut h = std::collections::hash_map::DefaultHasher::new();
(app.session.revision, lv.comp.0, t.0, scale.to_bits(), format!("{cam:?}{}", dc.is_some())).hash(&mut h);
(app.session.revision, lv.comp.0, t.0, scale.to_bits(), dc_key).hash(&mut h);
h.finish()
};
let id = egui::Id::new("viewer-locked");
+81
View File
@@ -0,0 +1,81 @@
//! The split locked viewer re-converts its pixels when the display profile or the output
//! simulation changes (egui_kittest, texture uploads captured).
use effectcraft_engine::{Session, render::RenderOpts, viewer::DisplayColor};
use effectcraft_ui_egui::EffectcraftApp;
use egui_kittest::{Harness, TestRenderer};
use serde_json::{Value, json};
use std::{cell::RefCell, collections::HashMap, rc::Rc};
type Images = Rc<RefCell<HashMap<egui::TextureId, egui::ColorImage>>>;
struct Capture(Images);
impl TestRenderer for Capture {
fn handle_delta(&mut self, delta: &mut egui::TexturesDelta) {
for (id, ops) in std::mem::take(&mut delta.set) {
for op in ops {
let egui::ImageData::Color(image) = op.image;
if op.pos.is_none() && image.size[0] <= 256 && image.size[1] <= 256 {
self.0.borrow_mut().insert(id, (*image).clone());
}
}
}
for id in std::mem::take(&mut delta.free) {
self.0.borrow_mut().remove(&id);
}
}
fn render(&mut self, _: &egui::Context, _: &egui::FullOutput) -> Result<image::RgbaImage, String> {
Err("Texture inspection only".into())
}
}
fn invoke(h: &mut Harness<'_, EffectcraftApp>, command: &str, params: Value) {
let ctx = h.ctx.clone();
effectcraft_ui_egui::menus::invoke(h.state_mut(), &ctx, command, params).unwrap();
h.run_steps(3);
}
fn locked_center(h: &Harness<'_, EffectcraftApp>, images: &Images) -> [u8; 4] {
let (_, texture) = h.ctx.data(|d| d.get_temp::<(u64, egui::TextureHandle)>(egui::Id::new("viewer-locked"))).unwrap();
let images = images.borrow();
let image = images.get(&texture.id()).unwrap();
image.pixels[(image.size[1] / 2) * image.size[0] + image.size[0] / 2].to_array()
}
fn expected_center(h: &mut Harness<'_, EffectcraftApp>) -> [u8; 4] {
let s = &mut h.state_mut().session;
let comp = s.active_comp_id().unwrap();
let image = s.render(comp, s.time(), RenderOpts::default());
let ci = effectcraft_ui_egui::frames::to_color_image(&image);
let mut pixels = vec![ci.pixels[(ci.size[1] / 2) * ci.size[0] + ci.size[0] / 2].to_array()];
DisplayColor::of(s).unwrap().apply(&mut pixels);
pixels[0]
}
#[test]
fn locked_viewer_follows_display_profile_and_simulation() {
let mut s = Session::default();
s.execute("comp.new", json!({"name": "Color", "width": 64, "height": 64, "duration": 1})).unwrap();
s.execute("layer.newSolid", json!({"name": "Plate", "color": "#406080", "width": 64, "height": 64})).unwrap();
s.execute("file.projectSettings", json!({"workingSpace": "srgb", "outputSpace": "rec2020", "renderer": "software"})).unwrap();
s.execute("prefs.set", json!({"key": "previews.displayProfile", "value": "srgb"})).unwrap();
s.execute("view.displayColorManagement", json!({"value": true})).unwrap();
let images = Images::default();
let mut h = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).renderer(Capture(images.clone())).build_eframe(|_| EffectcraftApp::new(s));
h.run_steps(3);
invoke(&mut h, "view.splitLockedViewer", json!({}));
let first = expected_center(&mut h);
assert_eq!(locked_center(&h, &images), first);
invoke(&mut h, "prefs.set", json!({"key": "previews.displayProfile", "value": "p3"}));
let after_display = expected_center(&mut h);
assert_ne!(after_display, first);
assert_eq!(locked_center(&h, &images), after_display);
invoke(&mut h, "view.simulateOutput", json!({"profile": "rec709", "preserveRgb": true}));
let after_sim = expected_center(&mut h);
assert_ne!(after_sim, after_display);
assert_eq!(locked_center(&h, &images), after_sim);
}