// Copyright © 2017-2025 QL-Win Contributors
//
// This file is part of QuickLook program.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
using Com.Opensource.Svga;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
namespace QuickLook.Plugin.ImageViewer.Webview.Svga;
///
/// Migrate from SVGAPlayer.Data.cs
/// https://github.com/svga/SVGAPlayer-UWP/blob/master/Svga/SvgaPlayer/Controls/SvgaPlayer.Data.cs
///
public partial class SvgaPlayer
{
///
/// Original binary data of the SVGA file
///
private byte[] _inflatedBytes;
///
/// SVGA configuration parameters.
///
private MovieParams _movieParams;
///
/// List of SVGA Sprite Entities
///
private List _sprites;
///
/// Number of Sprites
///
private int _spriteCount;
public int SpriteCount
{
get => _spriteCount;
set => _spriteCount = value;
}
///
/// Number of playback loops, default is 0
/// When 0, it means infinite loop playback
///
public int LoopCount { get; set; }
///
/// Current playback frame
///
private int _currentFrame;
public int CurrentFrame
{
get => _currentFrame;
private set => _currentFrame = value;
}
///
/// Whether it is in playing state
///
private bool _isInPlay;
public bool IsInPlay
{
get => _isInPlay;
set => _isInPlay = value;
}
///
/// Total number of animation frames
///
private int _totalFrame;
public int TotalFrame
{
get => _totalFrame;
private set => _totalFrame = value;
}
///
/// Target playback frame rate
/// If not set or set to 0, the default frame rate is used. If set, the custom frame rate is used
///
private int _fps;
public int Fps
{
get => _fps;
set
{
if (value < 0) { value = 0; }
_fps = value;
}
}
///
/// Canvas width
///
private float _stageWidth;
public float StageWidth
{
get => _stageWidth;
set => _stageWidth = value;
}
///
/// Canvas height
///
private float _stageHeight;
public float StageHeight
{
get => _stageHeight;
set => _stageHeight = value;
}
///
/// Inflate the SVGA file to get its original data
/// The SVGA file has been deflated, so the first step is to inflate it
///
private void InflateSvgaFile(Stream svgaFileBuffer)
{
byte[] inflatedBytes;
// The built-in DeflateStream in Microsoft .NET does not recognize the first two bytes of the file header. For SVGA, these two bytes are 78 9C, which is the default compression indicator for Deflate
// For more information, see https://stackoverflow.com/questions/17212964/net-zlib-inflate-with-net-4-5
// For Zlib file header, see https://stackoverflow.com/questions/9050260/what-does-a-zlib-header-look-like
svgaFileBuffer.Seek(2, SeekOrigin.Begin);
using (var deflatedStream = new DeflateStream(svgaFileBuffer, CompressionMode.Decompress))
{
using var stream = new MemoryStream();
deflatedStream.CopyTo(stream);
inflatedBytes = stream.ToArray();
}
_inflatedBytes = inflatedBytes;
}
///
/// Get the SVGA MovieEntity from the inflated data
///
///
private void InitMovieEntity()
{
if (_inflatedBytes == null)
{
return;
}
var moveEntity = MovieEntity.Parser.ParseFrom(_inflatedBytes);
_movieParams = moveEntity.Params;
_sprites = [.. moveEntity.Sprites];
TotalFrame = moveEntity.Params.Frames;
SpriteCount = _sprites.Count;
StageWidth = _movieParams.ViewBoxWidth;
StageHeight = _movieParams.ViewBoxHeight;
}
///
/// Load SVGA file data
///
/// SVGA file binary Stream
public void LoadSvgaFileData(Stream svgaFileBuffer)
{
InflateSvgaFile(svgaFileBuffer);
InitMovieEntity();
// Clear the inflated bytes after parsing to free memory
_inflatedBytes = null;
}
}