Files
XplorePlane/XplorePlane/Services/Pipeline/PipelineExecutionService.cs
T
2026-04-20 11:13:40 +08:00

115 lines
4.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using XplorePlane.ViewModels;
namespace XplorePlane.Services
{
public class PipelineExecutionService : IPipelineExecutionService
{
private const int PreviewMaxHeight = 1080;
private const int UhdThreshold = 3840;
private readonly IImageProcessingService _imageProcessingService;
public PipelineExecutionService(IImageProcessingService imageProcessingService)
{
_imageProcessingService = imageProcessingService ?? throw new ArgumentNullException(nameof(imageProcessingService));
}
public async Task<BitmapSource> ExecutePipelineAsync(
IEnumerable<PipelineNodeViewModel> nodes,
BitmapSource source,
IProgress<PipelineProgress> progress = null,
CancellationToken cancellationToken = default)
{
if (source == null) throw new ArgumentNullException(nameof(source));
var enabledNodes = (nodes ?? Enumerable.Empty<PipelineNodeViewModel>())
.Where(n => n.IsEnabled)
.OrderBy(n => n.Order)
.ToList();
if (enabledNodes.Count == 0)
return source;
// 大图像预览缩放
var current = ScaleForPreview(source);
int total = enabledNodes.Count;
for (int step = 0; step < total; step++)
{
cancellationToken.ThrowIfCancellationRequested();
var node = enabledNodes[step];
var invalidParameters = node.Parameters
.Where(p => !p.IsValueValid)
.Select(p => p.DisplayName)
.ToList();
if (invalidParameters.Count > 0)
{
throw new PipelineExecutionException(
$"算子 '{node.DisplayName}' 存在无效参数:{string.Join("", invalidParameters)}",
node.Order,
node.OperatorKey);
}
var parameters = node.Parameters
.Where(p => p.IsValueValid)
.ToDictionary(p => p.Name, p => p.Value);
try
{
current = await _imageProcessingService.ProcessImageAsync(
current, node.OperatorKey, parameters, null, cancellationToken);
if (current == null)
throw new PipelineExecutionException(
$"算子 '{node.OperatorKey}' 返回了空图像",
node.Order, node.OperatorKey);
}
catch (OperationCanceledException)
{
throw;
}
catch (PipelineExecutionException)
{
throw;
}
catch (Exception ex)
{
throw new PipelineExecutionException(
$"算子 '{node.DisplayName}' 执行失败:{ex.Message}",
node.Order, node.OperatorKey, ex);
}
progress?.Report(new PipelineProgress(step + 1, total, node.DisplayName));
}
if (!current.IsFrozen)
current.Freeze();
return current;
}
private static BitmapSource ScaleForPreview(BitmapSource source)
{
if (source.PixelWidth <= UhdThreshold && source.PixelHeight <= UhdThreshold)
return source;
double scale = (double)PreviewMaxHeight / source.PixelHeight;
if (source.PixelWidth * scale > UhdThreshold)
scale = (double)UhdThreshold / source.PixelWidth;
var scaled = new TransformedBitmap(source, new ScaleTransform(scale, scale));
scaled.Freeze();
return scaled;
}
}
}