93 lines
2.7 KiB
C#
93 lines
2.7 KiB
C#
namespace Minint.Core.Models;
|
|
|
|
/// <summary>
|
|
/// A single document (frame) within a container.
|
|
/// Has its own palette shared by all layers, plus a list of layers.
|
|
/// </summary>
|
|
public sealed class MinintDocument
|
|
{
|
|
public string Name { get; set; }
|
|
|
|
public uint FrameDelayMs { get; set; }
|
|
|
|
public List<RgbaColor> Palette { get; }
|
|
|
|
public List<MinintLayer> Layers { get; }
|
|
|
|
private Dictionary<RgbaColor, int>? _paletteCache;
|
|
|
|
public MinintDocument(string name)
|
|
{
|
|
Name = name;
|
|
FrameDelayMs = 100;
|
|
Palette = [RgbaColor.Transparent];
|
|
Layers = [];
|
|
}
|
|
|
|
/// <summary>
|
|
/// Constructor for deserialization — accepts pre-built palette and layers.
|
|
/// </summary>
|
|
public MinintDocument(string name, uint frameDelayMs, List<RgbaColor> palette, List<MinintLayer> layers)
|
|
{
|
|
Name = name;
|
|
FrameDelayMs = frameDelayMs;
|
|
Palette = palette;
|
|
Layers = layers;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the number of bytes needed to store a single palette index on disk.
|
|
/// </summary>
|
|
public int IndexByteWidth => Palette.Count switch
|
|
{
|
|
<= 255 => 1,
|
|
<= 65_535 => 2,
|
|
<= 16_777_215 => 3,
|
|
_ => 4
|
|
};
|
|
|
|
/// <summary>
|
|
/// O(1) lookup of a color in the palette. Returns the index, or -1 if not found.
|
|
/// Lazily builds an internal dictionary on first call.
|
|
/// </summary>
|
|
public int FindColorCached(RgbaColor color)
|
|
{
|
|
var cache = EnsurePaletteCache();
|
|
return cache.GetValueOrDefault(color, -1);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the index of <paramref name="color"/>. If absent, appends it to the palette
|
|
/// and updates the cache. O(1) amortized.
|
|
/// </summary>
|
|
public int EnsureColorCached(RgbaColor color)
|
|
{
|
|
var cache = EnsurePaletteCache();
|
|
if (cache.TryGetValue(color, out int idx))
|
|
return idx;
|
|
|
|
idx = Palette.Count;
|
|
Palette.Add(color);
|
|
cache[color] = idx;
|
|
return idx;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Drops the reverse lookup cache. Must be called after any operation that
|
|
/// reorders, removes, or bulk-replaces palette entries (e.g. compact, grayscale).
|
|
/// </summary>
|
|
public void InvalidatePaletteCache() => _paletteCache = null;
|
|
|
|
private Dictionary<RgbaColor, int> EnsurePaletteCache()
|
|
{
|
|
if (_paletteCache is not null)
|
|
return _paletteCache;
|
|
|
|
var cache = new Dictionary<RgbaColor, int>(Palette.Count);
|
|
for (int i = 0; i < Palette.Count; i++)
|
|
cache.TryAdd(Palette[i], i); // first occurrence wins (for dupes)
|
|
_paletteCache = cache;
|
|
return cache;
|
|
}
|
|
}
|