add gvar::get_extents

This commit is contained in:
Michiharu Ariza 2019-03-12 11:03:53 -07:00
parent 7b27fe5255
commit 628d457c87
4 changed files with 175 additions and 14 deletions

View File

@ -72,6 +72,7 @@
HB_OT_TABLE(OT, fvar) \
HB_OT_TABLE(OT, avar) \
HB_OT_TABLE(OT, MVAR) \
HB_OT_ACCELERATOR(OT, gvar) \
/* OpenType math. */ \
HB_OT_TABLE(OT, MATH) \
/* OpenType color fonts. */ \

View File

@ -42,6 +42,7 @@
#include "hb-ot-post-table.hh"
#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise.
#include "hb-ot-vorg-table.hh"
#include "hb-ot-var-gvar-table.hh"
#include "hb-ot-color-cbdt-table.hh"
#include "hb-ot-color-sbix-table.hh"
@ -181,6 +182,8 @@ hb_ot_get_glyph_extents (hb_font_t *font,
{
const hb_ot_face_t *ot_face = (const hb_ot_face_t *) font_data;
bool ret = ot_face->sbix->get_extents (font, glyph, extents);
if (!ret)
ret = ot_face->gvar->get_extents (font, glyph, extents);
if (!ret)
ret = ot_face->glyf->get_extents (glyph, extents);
if (!ret)

View File

@ -306,7 +306,7 @@ struct glyf
bool in_range (const T *p) const
{
return ((const char *) p) >= table + start_offset
&& ((const char *) (p + T::static_size)) <= table + end_offset;
&& ((const char *) p + T::static_size) <= table + end_offset;
}
protected:
@ -366,9 +366,9 @@ struct glyf
* in both cases points trailed with four phantom points
*/
bool get_contour_points (hb_codepoint_t glyph,
bool phantom_only,
hb_vector_t<contour_point_t> &_points /* OUT */,
hb_vector_t<unsigned int> &_end_points /* OUT */) const
hb_vector_t<unsigned int> &_end_points /* OUT */,
const bool phantom_only=false) const
{
unsigned int num_points = 0;
unsigned int start_offset, end_offset;
@ -377,8 +377,17 @@ struct glyf
if (end_offset - start_offset < GlyphHeader::static_size)
return false;
glyf::CompositeGlyphHeader::Iterator composite;
if (get_composite (glyph, &composite))
{
/* For a composite glyph, add one pseudo point for each component */
do { num_points++; } while (composite.move_to_next());
_points.resize (num_points + PHANTOM_COUNT);
for (unsigned int i = 0; i < _points.length; i++) _points[i].init ();
return true;
}
const GlyphHeader &glyph_header = StructAtOffset<GlyphHeader> (glyf_table, start_offset);
if (unlikely (glyph_header.numberOfContours < 0)) return false;
int16_t num_contours = (int16_t) glyph_header.numberOfContours;
const HBUINT16 *end_pts = &StructAfter<HBUINT16, GlyphHeader> (glyph_header);
@ -428,8 +437,8 @@ struct glyf
}
/* Read x & y coordinates */
return (!read_points<x_setter_t> (p, _points, checker) &&
!read_points<y_setter_t> (p, _points, checker));
return (read_points<x_setter_t> (p, _points, checker) &&
read_points<y_setter_t> (p, _points, checker));
}
/* based on FontTools _g_l_y_f.py::trim */

View File

