Skip to content

Commit

Permalink
Enable/disable effect preview + fixes 'n cleanups
Browse files Browse the repository at this point in the history
  • Loading branch information
valadaptive committed Dec 16, 2023
1 parent 6ef6ac0 commit ce241c5
Show file tree
Hide file tree
Showing 8 changed files with 289 additions and 149 deletions.
226 changes: 146 additions & 80 deletions crates/gui/src/bin/ntsc-rs-standalone.rs

Large diffs are not rendered by default.

85 changes: 57 additions & 28 deletions crates/gui/src/gst_utils/egui_sink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ use gstreamer::glib::once_cell::sync::Lazy;
use gstreamer::prelude::*;
use gstreamer::{glib, PadTemplate};
use gstreamer_video::subclass::prelude::*;
use ntscrs::settings::UseField;
use ntscrs::yiq_fielding::{Rgbx8, YiqField, YiqOwned, YiqView};
use ntscrs::yiq_fielding::{Rgbx8, YiqOwned, YiqView};
use std::fmt::Debug;
use std::sync::Mutex;

Expand All @@ -16,6 +15,15 @@ use super::ntscrs_filter::NtscFilterSettings;
#[boxed_type(name = "SinkTexture")]
pub struct SinkTexture(pub Option<TextureHandle>);

#[derive(Debug, Clone, Copy, PartialEq, glib::Boxed, Default)]
#[boxed_type(name = "VideoPreviewSetting")]
pub enum EffectPreviewSetting {
#[default]
Enabled,
Disabled,
SplitScreen(f64),
}

