mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-31 21:48:20 -06:00
Merge main and retain Cineon Settings in the extracted modules
This commit is contained in:
@@ -0,0 +1,99 @@
|
||||
use super::*;
|
||||
|
||||
impl Session {
|
||||
/// Render a comp frame with the session's footage + expression hosts.
|
||||
pub fn render(&self, comp: ItemId, t: Tick, opts: RenderOpts) -> Image {
|
||||
self.renderer(opts).comp_frame(comp, t)
|
||||
}
|
||||
|
||||
/// The renderer [`Session::render`] uses: the session's footage, expressions, layer cache and
|
||||
/// accelerator, and the render prefs (nested switches, draft shadows) over `opts`.
|
||||
pub fn renderer(&self, opts: RenderOpts) -> Renderer<'_> {
|
||||
let opts = RenderOpts { nested_switches: self.prefs.general.switches_affect_nested_comps, draft_shadows: self.prefs.three_d.realtime_shadows, ..opts };
|
||||
let mut r = Renderer::new(&self.project, self.footage.as_ref(), opts);
|
||||
r.expr = self.expr.as_deref();
|
||||
r.cache = Some(&self.layer_cache);
|
||||
r.accel = self.accel.as_deref();
|
||||
r
|
||||
}
|
||||
|
||||
/// Render a comp frame (comp time `t`) as opaque 8-bit RGBA over the comp background, scaled
|
||||
/// so the longest side is at most `max_side` pixels (0 = full size). Returns (width, height, rgba).
|
||||
pub fn render_rgba8(&self, comp: ItemId, t: Tick, max_side: u32) -> Result<(u32, u32, Vec<u8>)> {
|
||||
let c = self.project.comp(comp).ok_or(EngineError::NoComp)?;
|
||||
let long = c.width.max(c.height).max(1) as f64;
|
||||
let scale = if max_side == 0 { 1.0 } else { (max_side as f64 / long).min(1.0) };
|
||||
let img = self.render(comp, t, RenderOpts { scale, backend: effectcraft_render::Backend::Auto, ..Default::default() });
|
||||
Ok((img.width, img.height, img.to_rgba8_over(c.background)))
|
||||
}
|
||||
|
||||
/// A Project panel thumbnail of an item (a comp at `t`, footage at its first frame), longest
|
||||
/// side at most `max_side`, as a job that owns everything it reads: frontends run it off the
|
||||
/// UI thread (a big comp takes far longer than a frame). `None` for items without pixels.
|
||||
pub fn thumbnail_job(&self, item: ItemId, t: Tick, max_side: u32) -> Option<ThumbnailJob> {
|
||||
let it = self.project.item(item)?;
|
||||
let footage = self.footage.clone();
|
||||
match &it.kind {
|
||||
effectcraft_project::ItemKind::Comp(c) => {
|
||||
let (project, expr, cache) = (self.project.clone(), self.expr.clone(), self.layer_cache.clone());
|
||||
let long = c.width.max(c.height).max(1) as f64;
|
||||
let scale = if max_side == 0 { 1.0 } else { (max_side as f64 / long).min(1.0) };
|
||||
let opts = RenderOpts {
|
||||
scale,
|
||||
nested_switches: self.prefs.general.switches_affect_nested_comps,
|
||||
draft_shadows: self.prefs.three_d.realtime_shadows,
|
||||
..Default::default()
|
||||
};
|
||||
let bg = c.background;
|
||||
Some(Box::new(move || {
|
||||
let mut r = Renderer::new(&project, footage.as_ref(), opts);
|
||||
r.expr = expr.as_deref();
|
||||
r.cache = Some(&cache);
|
||||
let img = r.comp_frame(item, t);
|
||||
Some((img.width, img.height, img.to_rgba8_over(bg)))
|
||||
}))
|
||||
}
|
||||
effectcraft_project::ItemKind::Footage(f) if f.has_video => {
|
||||
let f = f.clone();
|
||||
Some(Box::new(move || {
|
||||
let img = effectcraft_render::cineon_frame(footage.frame(item, &f, Tick(0))?, &f);
|
||||
// Point-sample down to at most `max_side` on the long side.
|
||||
let step = (img.width.max(img.height) as f32 / max_side.max(1) as f32).ceil().max(1.0) as u32;
|
||||
let (w, h) = (img.width.div_ceil(step), img.height.div_ceil(step));
|
||||
let full = img.to_rgba8();
|
||||
let mut px = Vec::with_capacity((w * h * 4) as usize);
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let i = (((y * step) * img.width + x * step) * 4) as usize;
|
||||
px.extend_from_slice(&full[i..i + 4]);
|
||||
}
|
||||
}
|
||||
Some((w, h, px))
|
||||
}))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// [`Session::render_rgba8`], or with `transparent` the frame's own straight alpha (what a
|
||||
/// render with RGB + Alpha channels writes) instead of compositing over the background.
|
||||
pub fn render_rgba8_alpha(&self, comp: ItemId, t: Tick, max_side: u32, transparent: bool) -> Result<(u32, u32, Vec<u8>)> {
|
||||
if !transparent {
|
||||
return self.render_rgba8(comp, t, max_side);
|
||||
}
|
||||
let c = self.project.comp(comp).ok_or(EngineError::NoComp)?;
|
||||
let long = c.width.max(c.height).max(1) as f64;
|
||||
let scale = if max_side == 0 { 1.0 } else { (max_side as f64 / long).min(1.0) };
|
||||
let img = self.render(comp, t, RenderOpts { scale, backend: effectcraft_render::Backend::Auto, ..Default::default() });
|
||||
Ok((img.width, img.height, img.to_rgba8()))
|
||||
}
|
||||
|
||||
/// Make the media layer and the layer cache let go of what they decoded from `footage`'s
|
||||
/// files (it keys frames by path, so an overwritten file would keep showing its old pixels).
|
||||
pub(crate) fn forget_decoded(&self, footage: &effectcraft_project::Footage) {
|
||||
for p in std::iter::once(&footage.path).chain(&footage.sequence) {
|
||||
self.footage.forget(p);
|
||||
}
|
||||
self.layer_cache.clear();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user