@ -31,6 +31,8 @@
#include "hb-ot-glyf-table.hh"
#include "hb-ot-var-fvar-table.hh"
#include <float.h>
/*
* gvar -- Glyph Variation Table
* https://docs.microsoft.com/en-us/typography/opentype/spec/gvar
@ -376,6 +378,24 @@ struct gvar
glyf_accel.fini ();
}
protected:
typedef glyf::accelerator_t glyf_acc_t;
static float infer_delta (float target_val, float pre_val, float fol_val,
float pre_delta, float fol_delta)
{
if (pre_val == fol_val)
return (pre_delta == fol_delta)? pre_delta: 0.f;
else if (target_val <= MIN (pre_val, fol_val))
return (pre_val < fol_val) ? pre_delta: fol_delta;
else if (target_val >= MAX (pre_val, fol_val))
return (pre_val > fol_val)? pre_delta: fol_delta;
/* linear interpolation */
float r = (target_val - pre_val) / (fol_val - pre_val);
return (1.f - r) * pre_delta + r * fol_delta;
}
bool apply_deltas_to_points (hb_codepoint_t glyph,
const int *coords, unsigned int coord_count,
const hb_array_t<contour_point_t> points,
@ -391,6 +411,11 @@ struct gvar
&iterator))
return false;
hb_vector_t<contour_point_t> deltas; /* flag is used to indicate referenced point */
deltas.resize (points.length);
for (unsigned int i = 0; i < deltas.length; i++)
deltas[i].init ();
do {
float scalar = iterator.current_tuple->calculate_scalar (coords, coord_count, shared_tuples.as_array ());
if (scalar == 0.f) continue;
@ -419,12 +444,49 @@ struct gvar
for (unsigned int i = 0; i < num_deltas; i++)
{
unsigned int pt_index = apply_to_all? i: indices[i];
points[pt_index].x += x_deltas[i] * scalar;
points[pt_index].y += y_deltas[i] * scalar;
deltas[pt_index].flag = 1; /* this point is referenced, i.e., explicit deltas specified */
deltas[pt_index].x += x_deltas[i] * scalar;
deltas[pt_index].y += y_deltas[i] * scalar;
}
/* TODO: interpolate untouched points for glyph extents */
} while (iterator.move_to_next ());
/* infer deltas for unreferenced points */
unsigned int start_point = 0;
for (unsigned int c = 0; c < end_points.length; c++)
{
unsigned int end_point = end_points[c];
for (unsigned int i = start_point; i < end_point; i++)
{
if (deltas[i].flag) continue;
/* search in both directions within the contour for a pair of referenced points */
unsigned int pre;
for (pre = i;;)
{
if (pre-- <= start_point) pre = end_point;
if (pre == i || deltas[pre].flag) break;
}
if (pre == i) continue; /* no (preceeding) referenced point was found */
unsigned int fol;
for (fol = i;;)
{
if (fol++ >= end_point) fol = start_point;
if (fol == i || deltas[fol].flag) break;
}
assert (fol != i);
deltas[i].x = infer_delta (points[i].x, points[pre].x, points[fol].x, deltas[pre].x, deltas[fol].x);
deltas[i].y = infer_delta (points[i].y, points[pre].y, points[fol].y, deltas[pre].y, deltas[fol].y);
}
start_point = end_point + 1;
}
/* apply accumulated / inferred deltas to points */
for (unsigned int i = 0; i < points.length; i++)
{
points[i].x += deltas[i].x;
points[i].y += deltas[i].y;
}
return true;
}
@ -435,11 +497,11 @@ struct gvar
{
hb_vector_t<contour_point_t> points;
hb_vector_t<unsigned int> end_points;
if (!glyf_accel.get_contour_points (glyph, true, points, end_points)) return false;
if (!glyf_accel.get_contour_points (glyph, points, end_points, true/*phantom_only*/)) return false;
if (!apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ())) return false;
for (unsigned int i = 0; i < glyf::accelerator_t::PHANTOM_COUNT; i++)
phantoms[i] = points[points.length - glyf::accelerator_t::PHANTOM_COUNT + i];
for (unsigned int i = 0; i < glyf_acc_t::PHANTOM_COUNT; i++)
phantoms[i] = points[points.length - glyf_acc_t::PHANTOM_COUNT + i];
glyf::CompositeGlyphHeader::Iterator composite;
if (!glyf_accel.get_composite (glyph, &composite)) return true; /* simple glyph */
@ -453,6 +515,57 @@ struct gvar
return true;
}
struct bounds_t
{
bounds_t () { min.x = min.y = FLT_MAX; max.x = max.y = FLT_MIN; }
void add (const contour_point_t &p)
{
min.x = MIN (min.x, p.x);
min.y = MIN (min.y, p.y);
max.x = MAX (max.x, p.x);
max.y = MAX (max.y, p.y);
}
void _union (const bounds_t &b)
{ add (b.min); add (b.max); }
contour_point_t min;
contour_point_t max;
};
/* Note: Recursively calls itself. Who's checking recursively nested composite glyph BTW? */
bool get_bounds_var (hb_codepoint_t glyph,
const int *coords, unsigned int coord_count,
bounds_t &bounds) const
{
hb_vector_t<contour_point_t> points;
hb_vector_t<unsigned int> end_points;
if (!glyf_accel.get_contour_points (glyph, points, end_points)) return false;
if (!apply_deltas_to_points (glyph, coords, coord_count, points.as_array (), end_points.as_array ())) return false;
glyf::CompositeGlyphHeader::Iterator composite;
if (!glyf_accel.get_composite (glyph, &composite))
{
/* simple glyph */
for (unsigned int i = 0; i + glyf_acc_t::PHANTOM_COUNT < points.length; i++)
bounds.add (points[i]);
return true;
}
/* composite glyph */
do
{
bounds_t comp_bounds;
if (!get_bounds_var (composite.current->glyphIndex, coords, coord_count, comp_bounds))
return false;
/* TODO: support component scale/transformation */
bounds._union (comp_bounds);
} while (composite.move_to_next());
return true;
}
public:
float get_advance_var (hb_codepoint_t glyph,
const int *coords, unsigned int coord_count,
bool vertical) const
@ -461,15 +574,48 @@ struct gvar
if (coord_count != gvar_table->axisCount) return advance;
hb_vector_t<contour_point_t> points;
points.resize (glyf::accelerator_t::PHANTOM_COUNT);
points.resize (glyf_acc_t::PHANTOM_COUNT);
if (!get_var_metrics (glyph, coords, coord_count, points))
return advance;
if (vertical)
return -(points[glyf::accelerator_t::PHANTOM_BOTTOM].y - points[glyf::accelerator_t::PHANTOM_TOP].y); // is this sign correct?
return -(points[glyf_acc_t::PHANTOM_BOTTOM].y - points[glyf_acc_t::PHANTOM_TOP].y); // is this sign correct?
else
return points[glyf::accelerator_t::PHANTOM_RIGHT].x - points[glyf::accelerator_t::PHANTOM_LEFT].x;
return points[glyf_acc_t::PHANTOM_RIGHT].x - points[glyf_acc_t::PHANTOM_LEFT].x;
}
bool get_extents (hb_font_t *font, hb_codepoint_t glyph,
hb_glyph_extents_t *extents) const
{
unsigned int coord_count;
const int *coords = hb_font_get_var_coords_normalized (font, &coord_count);
if (!coords || coord_count != gvar_table->axisCount) return false; /* fallback on glyf */
bounds_t bounds;
if (unlikely (!get_bounds_var (glyph, coords, coord_count, bounds))) return false;
if (bounds.min.x >= bounds.max.x)
{
extents->width = 0;
extents->x_bearing = 0;
}
else
{
extents->x_bearing = (int32_t)floorf (bounds.min.x);
extents->width = (int32_t)ceilf (bounds.max.x) - extents->x_bearing;
}
if (bounds.min.y >= bounds.max.y)
{
extents->height = 0;
extents->y_bearing = 0;
}
else
{
extents->y_bearing = (int32_t)ceilf (bounds.max.y);
extents->height = (int32_t)floorf (bounds.min.y) - extents->y_bearing;
}
return true;
}
protected:
@ -590,6 +736,8 @@ struct gvar
DEFINE_SIZE_MIN (20);
};
struct gvar_accelerator_t : gvar::accelerator_t {};
} /* namespace OT */
#endif /* HB_OT_VAR_GVAR_TABLE_HH */