// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System;
using System.IO;
namespace SixLabors.Fonts.Tables.AdvancedTypographic.Variations {
///
/// Represents a single entry in a DeltaSetIndexMap, mapping a glyph ID to an outer/inner index pair
/// into an .
///
///
internal class DeltaSetIndexMap
{
///
/// Mask for the low 4 bits of the entry format, giving the number of inner index bits minus one.
///
private const int InnerIndexBitCountMask = 0x0F;
///
/// Mask for bits 4-5 of the entry format, giving the entry size minus one.
///
private const int MapEntrySizeMask = 0x30;
///
/// Initializes a new instance of the class.
///
/// The outer index into the ItemVariationStore.
/// The inner index into the ItemVariationStore.
public DeltaSetIndexMap(int outerIndex, int innerIndex)
{
this.OuterIndex = outerIndex;
this.InnerIndex = innerIndex;
}
///
/// Gets the outer index into the ItemVariationStore (selects the ItemVariationData subtable).
///
public int OuterIndex { get; }
///
/// Gets the inner index into the ItemVariationStore (selects the delta set within the subtable).
///
public int InnerIndex { get; }
///
/// Loads an array of entries from the specified binary reader.
///
/// The big-endian binary reader.
/// The byte offset from the start of the stream to this map. If zero, no map is present.
/// The array of entries, or if the offset is zero.
public static DeltaSetIndexMap[]? Load(BigEndianBinaryReader reader, long offset)
{
// This can be null if the offset is zero.
if (offset == 0)
{
return null;
}
// DeltaSetIndexMap.
// +-----------------+----------------------------------------+-----------------------------------------------------------------------------------+
// | Type | Name | Description |
// +=================+========================================+===================================================================================+
// | uint8 | format | DeltaSetIndexMap format. Either 0 or 1 |
// +-----------------+----------------------------------------+-----------------------------------------------------------------------------------+
// | uint8 | entryFormat | A packed field that describes the compressed representation of delta-set indices. |
// +-----------------+----------------------------------------+-----------------------------------------------------------------------------------+
// | uint16 or uin32 | mapCount | The number of mapping entries. uint16 for format0, uint32 for format 1 |
// +-----------------+----------------------------------------+-----------------------------------------------------------------------------------+
// | uint8 | mapData[variable] | The delta-set index mapping data. |
// +-----------------+----------------------------------------+-----------------------------------------------------------------------------------+
reader.Seek(offset, SeekOrigin.Begin);
byte format = reader.ReadUInt8();
byte entryFormat = reader.ReadUInt8();
if (format is not (0 or 1))
{
throw new NotSupportedException("Only format 0 or 1 of DeltaSetIndexMap is supported");
}
// Format 0 uses uint16 for mapCount, format 1 uses uint32.
int mapCount = format == 0 ? reader.ReadUInt16() : (int)reader.ReadUInt32();
int entrySize = ((entryFormat & MapEntrySizeMask) >> 4) + 1;
int innerBitCount = (entryFormat & InnerIndexBitCountMask) + 1;
int innerIndexMask = (1 << innerBitCount) - 1;
DeltaSetIndexMap[] deltaSetIndexMaps = new DeltaSetIndexMap[mapCount];
for (int i = 0; i < mapCount; i++)
{
int entry = entrySize switch
{
1 => reader.ReadByte(),
2 => (reader.ReadByte() << 8) | reader.ReadByte(),
3 => (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte(),
4 => (reader.ReadByte() << 24) | (reader.ReadByte() << 16) | (reader.ReadByte() << 8) | reader.ReadByte(),
_ => throw new NotSupportedException("unsupported delta set index map"),
};
deltaSetIndexMaps[i] = new DeltaSetIndexMap(entry >> innerBitCount, entry & innerIndexMask);
}
return deltaSetIndexMaps;
}
}
}