208 lines
6.8 KiB
C#
208 lines
6.8 KiB
C#
using System;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace XP.Common.Controls.ImageHistogram
|
|
{
|
|
/// <summary>
|
|
/// 帧率限流器,确保计算频率不超过 MaxFrameRate | Frame rate throttler
|
|
/// 支持从任意线程调用,使用 lock 保护内部状态
|
|
/// </summary>
|
|
internal sealed class FrameThrottler : IDisposable
|
|
{
|
|
private readonly object _lock = new();
|
|
private DateTime _lastProcessTime = DateTime.MinValue;
|
|
private Action? _pendingAction;
|
|
private CancellationTokenSource? _delayCts;
|
|
private bool _isProcessing;
|
|
private bool _disposed;
|
|
private int _maxFrameRate = 15;
|
|
|
|
/// <summary>
|
|
/// 最大刷新帧率(fps),有效范围 1-60,超出范围自动钳位 | Max frame rate (fps), valid range 1-60, auto-clamped
|
|
/// </summary>
|
|
public int MaxFrameRate
|
|
{
|
|
get => _maxFrameRate;
|
|
set => _maxFrameRate = Math.Clamp(value, 1, 60);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 获取当前帧间隔(毫秒)| Get current frame interval (ms)
|
|
/// </summary>
|
|
private double FrameIntervalMs => 1000.0 / _maxFrameRate;
|
|
|
|
/// <summary>
|
|
/// 提交一帧计算动作 | Submit a frame compute action
|
|
/// 若未超过帧率限制则立即执行,否则缓存最新帧并延迟触发
|
|
/// </summary>
|
|
/// <param name="computeAction">计算动作 | Compute action</param>
|
|
/// <returns>是否被立即接受处理 | Whether it was immediately accepted</returns>
|
|
public bool TrySubmit(Action computeAction)
|
|
{
|
|
if (_disposed || computeAction == null)
|
|
return false;
|
|
|
|
lock (_lock)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
var elapsed = (now - _lastProcessTime).TotalMilliseconds;
|
|
|
|
if (elapsed >= FrameIntervalMs && !_isProcessing)
|
|
{
|
|
// 已超过间隔且无正在处理的任务,立即执行 | Interval exceeded and no processing, execute immediately
|
|
_isProcessing = true;
|
|
_lastProcessTime = now;
|
|
ExecuteAction(computeAction);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
// 未超过间隔或正在处理中,缓存最新帧(丢弃之前的中间帧)| Cache latest frame, discard previous
|
|
_pendingAction = computeAction;
|
|
ScheduleDelayedExecution(elapsed);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 执行计算动作(异步,完成后检查待处理帧)| Execute compute action asynchronously
|
|
/// </summary>
|
|
private void ExecuteAction(Action action)
|
|
{
|
|
Task.Run(() =>
|
|
{
|
|
try
|
|
{
|
|
action.Invoke();
|
|
}
|
|
catch
|
|
{
|
|
// 异常不外抛 | Do not propagate exceptions
|
|
}
|
|
finally
|
|
{
|
|
OnActionCompleted();
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 计算动作完成后的回调 | Callback after compute action completes
|
|
/// </summary>
|
|
private void OnActionCompleted()
|
|
{
|
|
Action? nextAction = null;
|
|
|
|
lock (_lock)
|
|
{
|
|
_isProcessing = false;
|
|
|
|
if (_pendingAction != null && !_disposed)
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
var elapsed = (now - _lastProcessTime).TotalMilliseconds;
|
|
|
|
if (elapsed >= FrameIntervalMs)
|
|
{
|
|
// 间隔已到,立即执行待处理帧 | Interval reached, execute pending frame
|
|
nextAction = _pendingAction;
|
|
_pendingAction = null;
|
|
_isProcessing = true;
|
|
_lastProcessTime = now;
|
|
}
|
|
// 否则等待延迟触发 | Otherwise wait for delayed trigger
|
|
}
|
|
}
|
|
|
|
if (nextAction != null)
|
|
{
|
|
ExecuteAction(nextAction);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 安排延迟执行(等待帧间隔到期后处理最新帧)| Schedule delayed execution
|
|
/// </summary>
|
|
private void ScheduleDelayedExecution(double elapsedMs)
|
|
{
|
|
// 取消之前的延迟任务 | Cancel previous delay task
|
|
_delayCts?.Cancel();
|
|
_delayCts?.Dispose();
|
|
_delayCts = new CancellationTokenSource();
|
|
var token = _delayCts.Token;
|
|
|
|
var delayMs = Math.Max(0, FrameIntervalMs - elapsedMs);
|
|
|
|
Task.Run(async () =>
|
|
{
|
|
try
|
|
{
|
|
await Task.Delay((int)delayMs, token);
|
|
|
|
if (token.IsCancellationRequested)
|
|
return;
|
|
|
|
Action? actionToExecute = null;
|
|
|
|
lock (_lock)
|
|
{
|
|
if (_pendingAction != null && !_isProcessing && !_disposed)
|
|
{
|
|
actionToExecute = _pendingAction;
|
|
_pendingAction = null;
|
|
_isProcessing = true;
|
|
_lastProcessTime = DateTime.UtcNow;
|
|
}
|
|
}
|
|
|
|
if (actionToExecute != null)
|
|
{
|
|
ExecuteAction(actionToExecute);
|
|
}
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
// 延迟被取消,正常情况 | Delay cancelled, normal case
|
|
}
|
|
catch
|
|
{
|
|
// 异常不外抛 | Do not propagate exceptions
|
|
}
|
|
});
|
|
}
|
|
|
|
/// <summary>
|
|
/// 取消所有待处理任务 | Cancel all pending tasks
|
|
/// </summary>
|
|
public void Cancel()
|
|
{
|
|
lock (_lock)
|
|
{
|
|
_pendingAction = null;
|
|
_delayCts?.Cancel();
|
|
_delayCts?.Dispose();
|
|
_delayCts = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 释放所有资源 | Dispose all resources
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
if (_disposed) return;
|
|
_disposed = true;
|
|
|
|
lock (_lock)
|
|
{
|
|
_pendingAction = null;
|
|
_delayCts?.Cancel();
|
|
_delayCts?.Dispose();
|
|
_delayCts = null;
|
|
}
|
|
}
|
|
}
|
|
}
|