Merge pull request #5055 from harfbuzz/trak-interpolate

[trak] Interpolate between tracks
This commit is contained in:
Behdad Esfahbod 2025-02-09 03:01:21 +00:00 committed by GitHub
commit b9675d6d40
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 87 additions and 59 deletions

View file

@ -48,17 +48,69 @@ struct TrackTableEntry
float get_track_value () const { return track.to_float (); }
int get_value (const void *base, unsigned int index,
unsigned int table_size) const
{ return (base+valuesZ).as_array (table_size)[index]; }
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];
// Deal with font bugs.
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;
/*
* Choose size.
*/
if (!n_sizes) return 0.f;
if (n_sizes == 1) return values[0];
// At least two entries.
unsigned i;
for (i = 0; i < n_sizes; i++)
if (size_table[i].to_float () >= ptem)
break;
// Boundary conditions.
if (i == 0) return values[0];
if (i == n_sizes) return values[n_sizes - 1];
// Exact match.
if (size_table[i].to_float () == ptem) return values[i];
// Interpolate.
return interpolate_at (i - 1, ptem, base, size_table);
}
public:
bool sanitize (hb_sanitize_context_t *c, const void *base,
unsigned int table_size) const
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, table_size))));
(valuesZ.sanitize (c, base, n_sizes))));
}
protected:
@ -76,61 +128,38 @@ struct TrackTableEntry
struct TrackData
{
float interpolate_at (unsigned int idx,
float target_size,
const TrackTableEntry &trackTableEntry,
const void *base) const
float get_tracking (const void *base, float ptem, float track = 0.f) const
{
unsigned int sizes = nSizes;
hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
unsigned count = nTracks;
hb_array_t<const F16DOT16> size_table = (base+sizeTable).as_array (nSizes);
float s0 = size_table[idx].to_float ();
float s1 = size_table[idx + 1].to_float ();
float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0);
return t * trackTableEntry.get_value (base, idx + 1, sizes) +
(1.f - t) * trackTableEntry.get_value (base, idx, sizes);
}
if (!count) return 0.f;
if (count == 1) return trackTable[0].get_value (ptem, base, size_table);
int get_tracking (const void *base, float ptem) const
{
/*
* Choose track.
*/
const TrackTableEntry *trackTableEntry = nullptr;
unsigned int count = nTracks;
float last_trak = 1e5;
for (unsigned int i = 0; i < count; i++)
{
/* Note: Seems like the track entries are sorted by values. But the
* spec doesn't explicitly say that. It just mentions it in the example. */
// At least two entries.
/* Not sure what CoreText does, but it looks to apply a trak=1.0 by default
* if there is no 0.0 trak. So, just pick the one closest to 0.0. */
unsigned i = 0;
unsigned j = count - 1;
float trak = trackTable[i].get_track_value ();
if (fabsf (trak) < fabsf (last_trak))
{
trackTableEntry = &trackTable[i];
break;
}
}
if (!trackTableEntry) return 0;
// Find the two entries that track is between.
while (i + 1 < count && trackTable[i + 1].get_track_value () < track)
i++;
while (j > 0 && trackTable[j - 1].get_track_value () > track)
j--;
/*
* Choose size.
*/
unsigned int sizes = nSizes;
if (!sizes) return 0;
if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
// Exact match.
if (i == j) return trackTable[i].get_value (ptem, base, size_table);
hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes);
unsigned int size_index;
for (size_index = 0; size_index < sizes - 1; size_index++)
if (size_table[size_index].to_float () >= ptem)
break;
// Interpolate.
return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem,
*trackTableEntry, base));
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
@ -178,7 +207,7 @@ struct trak
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
{
const TrackData &trackData = this+horizData;
int tracking = trackData.get_tracking (this, ptem);
float tracking = trackData.get_tracking (this, ptem);
hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2);
hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
foreach_grapheme (buffer, start, end)
@ -191,7 +220,7 @@ struct trak
else
{
const TrackData &trackData = this+vertData;
int tracking = trackData.get_tracking (this, ptem);
float tracking = trackData.get_tracking (this, ptem);
hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2);
hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
foreach_grapheme (buffer, start, end)

View file

@ -4,8 +4,7 @@
../fonts/TRAK.ttf;--font-ptem=2;U+0041,U+0042,U+0043;[A.alt=0@100,0+1200|B=1@100,0+1200|C.alt=2@100,0+1200]
../fonts/TRAK.ttf;--font-ptem=9;U+0041,U+0042,U+0043;[A.alt=0@30,0+1060|B=1@30,0+1060|C.alt=2@30,0+1060]
../fonts/TRAK.ttf;--font-ptem=24;U+0041,U+0042,U+0043;[A.alt=0@-7,0+986|B=1@-7,0+986|C.alt=2@-7,0+986]
../fonts/TRAK.ttf;--font-ptem=72;U+0041,U+0042,U+0043;[A.alt=0@-35,0+929|B=1@-35,0+929|C.alt=2@-35,0+929]
../fonts/TRAK.ttf;--font-ptem=144;U+0041,U+0042,U+0043;[A.alt=0@-78,0+843|B=1@-78,0+843|C.alt=2@-78,0+843]
../fonts/TRAK.ttf;--font-ptem=144;U+0041,U+0042,U+0043;[A.alt=0@-78,0+843|B=1@-78,0+843|C.alt=2@-78,0+843]
../fonts/TRAK.ttf;--font-ptem=72;U+0041,U+0042,U+0043;[A.alt=0@-36,0+929|B=1@-36,0+929|C.alt=2@-36,0+929]
../fonts/TRAK.ttf;--font-ptem=144;U+0041,U+0042,U+0043;[A.alt=0@-50,0+900|B=1@-50,0+900|C.alt=2@-50,0+900]
../fonts/TRAK.ttf;--font-ptem=144 --features=-trak;U+0041,U+0042,U+0043;[A.alt=0+1000|B=1+1000|C.alt=2+1000]
../fonts/TRAK.ttf;--font-ptem=144 --features=-trak[1:3];U+0041,U+0042,U+0043,U+0041,U+0042,U+0043;[A.alt=0@-78,0+843|B=1+1000|C.alt=2+1000|A.alt=3@-78,0+843|B=4@-78,0+843|C.alt=5@-78,0+843]
../fonts/TRAK.ttf;--font-ptem=144 --features=-trak[1:3];U+0041,U+0042,U+0043,U+0041,U+0042,U+0043;[A.alt=0@-50,0+900|B=1+1000|C.alt=2+1000|A.alt=3@-50,0+900|B=4@-50,0+900|C.alt=5@-50,0+900]