namespace Minint.Core.Models; /// /// A single document (frame) within a container. /// Has its own palette shared by all layers, plus a list of layers. /// public sealed class MinintDocument { public string Name { get; set; } /// /// Delay before showing the next frame during animation playback (ms). /// public uint FrameDelayMs { get; set; } /// /// Document palette. Index 0 is always . /// All layers reference colors by index into this list. /// public List Palette { get; } public List Layers { get; } /// /// Reverse lookup cache: RgbaColor → palette index. Built lazily, invalidated /// on structural palette changes (compact, clear). Call /// after bulk palette modifications. /// private Dictionary? _paletteCache; public MinintDocument(string name) { Name = name; FrameDelayMs = 100; Palette = [RgbaColor.Transparent]; Layers = []; } /// /// Constructor for deserialization — accepts pre-built palette and layers. /// public MinintDocument(string name, uint frameDelayMs, List palette, List layers) { Name = name; FrameDelayMs = frameDelayMs; Palette = palette; Layers = layers; } /// /// Returns the number of bytes needed to store a single palette index on disk. /// public int IndexByteWidth => Palette.Count switch { <= 255 => 1, <= 65_535 => 2, <= 16_777_215 => 3, _ => 4 }; /// /// 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. /// public int FindColorCached(RgbaColor color) { var cache = EnsurePaletteCache(); return cache.TryGetValue(color, out int idx) ? idx : -1; } /// /// Returns the index of . If absent, appends it to the palette /// and updates the cache. O(1) amortized. /// 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; } /// /// Drops the reverse lookup cache. Must be called after any operation that /// reorders, removes, or bulk-replaces palette entries (e.g. compact, grayscale). /// public void InvalidatePaletteCache() => _paletteCache = null; private Dictionary EnsurePaletteCache() { if (_paletteCache is not null) return _paletteCache; var cache = new Dictionary(Palette.Count); for (int i = 0; i < Palette.Count; i++) cache.TryAdd(Palette[i], i); // first occurrence wins (for dupes) _paletteCache = cache; return cache; } }