115 lines
4.0 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|