// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Numerics;
using SixLabors.Fonts.Rendering;
using SixLabors.Fonts.Tables.TrueType.Glyphs;
namespace SixLabors.Fonts.Tables.General.Colr {
///
/// Supplies painted glyphs for COLR v0 fonts.
/// Flattens paint graphs into a linear stream and emits a .
///
internal sealed class ColrV0GlyphSource : ColrGlyphSourceBase
{
///
/// Cache of previously resolved painted glyphs keyed by glyph ID.
///
private readonly ConcurrentDictionary cachedGlyphs = [];
///
/// Initializes a new instance of the class.
///
/// The COLR table.
/// The CPAL table, or null if not present.
/// Delegate that loads a glyph outline for the given glyph id.
public ColrV0GlyphSource(ColrTable colr, CpalTable? cpal, Func glyphLoader)
: base(colr, cpal, glyphLoader)
{
}
///
public override bool TryGetPaintedGlyph(ushort glyphId, out PaintedGlyph glyph, out PaintedCanvasMetadata canvas)
{
(PaintedGlyph Glyph, PaintedCanvasMetadata Canvas) result = this.cachedGlyphs.GetOrAdd(glyphId, id =>
{
if (this.Colr.TryGetColrV0Layers(id, out Span resolved))
{
List layers = new(resolved.Length);
for (int i = 0; i < resolved.Length; i++)
{
LayerRecord rl = resolved[i];
GlyphVector? gv = this.GlyphLoader(rl.GlyphId);
if (gv is null || !gv.Value.HasValue())
{
continue;
}
// Build geometry once for this layer.
List path = BuildPath(gv.Value);
// Flatten paint graph: attach composite mode to leaves.
List leafPaints = [];
PaintSolid paint = new() { PaletteIndex = rl.PaletteIndex, Alpha = 1, Format = 2 };
FlattenPaint(paint, Matrix3x2.Identity, CompositeMode.SrcOver, this.Cpal, this.Colr, null, leafPaints);
// Emit one layer per leaf paint.
for (int p = 0; p < leafPaints.Count; p++)
{
// Unlike COLR v1, COLR v0 leaves have no transform so we can reuse the same path.
Rendering.Paint leaf = leafPaints[p];
layers.Add(new PaintedLayer(leaf, FillRule.NonZero, leaf.Transform, null, path));
}
}
if (layers.Count > 0)
{
// Canvas viewBox in Y-up; renderer downstream decides orientation via flag.
PaintedGlyph glyph = new(layers);
PaintedCanvasMetadata canvas = new(FontRectangle.Empty, isYDown: false, rootTransform: Matrix3x2.Identity);
return (glyph, canvas);
}
}
return (default, default);
});
glyph = result.Glyph;
canvas = result.Canvas;
return result.Glyph.Layers.Count > 0;
}
}
}