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));
}
}