Add frame interpolation presets in Filters

This commit is contained in:
Stu Leak 2025-12-20 20:25:21 -05:00
parent 2f9995d8f1
commit 28e2f40b75
3 changed files with 80 additions and 4 deletions

View File

@ -835,6 +835,7 @@ This file tracks completed features, fixes, and milestones.
- ✅ Reduced module video pane minimum sizes to allow GNOME window snapping
- ✅ Added cache/temp directory setting with SSD recommendation and override
- ✅ Snippet defaults now use conversion settings (not Match Source)
- ✅ Added frame interpolation presets to Filters and wired filter chain to Upscale
- ✅ Stabilized video seeking and embedded rendering
- ✅ Improved player window positioning
- ✅ Fixed clear video functionality

View File

@ -67,6 +67,7 @@ This file tracks upcoming features, improvements, and known issues.
- Snippet UI rearranged into Convert Snippet / Batch / Options with context-sensitive visibility
- Reduce module video pane min sizes to allow GNOME snapping
- Cache/temp directory setting with SSD recommendation
- Frame interpolation presets in Filters with Upscale linkage
*Last Updated: 2025-12-20*

82
main.go
View File

@ -801,6 +801,9 @@ type appState struct {
filterFlipV bool
filterGrayscale bool
filterActiveChain []string // Active filter chain
filterInterpEnabled bool
filterInterpPreset string
filterInterpFPS string
// Upscale module state
upscaleFile *videoSource
@ -12563,6 +12566,37 @@ func buildFiltersView(state *appState) fyne.CanvasObject {
state.filterSharpness = 0.0 // 0.0 to 5.0
state.filterDenoise = 0.0 // 0.0 to 10.0
}
if state.filterInterpPreset == "" {
state.filterInterpPreset = "Balanced"
}
if state.filterInterpFPS == "" {
state.filterInterpFPS = "60"
}
buildFilterChain := func() {
var chain []string
if state.filterInterpEnabled {
fps := state.filterInterpFPS
if fps == "" {
fps = "60"
}
var filter string
switch state.filterInterpPreset {
case "Ultra Fast":
filter = fmt.Sprintf("minterpolate=fps=%s:mi_mode=blend", fps)
case "Fast":
filter = fmt.Sprintf("minterpolate=fps=%s:mi_mode=duplicate", fps)
case "High Quality":
filter = fmt.Sprintf("minterpolate=fps=%s:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1:search_param=32", fps)
case "Maximum Quality":
filter = fmt.Sprintf("minterpolate=fps=%s:mi_mode=mci:mc_mode=aobmc:me_mode=bidir:vsbmc=1:search_param=64", fps)
default: // Balanced
filter = fmt.Sprintf("minterpolate=fps=%s:mi_mode=mci:mc_mode=obmc:me_mode=bidir:me=epzs:search_param=16:vsbmc=0", fps)
}
chain = append(chain, filter)
}
state.filterActiveChain = chain
}
// File label
fileLabel := widget.NewLabel("No file loaded")
@ -12607,8 +12641,8 @@ func buildFiltersView(state *appState) fyne.CanvasObject {
upscaleNavBtn := widget.NewButton("Send to Upscale →", func() {
if state.filtersFile != nil {
state.upscaleFile = state.filtersFile
// TODO: Transfer active filter chain to upscale
// state.upscaleFilterChain = state.filterActiveChain
buildFilterChain()
state.upscaleFilterChain = append([]string{}, state.filterActiveChain...)
}
state.showUpscaleView()
})
@ -12656,14 +12690,50 @@ func buildFiltersView(state *appState) fyne.CanvasObject {
widget.NewCheck("Grayscale", func(b bool) { state.filterGrayscale = b }),
))
// Frame Interpolation Section
interpEnabledCheck := widget.NewCheck("Enable Frame Interpolation", func(checked bool) {
state.filterInterpEnabled = checked
buildFilterChain()
})
interpEnabledCheck.SetChecked(state.filterInterpEnabled)
interpPresetSelect := widget.NewSelect([]string{"Ultra Fast", "Fast", "Balanced", "High Quality", "Maximum Quality"}, func(val string) {
state.filterInterpPreset = val
buildFilterChain()
})
interpPresetSelect.SetSelected(state.filterInterpPreset)
interpFPSSelect := widget.NewSelect([]string{"24", "30", "50", "59.94", "60"}, func(val string) {
state.filterInterpFPS = val
buildFilterChain()
})
interpFPSSelect.SetSelected(state.filterInterpFPS)
interpHint := widget.NewLabel("Balanced preset is recommended; higher presets are CPU-intensive.")
interpHint.TextStyle = fyne.TextStyle{Italic: true}
interpHint.Wrapping = fyne.TextWrapWord
interpSection := widget.NewCard("Frame Interpolation (Minterpolate)", "", container.NewVBox(
widget.NewLabel("Generate smoother motion by interpolating new frames"),
interpEnabledCheck,
container.NewGridWithColumns(2,
widget.NewLabel("Preset:"),
interpPresetSelect,
widget.NewLabel("Target FPS:"),
interpFPSSelect,
),
interpHint,
))
buildFilterChain()
// Apply button
applyBtn := widget.NewButton("Apply Filters", func() {
if state.filtersFile == nil {
dialog.ShowInformation("No Video", "Please load a video first.", state.window)
return
}
// TODO: Implement filter application
dialog.ShowInformation("Coming Soon", "Filter application will be implemented soon.", state.window)
buildFilterChain()
dialog.ShowInformation("Filters", "Filters are now configured and will be applied when sent to Upscale.", state.window)
})
applyBtn.Importance = widget.HighImportance
@ -12680,6 +12750,7 @@ func buildFiltersView(state *appState) fyne.CanvasObject {
colorSection,
enhanceSection,
transformSection,
interpSection,
creativeSection,
applyBtn,
)
@ -12743,6 +12814,9 @@ func buildUpscaleView(state *appState) fyne.CanvasObject {
if !state.upscaleAIAvailable {
state.upscaleAIAvailable = checkAIUpscaleAvailable()
}
if len(state.filterActiveChain) > 0 {
state.upscaleFilterChain = append([]string{}, state.filterActiveChain...)
}
// File label
fileLabel := widget.NewLabel("No file loaded")