diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..3e1e1e0
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,6 @@
+# 若要了解有关 .editorconfig 的详细信息,请参阅 https://aka.ms/editorconfigdocs
+
+# All files
+[*]
+indent_style = space
+indent_size = 2
diff --git a/Svga.sln b/Svga.sln
index 799cb9e..a24829d 100644
--- a/Svga.sln
+++ b/Svga.sln
@@ -1,10 +1,15 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.28307.329
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29001.49
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Svga", "Svga\Svga.csproj", "{F0F7B6EC-3DAF-4D81-AA3C-D4EEB2508943}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{90C0113A-9866-4EEF-AB49-2C146A0B57C6}"
+ ProjectSection(SolutionItems) = preProject
+ .editorconfig = .editorconfig
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM = Debug|ARM
diff --git a/Svga/MainPage.xaml b/Svga/MainPage.xaml
index dfb16fb..7912946 100644
--- a/Svga/MainPage.xaml
+++ b/Svga/MainPage.xaml
@@ -10,11 +10,15 @@
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
>
-
-
-
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+ --
+
+ --
+
+
+
+
+ --
+
+
+
+
+ --
+
+
+
+
+ --
+
+
+
+
+
+
+
+
diff --git a/Svga/MainPage.xaml.cs b/Svga/MainPage.xaml.cs
index f3e91e9..d9cf098 100644
--- a/Svga/MainPage.xaml.cs
+++ b/Svga/MainPage.xaml.cs
@@ -1,5 +1,4 @@
using System;
-using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Runtime.CompilerServices;
@@ -11,6 +10,9 @@
namespace Svga {
public sealed partial class MainPage : Page, INotifyPropertyChanged {
+ private int SpriteCount => 0;
+ private int TotalFrame => this.Player?.TotalFrame ?? 0;
+
public MainPage () {
this.InitializeComponent();
}
@@ -32,20 +34,24 @@ private async void SelectFileAndInitPlayer () {
picker.FileTypeFilter.Add(".svga");
var file = await picker.PickSingleFileAsync();
- if (file != null) {
- this.IsShowDoneText = false;
- using (var stream = await file.OpenAsync(FileAccessMode.Read, StorageOpenOptions.AllowOnlyReaders)) {
- var player = this.Player;
- player.UnloadStage();
-// player.LoopCount = 1;
- player.LoadSvgaFileData(stream.AsStream());
- player.InitStage();
- player.Play();
- player.OnLoopFinish += () => {
- this.IsShowDoneText = true;
- };
- }
+ if (file == null) {
+ return;
+ }
+
+ this.IsShowDoneText = false;
+ using (var stream = await file.OpenAsync(FileAccessMode.Read, StorageOpenOptions.AllowOnlyReaders)) {
+ var player = this.Player;
+ player.UnloadStage();
+ player.LoadSvgaFileData(stream.AsStream());
+ player.InitStage();
+ player.Play();
+ player.OnLoopFinish += () => {
+ this.IsShowDoneText = true;
+ };
}
+
+ this.Notify(nameof(this.SpriteCount));
+ this.Notify(nameof(this.TotalFrame));
}
private void OnSelectFileClick (object sender, RoutedEventArgs e) {
diff --git a/Svga/Svga.csproj b/Svga/Svga.csproj
index a21eb57..7b9bf05 100644
--- a/Svga/Svga.csproj
+++ b/Svga/Svga.csproj
@@ -17,7 +17,15 @@
512
{A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
true
- Svga_TemporaryKey.pfx
+
+
+ False
+ True
+ Always
+ x64
+ 43B4EC126AF9DEE491B1B0BE9DEE1AE70E4FDA89
+ 1
+ OnApplicationRun
true
@@ -120,7 +128,6 @@
App.xaml
-
SvgaPlayer.xaml
diff --git a/Svga/SvgaPlayer/Controls/SvgaPlayer.Data.cs b/Svga/SvgaPlayer/Controls/SvgaPlayer.Data.cs
index a9d8876..585f6fd 100644
--- a/Svga/SvgaPlayer/Controls/SvgaPlayer.Data.cs
+++ b/Svga/SvgaPlayer/Controls/SvgaPlayer.Data.cs
@@ -1,44 +1,128 @@
-using System;
+using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
+using System.Linq;
+using Windows.UI.Core;
using Com.Opensource.Svga;
using Google.Protobuf;
using Google.Protobuf.Collections;
namespace Svga.SvgaPlayer.Controls {
public partial class SvgaPlayer {
- ///
- /// 当前已播放次数.
- ///
- private int PlayedCount { get; set; }
-
///
/// SVGA 文件原始二进制.
///
- private byte[] InflatedBytes { get; set; }
+ private byte[] _inflatedBytes;
///
/// MovieEntity 对象.
/// SVGA 的所有数据将从本对象中读取.
///
- private MovieEntity MovieEntity { get; set; }
+ private MovieEntity _movieEntity;
///
/// 从 MovieEntity 中获取 SVGA 图片.
/// MovieEntity 的 Images 属性下保存了 SVGA 的图片文件, 为一个可枚举的 byteString 集合,
/// 键名为图片名称, 键值为 PNG 的二进制数据.
///
- private MapField Images => this.MovieEntity?.Images;
+ private MapField _images;
///
/// SVGA 配置参数.
///
- private MovieParams MovieParams => this.MovieEntity?.Params;
+ private MovieParams _movieParams;
+
+ ///
+ /// SVGA Sprite Entity 列表.
+ ///
+ private List _sprites;
+
+ ///
+ /// Sprite 数量.
+ ///
+ private int _spriteCount;
+ public int SpriteCount {
+ get => this._spriteCount;
+ set {
+ this._spriteCount = value;
+ this.Notify(nameof(this.SpriteCount));
+ }
+ }
+
+ ///
+ /// 播放循环次数, 默认为 0.
+ /// 当为 0 时代表无限循环播放.
+ ///
+ public int LoopCount { get; set; }
+
+ ///
+ /// 当前播放帧.
+ ///
+ private int _currentFrame;
+ public int CurrentFrame {
+ get => this._currentFrame;
+ private set {
+ this._currentFrame = value;
+ this.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => {
+ this.Notify(nameof(this.CurrentFrame));
+ });
+ }
+ }
+
+ ///
+ /// 是否处于播放状态.
+ ///
+ public bool IsInPlay => this.Stage.Paused == false;
///
- /// SVGA Sprite 对象.
+ /// 动画总帧数.
///
- private RepeatedField Sprites => this.MovieEntity?.Sprites;
+ private int _totalFrame;
+ public int TotalFrame {
+ get => this._totalFrame;
+ set {
+ this._totalFrame = value;
+ this.Notify(nameof(this.TotalFrame));
+ }
+ }
+
+ ///
+ /// 目标播放帧率.
+ /// 若不设置或设置为 0 时使用默认帧率, 设置后将使用自定义帧率.
+ ///
+ private int _fps;
+ public int Fps {
+ get => this._fps;
+ set {
+ if (value < 0) { value = 0; }
+ this._fps = value;
+ this.Notify(nameof(this.Fps));
+ }
+ }
+
+ ///
+ /// 画布宽度.
+ ///
+ private float _stageWidth;
+ public float StageWidth {
+ get => this._stageWidth;
+ set {
+ this._stageWidth = value;
+ this.Notify(nameof(this.StageWidth));
+ }
+ }
+
+ ///
+ /// 画布高度.
+ ///
+ private float _stageHeight;
+ public float StageHeight {
+ get => this._stageHeight;
+ set {
+ this._stageHeight = value;
+ this.Notify(nameof(this.StageHeight));
+ }
+ }
///
/// Inflate SVGA 文件, 获取其原始数据.
@@ -59,26 +143,27 @@ private void InflateSvgaFile (Stream svgaFileBuffer) {
}
}
- this.InflatedBytes = inflatedBytes;
+ this._inflatedBytes = inflatedBytes;
}
///
- /// 通过 Infalte 数据获取 SVGA 的 MovieEntity.
+ /// 通过 Inflate 数据获取 SVGA 的 MovieEntity.
///
///
- private void GetMovieEntity () {
- if (this.InflatedBytes != null) {
- this.MovieEntity = MovieEntity.Parser.ParseFrom(this.InflatedBytes);
+ private void InitMovieEntity () {
+ if (this._inflatedBytes == null) {
+ return;
}
- }
- ///
- /// 载入 SVGA 文件数据.
- ///
- /// SVGA 文件二进制 Stream.
- public void LoadSvgaFileData (Stream svgaFileBuffer) {
- this.InflateSvgaFile(svgaFileBuffer);
- this.GetMovieEntity();
+ var moveEntity = MovieEntity.Parser.ParseFrom(this._inflatedBytes);
+ this._movieEntity = moveEntity;
+ this._movieParams = moveEntity.Params;
+ this._images = moveEntity.Images;
+ this._sprites = moveEntity.Sprites.ToList();
+ this.TotalFrame = moveEntity.Params.Frames;
+ this.SpriteCount = this._sprites.Count;
+ this.StageWidth = this._movieParams.ViewBoxWidth;
+ this.StageHeight = this._movieParams.ViewBoxHeight;
}
}
}
diff --git a/Svga/SvgaPlayer/Controls/SvgaPlayer.Draw.cs b/Svga/SvgaPlayer/Controls/SvgaPlayer.Draw.cs
deleted file mode 100644
index 2dbb93f..0000000
--- a/Svga/SvgaPlayer/Controls/SvgaPlayer.Draw.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using System;
-using System.Numerics;
-using Windows.Foundation;
-using Microsoft.Graphics.Canvas;
-using Svga.SvgaPlayer.Models;
-
-namespace Svga.SvgaPlayer.Controls {
- public partial class SvgaPlayer {
- ///
- /// 绘制单个 Sprite.
- ///
- ///
- ///
- private void DrawSingleSprite (CanvasDrawingSession session, SvgaSprite sprite) {
- var currentFrame = sprite.Frames[this.CurrentFrame];
- if (currentFrame == null) {
- return;
- }
-
- var width = 0f;
- var height = 0f;
- var alpha = currentFrame.Alpha;
- var layout = currentFrame.Layout;
- if (layout != null) {
- width = layout.Width;
- height = layout.Height;
- }
-
- var transform = currentFrame.Transform;
- if (transform != null) {
- // Sprite 透视参数.
- var perspective = new Matrix4x4(new Matrix3x2(
- transform.A, transform.B, transform.C, transform.D, transform.Tx, transform.Ty
- ));
-
- session.DrawImage(
- sprite.CanvasBitmap, 0, 0, new Rect(0, 0, width, height), alpha,
- CanvasImageInterpolation.Linear, perspective
- );
- } else {
- session.DrawImage(
- sprite.CanvasBitmap, 0, 0, new Rect(0, 0, width, height), alpha,
- CanvasImageInterpolation.Linear
- );
- }
- }
- }
-}
diff --git a/Svga/SvgaPlayer/Controls/SvgaPlayer.Stage.cs b/Svga/SvgaPlayer/Controls/SvgaPlayer.Stage.cs
index 8f38587..d0d23d0 100644
--- a/Svga/SvgaPlayer/Controls/SvgaPlayer.Stage.cs
+++ b/Svga/SvgaPlayer/Controls/SvgaPlayer.Stage.cs
@@ -1,6 +1,9 @@
using System;
using System.Linq;
+using System.Numerics;
+using Windows.Foundation;
using Windows.UI.Core;
+using Microsoft.Graphics.Canvas;
using Microsoft.Graphics.Canvas.UI;
using Microsoft.Graphics.Canvas.UI.Xaml;
using Svga.SvgaPlayer.Models;
@@ -8,82 +11,88 @@
namespace Svga.SvgaPlayer.Controls {
public partial class SvgaPlayer {
///
- /// 播放循环次数, 默认为 0.
- /// 当为 0 时代表无限循环播放.
+ /// 当前已播放次数.
///
- public int LoopCount { get; set; }
-
- ///
- /// 是否处于播放状态.
- ///
- public bool IsInPlay => this.Stage.Paused == false;
-
- ///
- /// 目标播放帧率.
- /// 若不设置或设置为 0 时使用默认帧率, 设置后将使用自定义帧率.
- ///
- public int Fps {
- get => this._fps;
- set {
- if (value < 0) {
- value = 0;
- }
- this._fps = value;
- }
- }
- private int _fps;
+ private int _playedCount;
///
/// 舞台是否已经初始化.
///
- private bool IsStageInited { get; set; }
+ private bool _isStageInited;
///
/// 舞台资源是否准备完毕.
///
- private bool IsResourceReady { get; set; }
-
- ///
- /// 动画总帧数.
- ///
- private int TotalFrame => this.MovieParams?.Frames ?? 0;
-
- ///
- /// 当前播放帧.
- ///
- public int CurrentFrame { get; set; }
+ private bool _isResourceReady;
///
- /// CanvasControl 对象.
+ /// 舞台资源对象.
///
- private CanvasAnimatedControl Stage => this.Canvas;
+ private StageResource _stageResource;
///
- /// 舞台资源对象.
+ /// 初始化舞台资源.
///
- private StageResource StageResource { get; set; }
-
private async void InitStageResource () {
- if (this.IsResourceReady) {
+ if (this._isResourceReady) {
return;
}
- if (this.StageResource == null) {
- this.StageResource = new StageResource(this.Stage);
+ if (this._stageResource == null) {
+ this._stageResource = new StageResource(this.Stage);
}
- var sprites = this.Sprites;
+ var sprites = this._sprites;
foreach (var sprite in sprites) {
var imageKey = sprite.ImageKey;
- var image = this.Images.FirstOrDefault(item => item.Key == imageKey);
+ var image = this._images.FirstOrDefault(item => item.Key == imageKey);
// 有可能导出的 SVGA Image 实际不存在 PNG Binary.
if (image.Value != null) {
- await this.StageResource.AddSprite(sprite, image.Value);
+ await this._stageResource.AddSprite(sprite, image.Value);
}
}
- this.IsResourceReady = true;
+ this._isResourceReady = true;
+ }
+
+ ///
+ /// 绘制单个 Sprite.
+ ///
+ ///
+ ///
+ private void DrawSingleSprite(CanvasDrawingSession session, SvgaSprite sprite) {
+ var currentFrame = sprite.Frames[this.CurrentFrame];
+ if (currentFrame == null) {
+ return;
+ }
+
+ var width = 0f;
+ var height = 0f;
+ var alpha = currentFrame.Alpha;
+ var layout = currentFrame.Layout;
+ if (layout != null) {
+ width = layout.Width;
+ height = layout.Height;
+ }
+
+ var transform = currentFrame.Transform;
+ if (transform != null) {
+ // Sprite 透视参数.
+ var perspective = new Matrix4x4(new Matrix3x2(
+ transform.A, transform.B, transform.C, transform.D, transform.Tx, transform.Ty
+ ));
+
+ session.DrawImage(
+ sprite.CanvasBitmap, 0, 0, new Rect(0, 0, width, height), alpha,
+ CanvasImageInterpolation.Linear, perspective
+ );
+ } else {
+ session.DrawImage(
+ sprite.CanvasBitmap, 0, 0, new Rect(0, 0, width, height), alpha,
+ CanvasImageInterpolation.Linear
+ );
+ }
}
///
@@ -92,6 +101,7 @@ private async void InitStageResource () {
///
///
private void StageOnCreateResources (CanvasAnimatedControl sender, CanvasCreateResourcesEventArgs args) {
+ // ...
}
///
@@ -102,6 +112,7 @@ private void StageOnCreateResources (CanvasAnimatedControl sender, CanvasCreateR
///
///
private void StageOnUpdate (ICanvasAnimatedControl sender, CanvasAnimatedUpdateEventArgs args) {
+ // ...
}
///
@@ -110,11 +121,11 @@ private void StageOnUpdate (ICanvasAnimatedControl sender, CanvasAnimatedUpdateE
///
///
private void StageOnDraw (ICanvasAnimatedControl sender, CanvasAnimatedDrawEventArgs args) {
- if (!this.IsInPlay || !this.IsResourceReady || !this.IsStageInited) {
+ if (!this.IsInPlay || !this._isResourceReady || !this._isStageInited) {
return;
}
- var stageResource = this.StageResource;
+ var stageResource = this._stageResource;
var sprites = stageResource.Sprites;
using (var session = args.DrawingSession) {
// 遍历 Sprites 进行绘制.
@@ -127,13 +138,13 @@ private void StageOnDraw (ICanvasAnimatedControl sender, CanvasAnimatedDrawEvent
var isLoopFinished = nextFrame > this.TotalFrame - 1;
if (isLoopFinished) {
nextFrame = 0;
- this.PlayedCount++;
+ this._playedCount++;
}
this.CurrentFrame = nextFrame;
// 判断是否继续播放.
// 此条件需要写在结尾, 否则当前帧会被清空而显示空白.
- if (this.LoopCount > 0 && this.PlayedCount >= this.LoopCount) {
+ if (this.LoopCount > 0 && this._playedCount >= this.LoopCount) {
this.Pause();
this.NotifyLoopFinish();
}
@@ -147,58 +158,5 @@ await this.Dispatcher.RunAsync(CoreDispatcherPriority.Low, () => {
this.OnLoopFinish?.Invoke();
});
}
-
- ///
- /// 初始化 Player 舞台.
- /// 任何配置项请在调用此方法前执行.
- ///
- public void InitStage () {
- if (this.IsStageInited) {
- return;
- }
-
- var param = this.MovieParams;
- var stage = this.Stage;
-
- stage.Width = param.ViewBoxWidth;
- stage.Height = param.ViewBoxHeight;
-
- var fps = this.MovieParams.Fps;
- if (this.Fps > 0) {
- fps = this.Fps;
- }
- stage.TargetElapsedTime = TimeSpan.FromMilliseconds(1000d / fps);
-
- this.InitStageResource();
- this.IsStageInited = true;
- }
-
- ///
- /// 开始画布播放.
- ///
- public void Play () {
- this.Stage.Paused = false;
- }
-
- ///
- /// 暂停画布播放.
- ///
- public void Pause () {
- this.Stage.Paused = true;
- }
-
- ///
- /// 卸载舞台所有数据.
- ///
- public void UnloadStage () {
- this.Pause();
- this.PlayedCount = 0;
- this.CurrentFrame = 0;
- this.IsStageInited = false;
- this.IsResourceReady = false;
- this.StageResource = null;
- this.InflatedBytes = null;
- this.MovieEntity = null;
- }
}
}
diff --git a/Svga/SvgaPlayer/Controls/SvgaPlayer.xaml b/Svga/SvgaPlayer/Controls/SvgaPlayer.xaml
index 23766cf..606a1de 100644
--- a/Svga/SvgaPlayer/Controls/SvgaPlayer.xaml
+++ b/Svga/SvgaPlayer/Controls/SvgaPlayer.xaml
@@ -8,11 +8,9 @@
d:DesignHeight="300"
d:DesignWidth="400"
xmlns:win2d="using:Microsoft.Graphics.Canvas.UI.Xaml"
- Loaded="OnLoaded"
- Unloaded="OnUnloaded"
>
/// 单词循环播放完毕事件.
/// 只有非循环模式才进行触发.
@@ -11,25 +16,73 @@ public partial class SvgaPlayer : UserControl {
public event OnLoopFinishHandler OnLoopFinish;
///
- /// Onload handler.
+ /// 载入 SVGA 文件数据.
///
- ///
- ///
- private void OnLoaded (object sender, RoutedEventArgs e) {
- // ...
+ /// SVGA 文件二进制 Stream.
+ public void LoadSvgaFileData (Stream svgaFileBuffer) {
+ this.InflateSvgaFile(svgaFileBuffer);
+ this.InitMovieEntity();
}
///
- /// Onunload handler.
+ /// 初始化 Player 舞台.
+ /// 任何配置项请在调用此方法前执行.
///
- ///
- ///
- private void OnUnloaded (object sender, RoutedEventArgs e) {
- // ...
+ public void InitStage () {
+ if (this._isStageInited) {
+ return;
+ }
+
+ var stage = this.Stage;
+ stage.Width = this.StageWidth;
+ stage.Height = this.StageHeight;
+
+ var fps = this.Fps > 0 ? this.Fps : this._movieParams.Fps;
+ stage.TargetElapsedTime = TimeSpan.FromMilliseconds(1000d / fps);
+
+ this.InitStageResource();
+ this._isStageInited = true;
+ }
+
+ ///
+ /// 开始画布播放.
+ ///
+ public void Play () {
+ this.Stage.Paused = false;
+ this.Notify(nameof(this.IsInPlay));
+ }
+
+ ///
+ /// 暂停画布播放.
+ ///
+ public void Pause() {
+ this.Stage.Paused = true;
+ this.Notify(nameof(this.IsInPlay));
+ }
+
+ ///
+ /// 卸载舞台所有数据.
+ ///
+ public void UnloadStage () {
+ this.Pause();
+ this._playedCount = 0;
+ this.CurrentFrame = 0;
+ this._isStageInited = false;
+ this._isResourceReady = false;
+ this._stageResource = null;
+ this._inflatedBytes = null;
+ this._movieEntity = null;
}
public SvgaPlayer () {
this.InitializeComponent();
}
+
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ [NotifyPropertyChangedInvocator]
+ protected virtual void Notify ([CallerMemberName] string propertyName = null) {
+ this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
+ }
}
}