增加自动分辨率

This commit is contained in:
ww-rm
2025-04-17 20:00:15 +08:00
parent e6e7fc539f
commit 65bd11a346

View File

@@ -28,18 +28,23 @@ namespace SpineViewer.Spine.SpineExporter
protected string timestamp = DateTime.Now.ToString("yyMMddHHmmss"); protected string timestamp = DateTime.Now.ToString("yyMMddHHmmss");
/// <summary> /// <summary>
/// 模型包围盒缓存 /// 非自动分辨率下导出视区缓存
/// </summary> /// </summary>
private readonly Dictionary<string, RectangleF> boundsCache = []; private SFML.Graphics.View? exportViewCache = null;
/// <summary> /// <summary>
/// 非自动分辨率下导出视图缓存 /// 模型分辨率缓存
/// </summary> /// </summary>
private SFML.Graphics.View? viewCache = null; private readonly Dictionary<string, Size> spineResolutionCache = [];
/// <summary>
/// 自动分辨率下每个模型的导出视区缓存
/// </summary>
private readonly Dictionary<string, SFML.Graphics.View> spineViewCache = [];
~Exporter() { Dispose(false); } ~Exporter() { Dispose(false); }
public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }
protected virtual void Dispose(bool disposing) { View.Dispose(); } protected virtual void Dispose(bool disposing) { PreviewerView.Dispose(); }
/// <summary> /// <summary>
/// 输出文件夹 /// 输出文件夹
@@ -57,10 +62,10 @@ namespace SpineViewer.Spine.SpineExporter
public Size Resolution { get; set; } = new(100, 100); public Size Resolution { get; set; } = new(100, 100);
/// <summary> /// <summary>
/// 渲染视窗 /// 预览画面的视区
/// </summary> /// </summary>
public SFML.Graphics.View View { get => view; set { view.Dispose(); view = new(value); } } public SFML.Graphics.View PreviewerView { get => previewerView; set { previewerView.Dispose(); previewerView = new(value); } }
private SFML.Graphics.View view = new(); private SFML.Graphics.View previewerView = new();
/// <summary> /// <summary>
/// 是否仅渲染选中 /// 是否仅渲染选中
@@ -109,7 +114,7 @@ namespace SpineViewer.Spine.SpineExporter
private Padding margin = new(0); private Padding margin = new(0);
/// <summary> /// <summary>
/// 四周填充距离, 单位为像素 /// 四周填充距离, 单位为像素, 自动分辨率下忽略该值
/// </summary> /// </summary>
public Padding Padding public Padding Padding
{ {
@@ -131,53 +136,67 @@ namespace SpineViewer.Spine.SpineExporter
public bool AllowContentOverflow { get; set; } = false; public bool AllowContentOverflow { get; set; } = false;
/// <summary> /// <summary>
/// 自动分辨率, 将会忽略预览画面的分辨率和视图, 使用模型自身的包围盒 /// 自动分辨率, 将会忽略预览画面的分辨率和预览画面视区, 使用模型自身的包围盒, 四周填充会被忽略
/// </summary> /// </summary>
public bool AutoResolution { get; set; } = true; public bool AutoResolution { get; set; } = false;
/// <summary> /// <summary>
/// 按照预览画面的参数获取供渲染的 SFML.Graphics.RenderTexture /// 获取导出渲染对象, 如果提供了模型列表则分辨率为模型大小, 否则是预览画面大小
/// </summary> /// </summary>
private SFML.Graphics.RenderTexture GetRenderTexture() private SFML.Graphics.RenderTexture GetRenderTexture(SpineObject[]? spinesToRender = null)
{ {
if (viewCache is null) if (spinesToRender is null)
{ {
viewCache = new SFML.Graphics.View(View); if (exportViewCache is null)
{
// 记录缓存
exportViewCache = new SFML.Graphics.View(PreviewerView);
if (AllowContentOverflow) if (AllowContentOverflow)
{ {
var bounds = View.GetBounds().GetCanvasBounds(Resolution, Margin, Padding); var canvasBounds = exportViewCache.GetBounds().GetCanvasBounds(Resolution, Margin, Padding);
viewCache.Center = new(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2); exportViewCache.Center = new(canvasBounds.X + canvasBounds.Width / 2, canvasBounds.Y + canvasBounds.Height / 2);
viewCache.Size = new(bounds.Width, bounds.Height); exportViewCache.Size = new(canvasBounds.Width, canvasBounds.Height);
} }
else else
{ {
viewCache.SetViewport(Resolution, Margin, Padding); exportViewCache.SetViewport(Resolution, Margin, Padding);
} }
} }
var tex = new SFML.Graphics.RenderTexture((uint)(Resolution.Width + Margin.Horizontal), (uint)(Resolution.Height + Margin.Vertical)); var tex = new SFML.Graphics.RenderTexture((uint)(Resolution.Width + Margin.Horizontal), (uint)(Resolution.Height + Margin.Vertical));
tex.SetView(viewCache); tex.SetView(exportViewCache);
tex.Clear(SFML.Graphics.Color.Transparent);
return tex; return tex;
} }
else
{
var cacheKey = string.Join("|", spinesToRender.Select(v => v.ID));
/// <summary> // 记录缓存
/// 按照要被渲染的模型获取 SFML.Graphics.RenderTexture if (!spineViewCache.TryGetValue(cacheKey, out var spineView))
/// </summary> {
//private SFML.Graphics.RenderTexture GetRenderTexture(SpineObject[] spinesToRender) var spineBounds = spinesToRender[0].GetBounds();
//{ foreach (var sp in spinesToRender.Skip(1))
// var cacheKey = string.Join("|", spinesToRender.Select(v => v.ID)); spineBounds = spineBounds.Union(sp.GetBounds());
// var bounds = new RectangleF();
// if (!boundsCache.TryGetValue(cacheKey, out bounds)) var spineResolution = new Size((int)Math.Ceiling(spineBounds.Width), (int)Math.Ceiling(spineBounds.Height));
// { var canvasBounds = spineBounds.GetCanvasBounds(spineResolution, Margin); // 忽略填充
// bounds = spinesToRender[0].GetBounds();
// foreach (var sp in spinesToRender) spineResolutionCache[cacheKey] = spineResolution;
// { spineViewCache[cacheKey] = spineView = new SFML.Graphics.View(
// bounds.X = new(canvasBounds.X + canvasBounds.Width / 2, canvasBounds.Y + canvasBounds.Height / 2),
// } new(canvasBounds.Width, -canvasBounds.Height)
// } );
//}
logger.Info("Auto resolusion: ({}, {})", spineResolution.Width, spineResolution.Height);
}
var tex = new SFML.Graphics.RenderTexture(
(uint)(spineResolutionCache[cacheKey].Width + Margin.Horizontal),
(uint)(spineResolutionCache[cacheKey].Height + Margin.Vertical)
);
tex.SetView(spineViewCache[cacheKey]);
return tex;
}
}
/// <summary> /// <summary>
/// 获取单个模型的单帧画面 /// 获取单个模型的单帧画面
@@ -190,11 +209,11 @@ namespace SpineViewer.Spine.SpineExporter
protected SFMLImageVideoFrame GetFrame(SpineObject[] spinesToRender) protected SFMLImageVideoFrame GetFrame(SpineObject[] spinesToRender)
{ {
// RenderTexture 必须临时创建, 随用随取, 防止出现跨线程的情况 // RenderTexture 必须临时创建, 随用随取, 防止出现跨线程的情况
using var texPma = GetRenderTexture(); using var texPma = GetRenderTexture(AutoResolution ? spinesToRender : null);
// 先将预乘结果准确绘制出来, 注意背景色也应当是预乘的 // 先将预乘结果准确绘制出来, 注意背景色也应当是预乘的
texPma.Clear(BackgroundColorPma); texPma.Clear(BackgroundColorPma);
foreach (var spine in spinesToRender) { spine.EnableDebug = true; texPma.Draw(spine); } foreach (var spine in spinesToRender) texPma.Draw(spine);
texPma.Display(); texPma.Display();
// 背景色透明度不为 1 时需要处理反预乘, 否则直接就是结果 // 背景色透明度不为 1 时需要处理反预乘, 否则直接就是结果
@@ -218,7 +237,7 @@ namespace SpineViewer.Spine.SpineExporter
st.Shader = SFMLShader.InversePma; st.Shader = SFMLShader.InversePma;
// 在最终结果上二次渲染非预乘画面 // 在最终结果上二次渲染非预乘画面
using var tex = GetRenderTexture(); using var tex = GetRenderTexture(AutoResolution ? spinesToRender : null);
// 将非预乘结果覆盖式绘制在目标对象上, 注意背景色应该用非预乘的 // 将非预乘结果覆盖式绘制在目标对象上, 注意背景色应该用非预乘的
tex.Clear(BackgroundColor); tex.Clear(BackgroundColor);
@@ -258,6 +277,15 @@ namespace SpineViewer.Spine.SpineExporter
return null; return null;
} }
private void ClearCache()
{
exportViewCache?.Dispose();
exportViewCache = null;
spineResolutionCache.Clear();
foreach (var v in spineViewCache.Values) v.Dispose();
spineViewCache.Clear();
}
/// <summary> /// <summary>
/// 执行导出 /// 执行导出
/// </summary> /// </summary>
@@ -271,17 +299,13 @@ namespace SpineViewer.Spine.SpineExporter
var spinesToRender = spines.Where(sp => !RenderSelectedOnly || sp.IsSelected).Reverse().ToArray(); var spinesToRender = spines.Where(sp => !RenderSelectedOnly || sp.IsSelected).Reverse().ToArray();
if (spinesToRender.Length > 0) if (spinesToRender.Length > 0)
{ {
boundsCache.Clear(); ClearCache();
viewCache?.Dispose();
viewCache = null;
timestamp = DateTime.Now.ToString("yyMMddHHmmss"); // 刷新时间戳 timestamp = DateTime.Now.ToString("yyMMddHHmmss"); // 刷新时间戳
if (IsExportSingle) ExportSingle(spinesToRender, worker); if (IsExportSingle) ExportSingle(spinesToRender, worker);
else ExportIndividual(spinesToRender, worker); else ExportIndividual(spinesToRender, worker);
boundsCache.Clear(); ClearCache();
viewCache?.Dispose();
viewCache = null;
} }
logger.LogCurrentProcessMemoryUsage(); logger.LogCurrentProcessMemoryUsage();
@@ -317,10 +341,10 @@ namespace SpineViewer.Spine.SpineExporter
public Size Resolution { get => Exporter.Resolution; } public Size Resolution { get => Exporter.Resolution; }
/// <summary> /// <summary>
/// 渲染视窗 /// 预览画面视区
/// </summary> /// </summary>
[Category("[0] "), DisplayName(""), Description("")] [Category("[0] "), DisplayName(""), Description("")]
public SFML.Graphics.View View { get => Exporter.View; } public SFML.Graphics.View View { get => Exporter.PreviewerView; }
/// <summary> /// <summary>
/// 是否仅渲染选中 /// 是否仅渲染选中
@@ -355,5 +379,11 @@ namespace SpineViewer.Spine.SpineExporter
/// </summary> /// </summary>
[Category("[0] "), DisplayName(""), Description("")] [Category("[0] "), DisplayName(""), Description("")]
public bool AllowContentOverflow { get => Exporter.AllowContentOverflow; set => Exporter.AllowContentOverflow = value; } public bool AllowContentOverflow { get => Exporter.AllowContentOverflow; set => Exporter.AllowContentOverflow = value; }
/// <summary>
/// 自动分辨率
/// </summary>
[Category("[0] "), DisplayName(""), Description(", ")]
public bool AutoResolution { get => Exporter.AutoResolution; set => Exporter.AutoResolution = value; }
} }
} }