using Age.Engine.Model; namespace Age.Engine.Text; /// /// Transient live-layout presentation state. History persistence owns neither this list nor its Gfx /// bindings; saved-frame script reconstruction rebuilds both. /// public sealed class RetainedAdvTextLayoutPresentation { private readonly List _glyphs = new(); public RetainedAdvTextLayoutPresentation(AdvTextLayoutPresentationBinding binding) { if (binding.LayoutSlot <= 0) throw new ArgumentOutOfRangeException(nameof(binding)); if (binding.SourceSurfaceSlot < 0) throw new ArgumentOutOfRangeException(nameof(binding)); if (binding.FirstObjectHandle < 0 || binding.ObjectCapacity <= 0) throw new ArgumentException( "A retained ADV layout requires a nonnegative first handle and positive capacity.", nameof(binding)); Binding = binding; } public AdvTextLayoutPresentationBinding Binding { get; } public IReadOnlyList Glyphs => _glyphs; public int PublishedGlyphCount { get; private set; } public int PublishableGlyphCount => (int)Math.Min(_glyphs.Count, Binding.ObjectCapacity); public int Append( IReadOnlyList records, int layoutOriginX, int layoutOriginY) { ArgumentNullException.ThrowIfNull(records); int first = _glyphs.Count; foreach (AdvRetainedGlyphRecord record in records) _glyphs.Add(new AdvRetainedGlyphPlacement( record, layoutOriginX, layoutOriginY)); return first; } /// Publish newly revealed glyphs through a layout-global target count. public int PublishThrough(GfxState gfx, int targetGlyphCount) { ArgumentNullException.ThrowIfNull(gfx); int target = Math.Clamp(targetGlyphCount, 0, PublishableGlyphCount); for (int index = PublishedGlyphCount; index < target; index++) Bind(gfx, index); PublishedGlyphCount = Math.Max(PublishedGlyphCount, target); return PublishedGlyphCount; } /// Recreate every currently revealed binding after op 0x20a or temporary suspension. public int Republish(GfxState gfx) { ArgumentNullException.ThrowIfNull(gfx); for (int index = 0; index < PublishedGlyphCount; index++) Bind(gfx, index); return PublishedGlyphCount; } public void ErasePublished(GfxState gfx) { ArgumentNullException.ThrowIfNull(gfx); gfx.EraseRange(Binding.FirstObjectHandle, Binding.ObjectCapacity); } private void Bind(GfxState gfx, int index) { AdvRetainedGlyphPlacement placement = _glyphs[index]; AdvRetainedGlyphRecord record = placement.Record; if (record.Width <= 0 || record.Height <= 0) return; gfx.BindDraw( checked(Binding.FirstObjectHandle + index), Binding.SourceSurfaceSlot, record.Left, record.Top, record.Width, record.Height, checked(placement.LayoutOriginX + record.Left), checked(placement.LayoutOriginY + record.Top)); } }