ImageSharp/SixLabors.Fonts/Tables/AdvancedTypographic/Variations/ItemVariationData.cs
2026-08-03 22:31:27 +02:00

114 lines
6.4 KiB
C#

// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.Diagnostics;
using System.IO;
namespace SixLabors.Fonts.Tables.AdvancedTypographic.Variations {
/// <summary>
/// <para>Item variation data, docs: <see href="https://learn.microsoft.com/en-us/typography/opentype/otspec191alpha/otvarcommonformats_delta#item-variation-store-header-and-item-variation-data-subtables"/></para>
/// </summary>
[DebuggerDisplay("ItemCount: {ItemCount}, WordDeltaCount: {WordDeltaCount}, RegionIndexCount: {RegionIndexes.Length}")]
internal sealed class ItemVariationData
{
/// <summary>
/// Count of "word" deltas.
/// </summary>
private const int WordDeltaCountMask = 0x7FFF;
/// <summary>
/// Flag indicating that "word" deltas are long (int32).
/// </summary>
private const int LongWordsMask = 0x8000;
/// <summary>
/// Initializes a new instance of the <see cref="ItemVariationData"/> class.
/// </summary>
/// <param name="itemCount">The number of delta sets for distinct items.</param>
/// <param name="wordDeltaCount">The packed word delta count field.</param>
/// <param name="regionIndices">The array of region indices referenced by this subtable.</param>
/// <param name="deltaSets">The array of delta set rows.</param>
private ItemVariationData(ushort itemCount, ushort wordDeltaCount, ushort[] regionIndices, DeltaSet[] deltaSets)
{
this.ItemCount = itemCount;
this.WordDeltaCount = wordDeltaCount;
this.RegionIndexes = regionIndices;
this.DeltaSets = deltaSets;
}
/// <summary>
/// Gets the number of delta sets for distinct items.
/// </summary>
public ushort ItemCount { get; }
/// <summary>
/// Gets the packed word delta count field. The high bit is a flag indicating long words;
/// the low 15 bits give the count of word-sized deltas.
/// </summary>
public ushort WordDeltaCount { get; }
/// <summary>
/// Gets the array of indices into the variation region list for the regions referenced by this subtable.
/// </summary>
public ushort[] RegionIndexes { get; }
/// <summary>
/// Gets the array of delta set rows, one per item.
/// </summary>
public DeltaSet[] DeltaSets { get; }
/// <summary>
/// Loads an <see cref="ItemVariationData"/> from the specified binary reader.
/// </summary>
/// <param name="reader">The big-endian binary reader.</param>
/// <param name="offset">The byte offset from the start of the stream to this subtable.</param>
/// <returns>The <see cref="ItemVariationData"/>.</returns>
public static ItemVariationData Load(BigEndianBinaryReader reader, long offset)
{
// ItemVariationData
// +-----------------+----------------------------------------+----------------------------------------------------------------+
// | Type | Name | Description |
// +=================+========================================+================================================================+
// | uint16 | itemCount | The number of delta sets for distinct items. |
// +-----------------+----------------------------------------+----------------------------------------------------------------+
// | uint16 | wordDeltaCount | A packed field: the high bit is a flag. |
// +-----------------+----------------------------------------+----------------------------------------------------------------+
// + uint16 | regionIndexCount | The number of variation regions referenced. |
// +-----------------+----------------------------------------+----------------------------------------------------------------+
// + uint16 | regionIndexes[regionIndexCount] | Array of indices into the variation region list for |
// + | | the regions referenced by this item variation data table. |
// +-----------------+----------------------------------------+----------------------------------------------------------------+
// + DeltaSet | deltaSets[itemCount] | Delta-set rows. |
// +-----------------+----------------------------------------+----------------------------------------------------------------+
reader.Seek(offset, SeekOrigin.Begin);
ushort itemCount = reader.ReadUInt16();
ushort wordDeltaCount = reader.ReadUInt16();
ushort regionIndexCount = reader.ReadUInt16();
ushort[] regionIndexes = new ushort[regionIndexCount];
for (int i = 0; i < regionIndexCount; i++)
{
regionIndexes[i] = reader.ReadUInt16();
}
// The deltaSets array represents a logical two-dimensional table of delta values with itemCount rows and regionIndexCount columns.
// Logically, each DeltaSet record has regionIndexCount number of elements. The elements are represented using long and short types.
// These are either int16 and int8, or int32 and int16, according to whether the LONG_WORDS flag is set.
// The delta array has a sequence of deltas using the long type followed by a sequence of deltas using the short type.
bool longWords = (wordDeltaCount & LongWordsMask) != 0;
int wordDeltas = wordDeltaCount & WordDeltaCountMask;
var deltaSets = new DeltaSet[itemCount];
for (int i = 0; i < itemCount; i++)
{
var deltaSet = new DeltaSet(reader, wordDeltas, longWords, regionIndexCount);
deltaSets[i] = deltaSet;
}
return new ItemVariationData(itemCount, wordDeltaCount, regionIndexes, deltaSets);
}
/// <inheritdoc />
public override int GetHashCode() => HashCode.Combine(this.ItemCount, this.WordDeltaCount, this.RegionIndexes);
}
}