impl Debug for SinkTexture {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut t = f.debug_tuple("SinkTexture");
Expand All @@ -42,6 +50,8 @@ pub struct EguiSink {
ctx: Mutex<EguiCtx>,
#[property(get, set = Self::set_settings)]
settings: Mutex<NtscFilterSettings>,
#[property(get, set = Self::set_video_preview_mode)]
preview_mode: Mutex<EffectPreviewSetting>,

video_info: Mutex<Option<gstreamer_video::VideoInfo>>,
last_frame: Mutex<
Expand All @@ -62,6 +72,29 @@ impl EguiSink {
let _ = self.update_texture();
}

fn set_video_preview_mode(&self, value: EffectPreviewSetting) {
*self.preview_mode.lock().unwrap() = value;
let _ = self.update_texture();
}

fn apply_effect(&self, frame_num: usize, stride: usize, buf: &[u8], size: (usize, usize), image: &mut ColorImage) {
let settings = self.settings.lock().unwrap();
let field = settings.0.use_field.to_yiq_field(frame_num as usize);

let mut yiq = YiqOwned::from_strided_buffer::<Rgbx8>(
buf,
stride,
size.0,
size.1,
field,
);
let mut view = YiqView::from(&mut yiq);
settings
.0
.apply_effect_to_yiq(&mut view, frame_num as usize);
view.write_to_strided_buffer::<Rgbx8>(image.as_raw_mut(), size.0 * 4);
}

pub fn update_texture(&self) -> Result<(), gstreamer::FlowError> {
let mut tex = self.texture.lock().unwrap();
let vframe = self.last_frame.lock().unwrap();
Expand All @@ -73,32 +106,28 @@ impl EguiSink {

let stride = vframe.plane_stride()[0] as usize;

let settings = self.settings.lock().unwrap();
let field = match settings.0.use_field {
UseField::Alternating => {
if frame_num & 1 == 0 {
YiqField::Lower
} else {
YiqField::Upper
}
}
UseField::Upper => YiqField::Upper,
UseField::Lower => YiqField::Lower,
UseField::Both => YiqField::Both,
};

let mut yiq = YiqOwned::from_strided_buffer::<Rgbx8>(
vframe.plane_data(0).or(Err(gstreamer::FlowError::Error))?,
stride,
width,
height,
field,
);
let mut view = YiqView::from(&mut yiq);
settings
.0
.apply_effect_to_yiq(&mut view, *frame_num as usize);
view.write_to_strided_buffer::<Rgbx8>(image.as_raw_mut(), width * 4);
match *self.preview_mode.lock().unwrap() {
EffectPreviewSetting::Enabled => {
let buf = vframe.plane_data(0).or(Err(gstreamer::FlowError::Error))?;
self.apply_effect(*frame_num as usize, stride, buf, (width, height), &mut image);
},
#[allow(illegal_floating_point_literal_pattern)]
EffectPreviewSetting::Disabled | EffectPreviewSetting::SplitScreen(0f64) => {
// Copy directly to egui image when effect is disabled
let src_buf = vframe.plane_data(0).or(Err(gstreamer::FlowError::Error))?;
image.as_raw_mut().copy_from_slice(src_buf);
},
EffectPreviewSetting::SplitScreen(split) => {
let buf = vframe.plane_data(0).or(Err(gstreamer::FlowError::Error))?;
self.apply_effect(*frame_num as usize, stride, buf, (width, height), &mut image);

let split_boundary = (split * width as f64).round().clamp(0.0, width as f64) as usize;
let image_data = image.as_raw_mut();
image_data.chunks_exact_mut(width * 4).zip(buf.chunks_exact(width * 4)).for_each(|(img_row, vid_row)| {
img_row[split_boundary * 4..].copy_from_slice(&vid_row[split_boundary * 4..]);
});
},
}

tex.0
.as_mut()
Expand Down
16 changes: 2 additions & 14 deletions crates/gui/src/gst_utils/ntscrs_filter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ use gstreamer_video::subclass::prelude::*;
use gstreamer_video::VideoFormat;

use ntscrs::ntsc::NtscEffect;
use ntscrs::settings::UseField;
use ntscrs::yiq_fielding::{Bgrx8, Rgbx8, Xbgr8, Xrgb16, Xrgb8, YiqField, YiqOwned, YiqView};
use ntscrs::yiq_fielding::{Bgrx8, Rgbx8, Xbgr8, Xrgb16, Xrgb8, YiqOwned, YiqView};

#[derive(Clone, glib::Boxed, Default)]
#[boxed_type(name = "NtscFilterSettings")]
Expand Down Expand Up @@ -177,18 +176,7 @@ impl VideoFilterImpl for NtscFilter {
.clone()
.0;

let field = match settings.use_field {
UseField::Alternating => {
if frame & 1 == 0 {
YiqField::Lower
} else {
YiqField::Upper
}
}
UseField::Upper => YiqField::Upper,
UseField::Lower => YiqField::Lower,
UseField::Both => YiqField::Both,
};
let field = settings.use_field.to_yiq_field(frame as usize);

let mut yiq = match in_format {
VideoFormat::Rgbx | VideoFormat::Rgba => {
Expand Down
1 change: 1 addition & 0 deletions crates/gui/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod expression_parser;
pub mod gst_utils;
pub mod splitscreen;
pub mod timeline;
62 changes: 62 additions & 0 deletions crates/gui/src/splitscreen.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use std::f32;

use eframe::{
egui::{self, Sense, Widget},
emath::remap_clamp,
};

pub struct SplitScreen<'a> {
value: &'a mut f64,
}

impl<'a> SplitScreen<'a> {
pub fn new(value: &'a mut f64) -> Self {
Self {
value,
}
}
}

impl<'a> Widget for SplitScreen<'a> {
fn ui(self, ui: &mut egui::Ui) -> egui::Response {
let desired_size = ui.available_size();

let grab_radius = ui.style().interaction.resize_grab_radius_side;
let (id, rect) = ui.allocate_space(desired_size);

let x = rect.lerp_inside(egui::vec2(*self.value as f32, 0.0)).x;
let interact_rect = egui::Rect::from_min_max(egui::pos2(x - grab_radius, rect.top()), egui::pos2(x + grab_radius, rect.bottom()));

let mut response = ui.interact(interact_rect, id, Sense::drag());
if response.hovered() || response.dragged() {
ui.ctx().set_cursor_icon(egui::CursorIcon::ResizeHorizontal);
}

if let Some(pointer_position_2d) = response.interact_pointer_pos() {
let position = pointer_position_2d.x;
let normalized = remap_clamp(
position,
rect.x_range(),
0.0..=1.0,
);
*self.value = normalized as f64;
response.mark_changed();
}

if ui.is_rect_visible(rect) {
let visuals = ui.style().interact(&response);
let painter = ui.painter();


painter.line_segment([egui::pos2(x, rect.top()), egui::pos2(x, rect.bottom())], visuals.fg_stroke);

// Fill with red rectangle in debug mode
#[cfg(debug_assertions)]
if ui.ctx().debug_on_hover() && ui.interact(rect, id, Sense::hover()).hovered() {
painter.rect_filled(rect, egui::Rounding::ZERO, egui::Color32::from_rgba_unmultiplied(255, 0, 0, 64));
}
}

response
}
}
15 changes: 2 additions & 13 deletions crates/ntscrs/src/ntsc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use crate::{
filter::TransferFunction,
random::{Geometric, Seeder},
shift::{shift_row, shift_row_to, BoundaryHandling},
yiq_fielding::{YiqField, YiqOwned, YiqView},
yiq_fielding::{YiqOwned, YiqView},
};

pub use crate::settings::*;
Expand Down Expand Up @@ -1174,18 +1174,7 @@ impl NtscEffect {
}

pub fn apply_effect(&self, input_frame: &RgbImage, frame_num: usize) -> RgbImage {
let field = match self.use_field {
UseField::Alternating => {
if frame_num & 1 == 0 {
YiqField::Upper
} else {
YiqField::Lower
}
}
UseField::Upper => YiqField::Upper,
UseField::Lower => YiqField::Lower,
UseField::Both => YiqField::Both,
};
let field = self.use_field.to_yiq_field(frame_num);
let mut yiq = YiqOwned::from_image(input_frame, field);
let mut view = YiqView::from(&mut yiq);
self.apply_effect_to_yiq(&mut view, frame_num);
Expand Down
19 changes: 18 additions & 1 deletion crates/ntscrs/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::{
ops::RangeInclusive,
};

use crate::{FromPrimitive, ToPrimitive};
use crate::{FromPrimitive, ToPrimitive, yiq_fielding::YiqField};
use macros::FullSettings;
use tinyjson::JsonValue;

Expand All @@ -25,6 +25,23 @@ pub enum UseField {
Both,
}

impl UseField {
pub fn to_yiq_field(&self, frame_num: usize) -> YiqField {
match self {
UseField::Alternating => {
if frame_num & 1 == 0 {
YiqField::Lower
} else {
YiqField::Upper
}
}
UseField::Upper => YiqField::Upper,
UseField::Lower => YiqField::Lower,
UseField::Both => YiqField::Both,
}
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, FromPrimitive, ToPrimitive)]
pub enum LumaLowpass {
None,
Expand Down
14 changes: 1 addition & 13 deletions crates/openfx-plugin/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ use ntscrs::ToPrimitive;
use ntscrs::{
ntsc::NtscEffect,
yiq_fielding::{rgb_to_yiq, yiq_to_rgb, YiqField, YiqView},
settings::UseField,
};

include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
Expand Down Expand Up @@ -1062,18 +1061,7 @@ unsafe fn pixel_processing<S: Normalize + Sized, D: Normalize + Sized>(
let srcWidth = (srcBounds.x2 - srcBounds.x1) as usize;
let srcHeight = (srcBounds.y2 - srcBounds.y1) as usize;

let cur_field = match effect.use_field {
UseField::Alternating => {
if frame_num & 1 == 0 {
YiqField::Lower
} else {
YiqField::Upper
}
}
UseField::Upper => YiqField::Upper,
UseField::Lower => YiqField::Lower,
UseField::Both => YiqField::Both,
};
let cur_field = effect.use_field.to_yiq_field(frame_num);

let RowInfo {
row_lshift,
Expand Down

0 comments on commit ce241c5

Please sign in to comment.