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