using System;
using System.Collections.Generic;
using Amphora;
using Azimuth;
using Blacklight.Core.Drawables;
using Glass.Text;
using Prism;
namespace Glass.Blacklight
{
///
/// A tint texture and where it sits on the glyphs it tints.
///
public struct TintLook
{
// Null for no tint texture, in which case the other two make no difference.
public ITexture Texture;
public AffineMatrix Transform;
public TintSpace Space;
public readonly bool Matches(in TintLook other)
{
return ReferenceEquals(Texture, other.Texture) && Transform == other.Transform && Space == other.Space;
}
}
///
/// The tint and coverage gamma that text draws with where no span says otherwise.
///
public struct BaseTint
{
public Color Color;
public Scalar Gamma;
public TintLook Look;
public readonly bool Matches(in BaseTint other)
{
return Color.ToArgb() == other.Color.ToArgb() && Gamma == other.Gamma && Look.Matches(in other.Look);
}
}
///
/// Works out how the glyphs of each span of some text draw, and which of the text's groups they belong to. A group
/// is the glyphs sharing one tint texture look, since a sprite batch has just the one.
///
public class SpanLooks
{
// The group of glyphs that no span covers, and of any span whose texture look is the base's.
public const int BaseGroup = 0;
private readonly List groupLooks = new List();
private Color[] colors = Array.Empty();
private Scalar[] gammas = Array.Empty();
private int[] groups = Array.Empty();
private BaseTint baseTint;
public int GroupCount => groupLooks.Count;
///
/// Gets the per-glyph properties that some span draws differently from the base, which a sprite batch has to
/// vary. A span that sets one to what the base already is doesn't count.
///
public SpriteBatch.Varying Varying { get; private set; }
///
/// Returns the texture look the glyphs of a group draw with.
///
public TintLook LookOf(int group)
{
return groupLooks[group];
}
///
/// Returns the group the glyphs of a span belong to, where -1 stands for the glyphs no span covers.
///
public int GroupOf(int span)
{
return span < 0 ? BaseGroup : groups[span];
}
public Color ColorOf(int span)
{
return span < 0 ? baseTint.Color : colors[span];
}
public Scalar GammaOf(int span)
{
return span < 0 ? baseTint.Gamma : gammas[span];
}
private int GroupFor(in TintLook look)
{
for (int i = 0; i < groupLooks.Count; ++i)
{
TintLook existing = groupLooks[i];
if (existing.Matches(in look))
{
return i;
}
}
groupLooks.Add(look);
return groupLooks.Count - 1;
}
///
/// Works out each span's look over `baseTint`, replacing whatever was worked out before.
///
/// Whether the base color beats every span's, as a render override does.
public void Resolve(DynamicArray spans, in BaseTint baseTint, bool colorOverridden)
{
this.baseTint = baseTint;
groupLooks.Clear();
groupLooks.Add(baseTint.Look);
Varying = SpriteBatch.Varying.None;
int count = spans?.Count ?? 0;
if (colors.Length < count)
{
int length = Math.Max(count, 2 * colors.Length);
colors = new Color[length];
gammas = new Scalar[length];
groups = new int[length];
}
for (int i = 0; i < count; ++i)
{
TextStyle style = spans[i].Style;
Color color = baseTint.Color;
if (!colorOverridden && style.TintColor is Color spanColor)
{
color = spanColor;
}
if (color.ToArgb() != baseTint.Color.ToArgb())
{
Varying |= SpriteBatch.Varying.TintColor;
}
Scalar gamma = style.CoverageGamma ?? baseTint.Gamma;
if (gamma != baseTint.Gamma)
{
Varying |= SpriteBatch.Varying.CoverageGamma;
}
TintLook look = baseTint.Look;
look.Texture = style.TintTexture ?? look.Texture;
if (look.Texture != null)
{
look.Transform = style.TintTransform ?? look.Transform;
look.Space = style.TintSpace ?? look.Space;
}
colors[i] = color;
gammas[i] = gamma;
groups[i] = GroupFor(in look);
}
}
}
}