From 24bd1d78f261dfaef3d5bccb9111cb614219b68f Mon Sep 17 00:00:00 2001 From: JSLMPR Date: Wed, 22 Jul 2026 09:52:12 +0200 Subject: [PATCH] Premium highlight overlay styling (P3.3) Replace the plain hard-cut white caption with a refined 48px caption, a soft drop shadow, a thin subtle border, and a smooth alpha fade in and out (0.4s ramps) within each overlay timeline window. Highlight renderer only; the multi-clip renderer and overlay placement/safe-area logic are unchanged. Test added; mvn -o verify green (246 tests, 0 failures). Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01Bg76sLc43Wc3j5ZcLkboYR --- .../editing/HighlightFfmpegRenderer.java | 14 +++++++++++--- .../editing/HighlightFfmpegRendererTest.java | 14 ++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/example/videoclips/editing/HighlightFfmpegRenderer.java b/src/main/java/org/example/videoclips/editing/HighlightFfmpegRenderer.java index 183456f..f84069f 100644 --- a/src/main/java/org/example/videoclips/editing/HighlightFfmpegRenderer.java +++ b/src/main/java/org/example/videoclips/editing/HighlightFfmpegRenderer.java @@ -555,9 +555,17 @@ public class HighlightFfmpegRenderer { } private String drawTextFilter(TextOverlay overlay) { - return "drawtext=text='%s':x=%s:y=%s:fontsize=54:fontcolor=white:borderw=2:bordercolor=black@0.75:enable='between(t\\,%s\\,%s)'" - .formatted(escapeDrawText(overlay.text()), overlayX(overlay.placement()), overlayY(overlay.placement()), - overlay.timelineStartSeconds(), overlay.timelineEndSeconds()); + String start = Double.toString(overlay.timelineStartSeconds()); + String end = Double.toString(overlay.timelineEndSeconds()); + // Smooth alpha fade: ramp up over the first 0.4 s, hold, ramp down over the last 0.4 s. + String alpha = "if(lt(t\\," + start + "+0.4)\\,(t-" + start + ")/0.4\\," + + "if(lt(t\\," + end + "-0.4)\\,1\\,(" + end + "-t)/0.4))"; + // Premium caption: refined size, soft drop shadow, thin subtle border for legibility. + return ("drawtext=text='%s':x=%s:y=%s:fontsize=48:fontcolor=white" + + ":shadowcolor=black@0.55:shadowx=2:shadowy=2:borderw=1:bordercolor=black@0.35" + + ":alpha='%s':enable='between(t\\,%s\\,%s)'") + .formatted(escapeDrawText(overlay.text()), overlayX(overlay.placement()), + overlayY(overlay.placement()), alpha, start, end); } private String overlayX(String placement) { diff --git a/src/test/java/org/example/videoclips/editing/HighlightFfmpegRendererTest.java b/src/test/java/org/example/videoclips/editing/HighlightFfmpegRendererTest.java index 88bc321..8c861b9 100644 --- a/src/test/java/org/example/videoclips/editing/HighlightFfmpegRendererTest.java +++ b/src/test/java/org/example/videoclips/editing/HighlightFfmpegRendererTest.java @@ -76,6 +76,20 @@ class HighlightFfmpegRendererTest { .contains("[voice_line_0][voice_line_1]amix=inputs=2:duration=longest")); } + @Test + void stylesOverlaysWithSoftShadowAndAlphaFade() { + HighlightFfmpegRenderer renderer = renderer(); + String filter = renderer.overlayFilter(List.of( + new TextOverlay("First light.", 0.5, 3.0, "center_safe", "fade", "hook"))); + + assertThat(filter) + .contains("drawtext=text='First light.'") + .contains("fontsize=48") + .contains("shadowcolor=black@0.55:shadowx=2:shadowy=2") + .contains("alpha='if(lt(t\\,0.5+0.4)") + .contains("enable='between(t\\,0.5\\,3.0)'"); + } + @Test void buildsMeasuredQaProbeCommands() { HighlightFfmpegRenderer renderer = renderer();