#ifndef HB_AAT_LAYOUT_TRAK_TABLE_HH
#define HB_AAT_LAYOUT_TRAK_TABLE_HH
#include "hb-aat-layout-common.hh"
#include "hb-ot-layout.hh"
#include "hb-open-type.hh"
#include "hb-ot-stat-table.hh"
#define HB_AAT_TAG_trak HB_TAG('t','r','a','k')
namespace AAT {
struct TrackTableEntry
{
friend struct TrackData;
float get_track_value () const { return track.to_float (); }
float interpolate_at (unsigned int idx,
float ptem,
const void *base,
hb_array_t<const F16DOT16> size_table) const
{
const FWORD *values = (base+valuesZ).arrayZ;
float s0 = size_table[idx].to_float ();
float s1 = size_table[idx + 1].to_float ();
int v0 = values[idx];
int v1 = values[idx + 1];
if (unlikely (s1 < s0))
{ hb_swap (s0, s1); hb_swap (v0, v1); }
if (unlikely (ptem < s0)) return v0;
if (unlikely (ptem > s1)) return v1;
if (unlikely (s0 == s1)) return (v0 + v1) * 0.5f;
float t = (ptem - s0) / (s1 - s0);
return v0 + t * (v1 - v0);
}
float get_value (float ptem,
const void *base,
hb_array_t<const F16DOT16> size_table) const
{
const FWORD *values = (base+valuesZ).arrayZ;
unsigned int n_sizes = size_table.length;
if (!n_sizes) return 0.f;
if (n_sizes == 1) return values[0];
unsigned i;
for (i = 0; i < n_sizes; i++)
if (size_table[i].to_float () >= ptem)
break;
if (i == 0) return values[0];
if (i == n_sizes) return values[n_sizes - 1];
if (size_table[i].to_float () == ptem) return values[i];
return interpolate_at (i - 1, ptem, base, size_table);
}
public:
bool sanitize (hb_sanitize_context_t *c,
const void *base,
unsigned int n_sizes) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
(valuesZ.sanitize (c, base, n_sizes))));
}
protected:
F16DOT16 track;
OT::NameID trackNameID;
NNOffset16To<UnsizedArrayOf<FWORD>>
valuesZ;
public:
DEFINE_SIZE_STATIC (8);
};
struct TrackData
{
float get_tracking (const void *base, float ptem, float track = 0.f) const
{
unsigned count = nTracks;
hb_array_t<const F16DOT16> size_table = (base+sizeTable).as_array (nSizes);
if (!count) return 0.f;
if (count == 1) return trackTable[0].get_value (ptem, base, size_table);
unsigned i = 0;
unsigned j = count - 1;
while (i + 1 < count && trackTable[i + 1].get_track_value () <= track)
i++;
while (j > 0 && trackTable[j - 1].get_track_value () >= track)
j--;
if (i == j) return trackTable[i].get_value (ptem, base, size_table);
float t0 = trackTable[i].get_track_value ();
float t1 = trackTable[j].get_track_value ();
float t = (track - t0) / (t1 - t0);
float a = trackTable[i].get_value (ptem, base, size_table);
float b = trackTable[j].get_value (ptem, base, size_table);
return a + t * (b - a);
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
sizeTable.sanitize (c, base, nSizes) &&
trackTable.sanitize (c, nTracks, base, nSizes)));
}
protected:
HBUINT16 nTracks;
HBUINT16 nSizes;
NNOffset32To<UnsizedArrayOf<F16DOT16>>
sizeTable;
UnsizedArrayOf<TrackTableEntry>
trackTable;
public:
DEFINE_SIZE_ARRAY (8, trackTable);
};
struct trak
{
static constexpr hb_tag_t tableTag = HB_AAT_TAG_trak;
bool has_data () const { return version.to_int (); }
hb_position_t get_h_tracking (hb_font_t *font, float track = 0.f) const
{
float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
return font->em_scalef_x ((this+horizData).get_tracking (this, ptem, track));
}
hb_position_t get_v_tracking (hb_font_t *font, float track = 0.f) const
{
float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
return font->em_scalef_y ((this+vertData).get_tracking (this, ptem, track));
}
hb_position_t get_tracking (hb_font_t *font, hb_direction_t dir, float track = 0.f) const
{
#ifndef HB_NO_STYLE
if (!font->face->table.STAT->has_data ())
return 0;
return HB_DIRECTION_IS_HORIZONTAL (dir) ?
get_h_tracking (font, track) :
get_v_tracking (font, track);
#else
return 0;
#endif
}
bool apply (hb_aat_apply_context_t *c, float track = 0.f) const
{
TRACE_APPLY (this);
float ptem = c->font->ptem;
if (unlikely (ptem <= 0.f))
{
ptem = HB_CORETEXT_DEFAULT_FONT_SIZE;
}
hb_buffer_t *buffer = c->buffer;
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
{
hb_position_t advance_to_add = get_h_tracking (c->font, track);
foreach_grapheme (buffer, start, end)
buffer->pos[start].x_advance += advance_to_add;
}
else
{
hb_position_t advance_to_add = get_v_tracking (c->font, track);
foreach_grapheme (buffer, start, end)
buffer->pos[start].y_advance += advance_to_add;
}
return_trace (true);
}
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) &&
hb_barrier () &&
version.major == 1 &&
horizData.sanitize (c, this, this) &&
vertData.sanitize (c, this, this)));
}
protected:
FixedVersion<>version;
HBUINT16 format;
Offset16To<TrackData>
horizData;
Offset16To<TrackData>
vertData;
HBUINT16 reserved;
public:
DEFINE_SIZE_STATIC (12);
};
}
#endif