- Make image result/info structs readonly; improve error handling and memory safety in image library - Introduce IJobScheduler interface; move job scheduling docs to interface - Remove "index 0 invalid" convention from slot/sparse maps; fix Count logic - Add Owner<T> for disposable value types in low-level utilities - Improve ObjectPool<T> thread safety and logic - Change List<T>.RemoveAndSwapBack to return bool - Remove unsafe methods from generated math types; add debug range checks - Update benchmarks and enable collection checks in tests - Improve documentation, comments, and error messages - Bump assembly versions across all projects
156 lines
3.5 KiB
C#
156 lines
3.5 KiB
C#
using Misaki.HighPerformance.Image.Runtime;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
|
|
namespace Misaki.HighPerformance.Image;
|
|
|
|
internal class AnimatedGifEnumerator : IEnumerator<AnimatedFrameResult>
|
|
{
|
|
private readonly StbImage.stbi__context _context;
|
|
private StbImage.stbi__gif _gif;
|
|
private readonly ColorComponents _colorComponents;
|
|
|
|
public AnimatedGifEnumerator(Stream input, ColorComponents colorComponents)
|
|
{
|
|
if (input == null)
|
|
{
|
|
throw new ArgumentNullException("input");
|
|
}
|
|
|
|
_context = new StbImage.stbi__context(input);
|
|
|
|
if (StbImage.stbi__gif_test(_context) == 0)
|
|
{
|
|
throw new Exception("Input stream is not GIF file.");
|
|
}
|
|
|
|
_gif = new StbImage.stbi__gif();
|
|
_colorComponents = colorComponents;
|
|
}
|
|
|
|
public ColorComponents ColorComponents
|
|
{
|
|
get
|
|
{
|
|
return _colorComponents;
|
|
}
|
|
}
|
|
|
|
public AnimatedFrameResult Current
|
|
{
|
|
get; private set;
|
|
}
|
|
|
|
object IEnumerator.Current
|
|
{
|
|
get
|
|
{
|
|
return Current;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
public unsafe bool MoveNext()
|
|
{
|
|
// Read next frame
|
|
int ccomp;
|
|
byte two_back;
|
|
var result = StbImage.stbi__gif_load_next(_context, _gif, &ccomp, (int)ColorComponents, &two_back);
|
|
if (result == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (Current.Image.Data == null)
|
|
{
|
|
Current = new AnimatedFrameResult
|
|
{
|
|
Image = new ImageResult
|
|
{
|
|
Data = result,
|
|
Width = (uint)_gif.w,
|
|
Height = (uint)_gif.h,
|
|
SourceComp = (ColorComponents)ccomp,
|
|
Comp = ColorComponents == ColorComponents.Default ? (ColorComponents)ccomp : ColorComponents,
|
|
},
|
|
DelayInMs = _gif.delay
|
|
};
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
~AnimatedGifEnumerator()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
protected virtual unsafe void Dispose(bool disposing)
|
|
{
|
|
if (_gif != null)
|
|
{
|
|
if (_gif._out_ != null)
|
|
{
|
|
CRuntime.free(_gif._out_);
|
|
_gif._out_ = null;
|
|
}
|
|
|
|
if (_gif.history != null)
|
|
{
|
|
CRuntime.free(_gif.history);
|
|
_gif.history = null;
|
|
}
|
|
|
|
if (_gif.background != null)
|
|
{
|
|
CRuntime.free(_gif.background);
|
|
_gif.background = null;
|
|
}
|
|
|
|
_gif = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class AnimatedGifEnumerable : IEnumerable<AnimatedFrameResult>
|
|
{
|
|
private readonly Stream _input;
|
|
private readonly ColorComponents _colorComponents;
|
|
|
|
public AnimatedGifEnumerable(Stream input, ColorComponents colorComponents)
|
|
{
|
|
_input = input;
|
|
_colorComponents = colorComponents;
|
|
}
|
|
|
|
public ColorComponents ColorComponents
|
|
{
|
|
get
|
|
{
|
|
return _colorComponents;
|
|
}
|
|
}
|
|
|
|
public IEnumerator<AnimatedFrameResult> GetEnumerator()
|
|
{
|
|
return new AnimatedGifEnumerator(_input, ColorComponents);
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return GetEnumerator();
|
|
}
|
|
}
|