using NLog; using SFML.System; using SkiaSharp; using System; using System.Collections.Frozen; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Spine.Exporters { /// /// 多帧画面导出基类, 可以获取连续的帧序列 /// public abstract class VideoExporter : BaseExporter { public VideoExporter(uint width, uint height) : base(width, height) { } public VideoExporter(Vector2u resolution) : base(resolution) { } /// /// 导出时长 /// public float Duration { get => _duration; set { if (value < 0) { _logger.Warn("Omit invalid duration: {0}", value); return; } _duration = value; } } protected float _duration = 0; /// /// 帧率 /// public float Fps { get => _fps; set { if (value <= 0) { _logger.Warn("Omit invalid fps: {0}", value); return; } _fps = value; } } protected float _fps = 24; public float Speed { get => _speed; set { if (_speed <= 0) { _logger.Warn("Omit invalid speed: {0}", value); return; } _speed = value; } } protected float _speed = 1f; /// /// 是否保留最后一帧 /// public bool KeepLast { get => _keepLast; set => _keepLast = value; } protected bool _keepLast = true; /// /// 获取总帧数 /// public int GetFrameCount() { var delta = 1f / _fps; var total = (int)(_duration * _fps); // 完整帧的数量 var deltaFinal = _duration - delta * total; // 最后一帧时长 var final = _keepLast && deltaFinal > 1e-3 ? 1 : 0; var frameCount = 1 + total + final; // 所有帧的数量 = 起始帧 + 完整帧 + 最后一帧 return frameCount; } /// /// 生成帧序列, 用于导出帧序列 /// protected IEnumerable GetFrames(SpineObject[] spines) { float delta = 1f / _fps; int total = (int)(_duration * _fps); // 完整帧的数量 bool hasFinal = _keepLast && (_duration - delta * total) > 1e-3; // 导出首帧 var firstFrame = GetFrame(spines); yield return firstFrame; // 导出完整帧 for (int i = 0; i < total; i++) { foreach (var spine in spines) spine.Update(delta * _speed); yield return GetFrame(spines); } // 导出最后一帧 if (hasFinal) { // XXX: 此处还是按照完整的一帧时长进行更新, 也许可以只更新准确的最后一帧时长 foreach (var spine in spines) spine.Update(delta * _speed); yield return GetFrame(spines); } } /// /// 生成帧序列, 支持中途取消和进度输出, 用于动图视频等单个文件输出 /// protected IEnumerable GetFrames(SpineObject[] spines, string output, CancellationToken ct) { int frameCount = GetFrameCount(); int frameIdx = 0; _progressReporter?.Invoke(frameCount, 0, $"[0/{frameCount}] {output}"); foreach (var frame in GetFrames(spines)) { if (ct.IsCancellationRequested) { _logger.Info("Export cancelled"); frame.Dispose(); break; } _progressReporter?.Invoke(frameCount, frameIdx + 1, $"[{frameIdx + 1}/{frameCount}] {output}"); yield return frame; frameIdx++; } } public sealed override void Export(string output, params SpineObject[] spines) => Export(output, default, spines); /// /// 导出给定的模型, 从前往后对应从上往下的渲染顺序 /// /// 输出路径, 一般而言都是文件路径, 少数情况指定的是文件夹 /// 取消令牌 /// 要导出的模型, 从前往后对应从上往下的渲染顺序 public abstract void Export(string output, CancellationToken ct, params SpineObject[] spines); } }