1using System; 2using System.Collections.Generic; 3using System.Drawing; 4using System.Drawing.Drawing2D; 5using System.Drawing.Imaging; 6using System.Drawing.Text; 7using System.IO; 8using System.Linq; 9using System.Runtime.InteropServices; 10using System.Windows.Forms; 11using WindowCapture.App; 12using WindowCapture.Detection; 13using WindowCapture.Effects; 14using WindowCapture.Helpers; 15using WindowCapture.Integration; 16using WindowCapture.Models; 17using WindowCapture.Native; 18 19namespace WindowCapture.UI 20{ 21 public partial class EditorForm 22 { 23 // ===== Video overlay painting ===== 24 25 // ===== Video overlay painting ===== 26 27 private void VideoOverlay_Paint(object sender, PaintEventArgs e) 28 { 29 // UI is drawn via CreateGraphics in timer tick (over video) 30 // This handler only used if videoOverlay panel exists (legacy) 31 if (videoOverlay == null) return; 32 var g = e.Graphics; 33 g.SmoothingMode = SmoothingMode.AntiAlias; 34 PaintGlassButtons(g); 35 PaintVideoControls(g); 36 PaintFileNameIndicator(g); 37 38 // Volume indicator 39 PaintVolumeIndicator(g); 40 } 41 42 private string FormatTime(double seconds) 43 { 44 if (double.IsNaN(seconds) || seconds < 0) seconds = 0; 45 int s = (int)seconds; 46 int m = s / 60; 47 s = s % 60; 48 int h = m / 60; 49 m = m % 60; 50 if (h > 0) 51 return h + ":" + m.ToString("D2") + ":" + s.ToString("D2"); 52 return m + ":" + s.ToString("D2"); 53 } 54 55 private Rectangle GetVideoSeekBarRect(Control panel) 56 { 57 int w = panel.Width; 58 int h = panel.Height; 59 int barY = h - VideoControlBarH; 60 int seekX = 50; 61 int seekW = w - 50 - 200; 62 if (seekW < 60) seekW = 60; 63 return new Rectangle(seekX, barY + 7, seekW, 36); 64 } 65 66 private Rectangle GetVideoVolBarRect(Control panel) 67 { 68 int w = panel.Width; 69 int h = panel.Height; 70 int barY = h - VideoControlBarH; 71 return new Rectangle(w - 90, barY + 17, 60, 16); 72 } 73 74 private void PaintVideoControls(Graphics g) 75 { 76 if ((!isVideoFile && !isAudioFile) || isAnimatedGif || videoControlsAlpha <= 0.01f) return; 77 var panel = VideoPanel ?? scrollContainer; 78 // Apply Bezier S-curve for smooth non-linear fade 79 float alpha = videoControlsAlpha * videoControlsAlpha * (3f - 2f * videoControlsAlpha); 80 int w = panel.Width; 81 int h = panel.Height; 82 int barY = h - VideoControlBarH; 83 84 // Auto-color based on tint brightness 85 Color tint = Settings.BlurTintColor; 86 float br = ColorHelper.GetBrightness(tint); 87 bool isDark = br < 0.45f; 88 Color textColor = isDark 89 ? Color.FromArgb((int)(alpha * 210), 210, 218, 235) 90 : Color.FromArgb((int)(alpha * 210), 25, 25, 35); 91 Color dimColor = isDark 92 ? Color.FromArgb((int)(alpha * 100), 180, 190, 210) 93 : Color.FromArgb((int)(alpha * 100), 50, 50, 65); 94 Color edgeColor = isDark 95 ? Color.FromArgb((int)(alpha * 50), 200, 210, 230) 96 : Color.FromArgb((int)(alpha * 50), 80, 80, 100); 97 Color fillColor = isDark 98 ? Color.FromArgb((int)(alpha * 20), 140, 155, 180) 99 : Color.FromArgb((int)(alpha * 20), 40, 40, 55); 100 Color accentColor = Color.FromArgb((int)(alpha * 200), 120, 160, 230); 101 Color trackColor = isDark 102 ? Color.FromArgb((int)(alpha * 35), 160, 175, 200) 103 : Color.FromArgb((int)(alpha * 35), 80, 80, 100); 104 105 // Get cursor position for proximity glow 106 Point cursorPt = panel.PointToClient(Cursor.Position); 107 108 // Bar background — gradient fade from transparent to semi-opaque 109 for (int row = 0; row < VideoControlBarH; row++) 110 { 111 float t = (float)row / VideoControlBarH; 112 int a = (int)(alpha * 30 * t * t); 113 using (var pen = new Pen(Color.FromArgb(a, 0, 0, 0))) 114 g.DrawLine(pen, 0, barY + row, w, barY + row); 115 } 116 117 // ===== Play/Pause button ===== 118 int btnX = 14, btnCY = barY + VideoControlBarH / 2, btnSize = 20; 119 float playGlow = CalcElementGlow(cursorPt, new PointF(btnX + btnSize / 2f, btnCY), 150f); 120 playGlow = Math.Max(playGlow, alpha * 0.5f); // minimum visibility 121 122 if (videoPlaying) 123 { 124 int bw = 3, gap = 5; 125 int lx = btnX + (btnSize - bw * 2 - gap) / 2; 126 using (var brush = new SolidBrush(Color.FromArgb((int)(playGlow * 220), textColor))) 127 { 128 g.FillRectangle(brush, lx, btnCY - 8, bw, 16); 129 g.FillRectangle(brush, lx + bw + gap, btnCY - 8, bw, 16); 130 } 131 } 132 else 133 { 134 var pts = new PointF[] 135 { 136 new PointF(btnX + 3, btnCY - 9), 137 new PointF(btnX + btnSize - 2, btnCY), 138 new PointF(btnX + 3, btnCY + 9) 139 }; 140 using (var brush = new SolidBrush(Color.FromArgb((int)(playGlow * 220), textColor))) 141 g.FillPolygon(brush, pts); 142 } 143 144 // ===== Seek bar (Spectrogram Style — polygon fill like audio player) ===== 145 var seekRect = GetVideoSeekBarRect(panel); 146 int centerY = seekRect.Y + seekRect.Height / 2; 147 148 // Waveform (real peaks or fallback sine) 149 float[] peaks = waveformPeaks; 150 int peakCount = peaks != null ? peaks.Length : 0; 151 152 float displayProgress = seekBarProgress; 153 int fillW = (int)(seekRect.Width * Math.Max(0f, Math.Min(1f, displayProgress))); 154 int headX = seekRect.X + fillW; 155 int maxHalfH = seekRect.Height / 2 - 2; 156 157 // Build polygon points for the waveform (top half + bottom half in reverse) 158 int sw = seekRect.Width; 159 int polyCount = sw * 2; 160 if (polyCount < 4) polyCount = 4; 161 var polyPts = new PointF[polyCount]; 162 163 // Energy pulse near playhead (like audio player) 164 float effectRange = sw * 0.2f; 165 float energyBoost = ovlSeeking ? 1.6f : (videoPlaying ? 1.2f : 0.8f); 166 167 for (int i = 0; i < sw; i++) 168 { 169 int x = seekRect.X + i; 170 float t = (float)i / sw; 171 float peak; 172 if (peakCount > 0) 173 { 174 int idx = (int)(t * (peakCount - 1)); 175 peak = peaks[Math.Min(idx, peakCount - 1)]; 176 } 177 else 178 peak = 0.15f + 0.25f * (float)Math.Sin(x * 0.04f); 179 180 // Local energy pulse near playhead 181 float dist = Math.Abs(x - headX); 182 float localFactor = (dist < effectRange) ? (float)Math.Pow(1.0 - dist / effectRange, 2) : 0f; 183 float pulse = 1.0f + localFactor * energyBoost; 184 185 float hh = peak * maxHalfH * pulse; 186 // Soft-clipping 187 if (hh > maxHalfH * 0.75f) 188 { 189 float overflow = hh - maxHalfH * 0.75f; 190 hh = maxHalfH * 0.75f + maxHalfH * 0.25f * (float)Math.Atan(overflow / (maxHalfH * 0.25f)) / (float)(Math.PI / 2); 191 } 192 if (hh < 1) hh = 1; 193 194 polyPts[i] = new PointF(x, centerY - hh); 195 polyPts[polyCount - 1 - i] = new PointF(x, centerY + hh); 196 } 197 198 // Draw played + unplayed with clip regions 199 using (var brPlayed = new SolidBrush(Color.FromArgb((int)(alpha * 220), accentColor))) 200 using (var brUnplayed = new SolidBrush(Color.FromArgb((int)(alpha * 60), trackColor))) 201 { 202 var oldClip = g.Clip; 203 g.SetClip(new Rectangle(seekRect.X, seekRect.Y, fillW, seekRect.Height)); 204 g.FillPolygon(brPlayed, polyPts); 205 g.SetClip(new Rectangle(headX, seekRect.Y, seekRect.Width - fillW, seekRect.Height)); 206 g.FillPolygon(brUnplayed, polyPts); 207 g.Clip = oldClip; 208 } 209 210 // Head line 211 using (var pen = new Pen(Color.FromArgb((int)(alpha * 200), textColor), 1.5f)) 212 g.DrawLine(pen, headX, seekRect.Y + 2, headX, seekRect.Bottom - 2); 213 214 // Seek thumb (larger when dragging) 215 float seekGlow = CalcElementGlow(cursorPt, new PointF(headX, centerY), 100f); 216 seekGlow = Math.Max(seekGlow, ovlSeeking ? 1f : alpha * 0.3f); 217 int thumbR = (int)(3 + seekGlow * 4); 218 using (var brush = new SolidBrush(Color.FromArgb((int)(seekGlow * 240), 200, 215, 245))) 219 g.FillEllipse(brush, headX - thumbR, centerY - thumbR, thumbR * 2, thumbR * 2); 220 using (var pen = new Pen(Color.FromArgb((int)(seekGlow * 60), 160, 180, 220), 1f)) 221 g.DrawEllipse(pen, headX - thumbR, centerY - thumbR, thumbR * 2, thumbR * 2); 222 223 // ===== Time display ===== 224 string timeText = FormatTime(videoCurrentTime) + " / " + FormatTime(videoDuration); 225 using (var font = new Font("Segoe UI", 11f, FontStyle.Regular, GraphicsUnit.Pixel)) 226 { 227 var sz = g.MeasureString(timeText, font); 228 float tx = seekRect.Right + 12; 229 float ty = barY + (VideoControlBarH - sz.Height) / 2f; 230 float timeGlow = CalcElementGlow(cursorPt, new PointF(tx + sz.Width / 2f, ty + sz.Height / 2f), 120f); 231 timeGlow = Math.Max(timeGlow, alpha * 0.4f); 232 g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; 233 using (var brush = new SolidBrush(Color.FromArgb((int)(timeGlow * 150), dimColor))) 234 g.DrawString(timeText, font, brush, tx, ty); 235 } 236 237 // ===== Volume icon ===== 238 int volIconX = w - 118; 239 int volIconCY = barY + VideoControlBarH / 2; 240 float volGlow = CalcElementGlow(cursorPt, new PointF(volIconX + 8, volIconCY), 130f); 241 volGlow = Math.Max(volGlow, alpha * 0.4f); 242 243 Color volColor = videoMuted 244 ? Color.FromArgb((int)(volGlow * 120), dimColor) 245 : Color.FromArgb((int)(volGlow * 200), textColor); 246 int viy = volIconCY - 6; 247 using (var pen = new Pen(volColor, 1.2f)) 248 { 249 g.DrawLine(pen, volIconX, viy + 4, volIconX + 4, viy + 4); 250 g.DrawLine(pen, volIconX + 4, viy + 4, volIconX + 8, viy); 251 g.DrawLine(pen, volIconX + 8, viy, volIconX + 8, viy + 12); 252 g.DrawLine(pen, volIconX + 8, viy + 12, volIconX + 4, viy + 8); 253 g.DrawLine(pen, volIconX + 4, viy + 8, volIconX, viy + 8); 254 g.DrawLine(pen, volIconX, viy + 8, volIconX, viy + 4); 255 256 if (videoMuted) 257 { 258 using (var xPen = new Pen(Color.FromArgb((int)(volGlow * 160), 220, 100, 100), 1.2f)) 259 { 260 g.DrawLine(xPen, volIconX + 11, viy + 3, volIconX + 17, viy + 9); 261 g.DrawLine(xPen, volIconX + 17, viy + 3, volIconX + 11, viy + 9); 262 } 263 } 264 else 265 { 266 using (var wPen = new Pen(Color.FromArgb((int)(volGlow * 80), volColor), 1.0f)) 267 { 268 g.DrawArc(wPen, volIconX + 10, viy + 1, 8, 10, -45, 90); 269 if (videoVolume > 0.4f) 270 g.DrawArc(wPen, volIconX + 13, viy - 1, 10, 14, -50, 100); 271 } 272 } 273 } 274 275 // ===== Volume bar ===== 276 var volRect = GetVideoVolBarRect(panel); 277 int volTrackY = volRect.Y + volRect.Height / 2 - 2; 278 float volBarGlow = CalcElementGlow(cursorPt, new PointF(volRect.X + volRect.Width / 2f, volRect.Y + volRect.Height / 2f), 100f); 279 volBarGlow = Math.Max(volBarGlow, alpha * 0.3f); 280 float vol = videoMuted ? 0f : videoVolume; 281 282 using (var brush = new SolidBrush(Color.FromArgb((int)(volBarGlow * 50), trackColor))) 283 { 284 using (var path = CreateRoundRectPath(volRect.X, volTrackY, volRect.Width, 4, 2)) 285 g.FillPath(brush, path); 286 } 287 int volFillW = (int)(volRect.Width * vol); 288 if (volFillW > 2) 289 { 290 using (var brush = new SolidBrush(Color.FromArgb((int)(volBarGlow * 200), accentColor))) 291 { 292 using (var path = CreateRoundRectPath(volRect.X, volTrackY, volFillW, 4, 2)) 293 g.FillPath(brush, path); 294 } 295 } 296 int vThumbX = volRect.X + volFillW; 297 int vThumbCY = volRect.Y + volRect.Height / 2; 298 int vThumbR = (int)(3 + volBarGlow * 3); 299 using (var brush = new SolidBrush(Color.FromArgb((int)(volBarGlow * 220), 200, 215, 245))) 300 g.FillEllipse(brush, vThumbX - vThumbR, vThumbCY - vThumbR, vThumbR * 2, vThumbR * 2); 301 } 302 } 303}