Clamp menu logo scale and add preview

This commit is contained in:
Stu Leak 2026-01-06 23:42:14 -05:00
parent c3bd5a0baa
commit ea7cfbbf6a
2 changed files with 92 additions and 4 deletions

View File

@ -298,7 +298,7 @@ func buildMenuBackground(ctx context.Context, outputPath, title string, buttons
logoPath := resolveMenuLogoPath(logo)
if logoPath != "" {
posExpr := resolveMenuLogoPosition(logo, width, height)
scaleExpr := fmt.Sprintf("scale=iw*%.2f:ih*%.2f", resolveMenuLogoScale(logo), resolveMenuLogoScale(logo))
scaleExpr := resolveMenuLogoScaleExpr(logo, width, height)
args = append(args, "-i", logoPath)
filterExpr = fmt.Sprintf("[0:v]%s[bg];[1:v]%s[logo];[bg][logo]overlay=%s", filterChain, scaleExpr, posExpr)
}
@ -343,7 +343,7 @@ func buildDarkMenuBackground(ctx context.Context, outputPath, title string, butt
logoPath := resolveMenuLogoPath(logo)
if logoPath != "" {
posExpr := resolveMenuLogoPosition(logo, width, height)
scaleExpr := fmt.Sprintf("scale=iw*%.2f:ih*%.2f", resolveMenuLogoScale(logo), resolveMenuLogoScale(logo))
scaleExpr := resolveMenuLogoScaleExpr(logo, width, height)
args = append(args, "-i", logoPath)
filterExpr = fmt.Sprintf("[0:v]%s[bg];[1:v]%s[logo];[bg][logo]overlay=%s", filterChain, scaleExpr, posExpr)
}
@ -381,7 +381,7 @@ func buildPosterMenuBackground(ctx context.Context, outputPath, title string, bu
logoPath := resolveMenuLogoPath(logo)
if logoPath != "" {
posExpr := resolveMenuLogoPosition(logo, width, height)
scaleExpr := fmt.Sprintf("scale=iw*%.2f:ih*%.2f", resolveMenuLogoScale(logo), resolveMenuLogoScale(logo))
scaleExpr := resolveMenuLogoScaleExpr(logo, width, height)
args = append(args, "-i", logoPath)
filterExpr = fmt.Sprintf("[0:v]scale=%d:%d,%s[bg];[1:v]%s[logo];[bg][logo]overlay=%s", width, height, filterChain, scaleExpr, posExpr)
}
@ -573,6 +573,13 @@ func resolveMenuLogoScale(logo menuLogoOptions) float64 {
return logo.Scale
}
func resolveMenuLogoScaleExpr(logo menuLogoOptions, width, height int) string {
scale := resolveMenuLogoScale(logo)
maxW := float64(width) * 0.25
maxH := float64(height) * 0.25
return fmt.Sprintf("scale=w='min(iw*%.2f,%.0f)':h='min(ih*%.2f,%.0f)':force_original_aspect_ratio=decrease", scale, maxW, scale, maxH)
}
func resolveMenuLogoPosition(logo menuLogoOptions, width, height int) string {
margin := logo.Margin
if margin < 0 {

View File

@ -8,6 +8,7 @@ import (
"encoding/xml"
"errors"
"fmt"
"image"
"image/color"
"io"
"math"
@ -1030,6 +1031,78 @@ func buildAuthorMenuTab(state *appState) fyne.CanvasObject {
logoLabel := widget.NewLabel(state.authorMenuLogoPath)
logoLabel.Wrapping = fyne.TextWrapWord
logoPreview := canvas.NewImageFromFile("")
logoPreview.FillMode = canvas.ImageFillContain
logoPreview.SetMinSize(fyne.NewSize(220, 120))
logoPreviewLabel := widget.NewLabel("No logo selected")
logoPreviewLabel.Wrapping = fyne.TextWrapWord
logoPreviewSize := widget.NewLabel("")
logoPreviewSize.Wrapping = fyne.TextWrapWord
menuPreviewSize := func() (int, int) {
width := 720
height := 480
switch strings.ToUpper(strings.TrimSpace(state.authorRegion)) {
case "PAL":
height = 576
}
return width, height
}
updateLogoPreview := func() {
if !state.authorMenuLogoEnabled {
logoPreview.Hide()
logoPreviewLabel.SetText("Logo preview disabled")
logoPreviewSize.SetText("")
return
}
path := state.authorMenuLogoPath
if strings.TrimSpace(path) == "" {
path = filepath.Join("assets", "logo", "VT_Logo.png")
}
if _, err := os.Stat(path); err != nil {
logoPreview.Hide()
logoPreviewLabel.SetText("Logo file not found")
logoPreviewSize.SetText("")
return
}
logoPreview.Show()
logoPreviewLabel.SetText(filepath.Base(path))
logoPreview.File = path
logoPreview.Refresh()
file, err := os.Open(path)
if err != nil {
logoPreviewSize.SetText("")
return
}
defer file.Close()
cfg, _, err := image.DecodeConfig(file)
if err != nil {
logoPreviewSize.SetText("")
return
}
menuW, menuH := menuPreviewSize()
maxW := int(float64(menuW) * 0.25)
maxH := int(float64(menuH) * 0.25)
scale := state.authorMenuLogoScale
targetW := int(math.Round(float64(cfg.Width) * scale))
targetH := int(math.Round(float64(cfg.Height) * scale))
if targetW > maxW || targetH > maxH {
ratioW := float64(maxW) / float64(targetW)
ratioH := float64(maxH) / float64(targetH)
ratio := math.Min(ratioW, ratioH)
targetW = int(math.Round(float64(targetW) * ratio))
targetH = int(math.Round(float64(targetH) * ratio))
}
logoPreviewSize.SetText(fmt.Sprintf("Logo size: %dx%d (max %dx%d)", targetW, targetH, maxW, maxH))
}
logoPickButton := widget.NewButton("Select Logo", func() {
dialog.ShowFileOpen(func(reader fyne.URIReadCloser, err error) {
if err != nil || reader == nil {
@ -1038,6 +1111,7 @@ func buildAuthorMenuTab(state *appState) fyne.CanvasObject {
defer reader.Close()
state.authorMenuLogoPath = reader.URI().Path()
logoLabel.SetText(state.authorMenuLogoPath)
updateLogoPreview()
state.updateAuthorSummary()
state.persistAuthorConfig()
}, state.window)
@ -1082,6 +1156,7 @@ func buildAuthorMenuTab(state *appState) fyne.CanvasObject {
logoScaleSelect := widget.NewSelect(scaleOptions, func(value string) {
if scale, ok := scaleValueByLabel[value]; ok {
state.authorMenuLogoScale = scale
updateLogoPreview()
state.persistAuthorConfig()
}
})
@ -1148,7 +1223,11 @@ func buildAuthorMenuTab(state *appState) fyne.CanvasObject {
info := widget.NewLabel("DVD menus are generated using the VideoTools theme and IBM Plex Mono. Menu settings apply only to disc authoring.")
info.Wrapping = fyne.TextWrapWord
previewBox := buildMenuBox("Preview", widget.NewLabel("Menu preview is generated during authoring."))
previewBox := buildMenuBox("Logo Preview", container.NewVBox(
logoPreviewLabel,
logoPreview,
logoPreviewSize,
))
menuCore := buildMenuBox("Menu Core", container.NewVBox(
createMenuCheck,
@ -1233,11 +1312,13 @@ func buildAuthorMenuTab(state *appState) fyne.CanvasObject {
logoEnableCheck.OnChanged = func(checked bool) {
state.authorMenuLogoEnabled = checked
updateLogoPreview()
updateMenuControls(state.authorCreateMenu)
state.updateAuthorSummary()
state.persistAuthorConfig()
}
updateLogoPreview()
updateMenuControls(state.authorCreateMenu)
return container.NewPadded(controls)