harfbuzz: Update to 11.3.2

This commit is contained in:
Pāvels Nadtočajevs 2025-07-22 09:41:20 +03:00
parent 71a9948157
commit 37f506c33d
No known key found for this signature in database
GPG key ID: 8413210218EF35D2
72 changed files with 3901 additions and 2395 deletions

View file

@ -436,7 +436,7 @@ Patches:
## harfbuzz
- Upstream: https://github.com/harfbuzz/harfbuzz
- Version: 11.2.1 (33a3f8de60dcad7535f14f07d6710144548853ac, 2025)
- Version: 11.3.2 (4e3df1c1383481ed5717603d5dd3453a04fb16ba, 2025)
- License: MIT
Files extracted from upstream source:
@ -444,7 +444,7 @@ Files extracted from upstream source:
- `AUTHORS`, `COPYING`, `THANKS`
- From the `src` folder, recursively:
- All the `.cc`, `.h`, `.hh` files
- Except `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`, `hb-wasm*.*`, `wasm/*`
- Except `main.cc`, `harfbuzz*.cc`, `failing-alloc.c`, `test*.cc`, `hb-wasm*.*`, `hb-harfrust.cc`, `wasm/*`, `ms-use/*`, `rust/*`
## hidapi

View file

@ -104,7 +104,7 @@ public:
foreground (foreground_),
instancer (instancer_)
{
if (font->is_synthetic ())
if (font->is_synthetic)
{
font = hb_font_create_sub_font (font);
hb_font_set_synthetic_bold (font, 0, 0, true);
@ -1075,9 +1075,9 @@ struct PaintTranslate
float ddx = dx + c->instancer (varIdxBase, 0);
float ddy = dy + c->instancer (varIdxBase, 1);
bool p1 = c->funcs->push_translate (c->data, ddx, ddy);
c->funcs->push_translate (c->data, ddx, ddy);
c->recurse (this+src);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
HBUINT8 format; /* format = 14(noVar) or 15 (Var) */
@ -1124,9 +1124,9 @@ struct PaintScale
float sx = scaleX.to_float (c->instancer (varIdxBase, 0));
float sy = scaleY.to_float (c->instancer (varIdxBase, 1));
bool p1 = c->funcs->push_scale (c->data, sx, sy);
c->funcs->push_scale (c->data, sx, sy);
c->recurse (this+src);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
HBUINT8 format; /* format = 16 (noVar) or 17(Var) */
@ -1177,13 +1177,9 @@ struct PaintScaleAroundCenter
float tCenterX = centerX + c->instancer (varIdxBase, 2);
float tCenterY = centerY + c->instancer (varIdxBase, 3);
bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
bool p2 = c->funcs->push_scale (c->data, sx, sy);
bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
c->funcs->push_scale_around_center (c->data, sx, sy, tCenterX, tCenterY);
c->recurse (this+src);
if (p3) c->funcs->pop_transform (c->data);
if (p2) c->funcs->pop_transform (c->data);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
HBUINT8 format; /* format = 18 (noVar) or 19(Var) */
@ -1228,9 +1224,9 @@ struct PaintScaleUniform
TRACE_PAINT (this);
float s = scale.to_float (c->instancer (varIdxBase, 0));
bool p1 = c->funcs->push_scale (c->data, s, s);
c->funcs->push_scale (c->data, s, s);
c->recurse (this+src);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
HBUINT8 format; /* format = 20 (noVar) or 21(Var) */
@ -1278,13 +1274,9 @@ struct PaintScaleUniformAroundCenter
float tCenterX = centerX + c->instancer (varIdxBase, 1);
float tCenterY = centerY + c->instancer (varIdxBase, 2);
bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
bool p2 = c->funcs->push_scale (c->data, s, s);
bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
c->funcs->push_scale_around_center (c->data, s, s, tCenterX, tCenterY);
c->recurse (this+src);
if (p3) c->funcs->pop_transform (c->data);
if (p2) c->funcs->pop_transform (c->data);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
HBUINT8 format; /* format = 22 (noVar) or 23(Var) */
@ -1328,9 +1320,9 @@ struct PaintRotate
TRACE_PAINT (this);
float a = angle.to_float (c->instancer (varIdxBase, 0));
bool p1 = c->funcs->push_rotate (c->data, a);
c->funcs->push_rotate (c->data, a);
c->recurse (this+src);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
HBUINT8 format; /* format = 24 (noVar) or 25(Var) */
@ -1378,13 +1370,9 @@ struct PaintRotateAroundCenter
float tCenterX = centerX + c->instancer (varIdxBase, 1);
float tCenterY = centerY + c->instancer (varIdxBase, 2);
bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
bool p2 = c->funcs->push_rotate (c->data, a);
bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
c->funcs->push_rotate_around_center (c->data, a, tCenterX, tCenterY);
c->recurse (this+src);
if (p3) c->funcs->pop_transform (c->data);
if (p2) c->funcs->pop_transform (c->data);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
HBUINT8 format; /* format = 26 (noVar) or 27(Var) */
@ -1432,9 +1420,9 @@ struct PaintSkew
float sx = xSkewAngle.to_float(c->instancer (varIdxBase, 0));
float sy = ySkewAngle.to_float(c->instancer (varIdxBase, 1));
bool p1 = c->funcs->push_skew (c->data, sx, sy);
c->funcs->push_skew (c->data, sx, sy);
c->recurse (this+src);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
HBUINT8 format; /* format = 28(noVar) or 29 (Var) */
@ -1485,13 +1473,9 @@ struct PaintSkewAroundCenter
float tCenterX = centerX + c->instancer (varIdxBase, 2);
float tCenterY = centerY + c->instancer (varIdxBase, 3);
bool p1 = c->funcs->push_translate (c->data, +tCenterX, +tCenterY);
bool p2 = c->funcs->push_skew (c->data, sx, sy);
bool p3 = c->funcs->push_translate (c->data, -tCenterX, -tCenterY);
c->funcs->push_skew_around_center (c->data, sx, sy, tCenterX, tCenterY);
c->recurse (this+src);
if (p3) c->funcs->pop_transform (c->data);
if (p2) c->funcs->pop_transform (c->data);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
HBUINT8 format; /* format = 30(noVar) or 31 (Var) */
@ -2693,7 +2677,8 @@ struct COLR
{
ItemVarStoreInstancer instancer (get_var_store_ptr (),
get_delta_set_index_map_ptr (),
hb_array (font->coords, font->num_coords));
hb_array (font->coords,
font->has_nonzero_coords ? font->num_coords : 0));
hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
hb_decycler_node_t node (c.glyphs_decycler);

View file

@ -37,12 +37,12 @@ struct AnchorFormat3
*x = font->em_fscale_x (xCoordinate);
*y = font->em_fscale_y (yCoordinate);
if ((font->x_ppem || font->num_coords) && xDeviceTable.sanitize (&c->sanitizer, this))
if ((font->x_ppem || font->has_nonzero_coords) && xDeviceTable.sanitize (&c->sanitizer, this))
{
hb_barrier ();
*x += (this+xDeviceTable).get_x_delta (font, c->var_store, c->var_store_cache);
}
if ((font->y_ppem || font->num_coords) && yDeviceTable.sanitize (&c->sanitizer, this))
if ((font->y_ppem || font->has_nonzero_coords) && yDeviceTable.sanitize (&c->sanitizer, this))
{
hb_barrier ();
*y += (this+yDeviceTable).get_y_delta (font, c->var_store, c->var_store_cache);
@ -63,7 +63,7 @@ struct AnchorFormat3
hb_pair_t<unsigned, int> *new_varidx_delta;
if (!c->plan->layout_variation_idx_delta_map.has (x_varidx, &new_varidx_delta))
return_trace (false);
x_varidx = hb_first (*new_varidx_delta);
int delta = hb_second (*new_varidx_delta);
if (delta != 0)
@ -91,10 +91,13 @@ struct AnchorFormat3
}
}
/* in case that all axes are pinned or no variations after instantiation,
* both var_idxes will be mapped to HB_OT_LAYOUT_NO_VARIATIONS_INDEX */
if (x_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX &&
y_varidx == HB_OT_LAYOUT_NO_VARIATIONS_INDEX)
bool no_downgrade = (!xDeviceTable.is_null () && !(this+xDeviceTable).is_variation_device ()) ||
x_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX ||
y_varidx != HB_OT_LAYOUT_NO_VARIATIONS_INDEX ||
(!yDeviceTable.is_null () && !(this+yDeviceTable).is_variation_device ());
if (!no_downgrade)
return_trace (c->serializer->check_assign (out->format, 1, HB_SERIALIZE_ERROR_INT_OVERFLOW));
if (!c->serializer->embed (xDeviceTable)) return_trace (false);

View file

@ -56,7 +56,7 @@ struct ValueFormat : HBUINT16
* PosTable (may be NULL) */
#endif
IntType& operator = (uint16_t i) { v = i; return *this; }
NumType& operator = (uint16_t i) { v = i; return *this; }
unsigned int get_len () const { return hb_popcount ((unsigned int) *this); }
unsigned int get_size () const { return get_len () * Value::static_size; }
@ -111,8 +111,8 @@ struct ValueFormat : HBUINT16
if (!has_device ()) return ret;
bool use_x_device = font->x_ppem || font->num_coords;
bool use_y_device = font->y_ppem || font->num_coords;
bool use_x_device = font->x_ppem || font->has_nonzero_coords;
bool use_y_device = font->y_ppem || font->has_nonzero_coords;
if (!use_x_device && !use_y_device) return ret;

View file

@ -11,11 +11,11 @@ namespace GSUB_impl {
template <typename Types>
struct LigatureSet
{
protected:
public:
Array16OfOffset16To<Ligature<Types>>
ligature; /* Array LigatureSet tables
* ordered by preference */
public:
DEFINE_SIZE_ARRAY (2, ligature);
bool sanitize (hb_sanitize_context_t *c) const

View file

@ -115,7 +115,7 @@ struct Sequence
for (unsigned i = c->buffer->idx - count; i < c->buffer->idx; i++)
{
if (buf < p)
if (buf < p && sizeof(buf) - 1u > unsigned (p - buf))
*p++ = ',';
snprintf (p, sizeof(buf) - (p - buf), "%u", i);
p += strlen(p);

View file

@ -13,7 +13,7 @@ namespace OT {
struct hb_transforming_pen_context_t
{
hb_transform_t transform;
hb_transform_t<> transform;
hb_draw_funcs_t *dfuncs;
void *data;
hb_draw_state_t *st;
@ -130,9 +130,9 @@ hb_ubytes_t
VarComponent::get_path_at (const hb_varc_context_t &c,
hb_codepoint_t parent_gid,
hb_array_t<const int> coords,
hb_transform_t total_transform,
hb_transform_t<> total_transform,
hb_ubytes_t total_record,
VarRegionList::cache_t *cache) const
hb_scalar_cache_t *cache) const
{
const unsigned char *end = total_record.arrayZ + total_record.length;
const unsigned char *record = total_record.arrayZ;
@ -216,7 +216,7 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
* limit on the max number of coords for now. */
if ((flags & (unsigned) flags_t::RESET_UNSPECIFIED_AXES) ||
coords.length > HB_VAR_COMPOSITE_MAX_AXES)
component_coords = hb_array<int> (c.font->coords, c.font->num_coords);
component_coords = hb_array (c.font->coords, c.font->num_coords);
// Transform
@ -226,28 +226,28 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
#define PROCESS_TRANSFORM_COMPONENTS \
HB_STMT_START { \
PROCESS_TRANSFORM_COMPONENT (FWORD, HAVE_TRANSLATE_X, translateX); \
PROCESS_TRANSFORM_COMPONENT (FWORD, HAVE_TRANSLATE_Y, translateY); \
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HAVE_ROTATION, rotation); \
PROCESS_TRANSFORM_COMPONENT (F6DOT10, HAVE_SCALE_X, scaleX); \
PROCESS_TRANSFORM_COMPONENT (F6DOT10, HAVE_SCALE_Y, scaleY); \
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HAVE_SKEW_X, skewX); \
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HAVE_SKEW_Y, skewY); \
PROCESS_TRANSFORM_COMPONENT (FWORD, HAVE_TCENTER_X, tCenterX); \
PROCESS_TRANSFORM_COMPONENT (FWORD, HAVE_TCENTER_Y, tCenterY); \
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_X, translateX); \
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TRANSLATE_Y, translateY); \
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_ROTATION, rotation); \
PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_X, scaleX); \
PROCESS_TRANSFORM_COMPONENT (F6DOT10, 1.0f, HAVE_SCALE_Y, scaleY); \
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_X, skewX); \
PROCESS_TRANSFORM_COMPONENT (F4DOT12, HB_PI, HAVE_SKEW_Y, skewY); \
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_X, tCenterX); \
PROCESS_TRANSFORM_COMPONENT (FWORD, 1.0f, HAVE_TCENTER_Y, tCenterY); \
} HB_STMT_END
hb_transform_decomposed_t transform;
hb_transform_decomposed_t<> transform;
// Read transform components
#define PROCESS_TRANSFORM_COMPONENT(type, flag, name) \
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
if (flags & (unsigned) flags_t::flag) \
{ \
static_assert (type::static_size == HBINT16::static_size, ""); \
if (unlikely (unsigned (end - record) < HBINT16::static_size)) \
return hb_ubytes_t (); \
hb_barrier (); \
transform.name = * (const HBINT16 *) record; \
transform.name = mult * * (const HBINT16 *) record; \
record += HBINT16::static_size; \
}
PROCESS_TRANSFORM_COMPONENTS;
@ -279,22 +279,22 @@ VarComponent::get_path_at (const hb_varc_context_t &c,
{
float transformValues[9];
unsigned numTransformValues = 0;
#define PROCESS_TRANSFORM_COMPONENT(type, flag, name) \
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
if (flags & (unsigned) flags_t::flag) \
transformValues[numTransformValues++] = transform.name;
transformValues[numTransformValues++] = transform.name / mult;
PROCESS_TRANSFORM_COMPONENTS;
#undef PROCESS_TRANSFORM_COMPONENT
varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache);
numTransformValues = 0;
#define PROCESS_TRANSFORM_COMPONENT(type, flag, name) \
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
if (flags & (unsigned) flags_t::flag) \
transform.name = transformValues[numTransformValues++];
transform.name = transformValues[numTransformValues++] * mult;
PROCESS_TRANSFORM_COMPONENTS;
#undef PROCESS_TRANSFORM_COMPONENT
}
// Divide them by their divisors
#define PROCESS_TRANSFORM_COMPONENT(type, flag, name) \
#define PROCESS_TRANSFORM_COMPONENT(type, mult, flag, name) \
if (flags & (unsigned) flags_t::flag) \
{ \
HBINT16 int_v; \
@ -334,9 +334,9 @@ bool
VARC::get_path_at (const hb_varc_context_t &c,
hb_codepoint_t glyph,
hb_array_t<const int> coords,
hb_transform_t transform,
hb_transform_t<> transform,
hb_codepoint_t parent_glyph,
VarRegionList::cache_t *parent_cache) const
hb_scalar_cache_t *parent_cache) const
{
// Don't recurse on the same glyph.
unsigned idx = glyph == parent_glyph ?
@ -372,7 +372,7 @@ VARC::get_path_at (const hb_varc_context_t &c,
#endif
return false;
hb_extents_t comp_extents (glyph_extents);
hb_extents_t<> comp_extents (glyph_extents);
transform.transform_extents (comp_extents);
c.extents->union_ (comp_extents);
}
@ -392,10 +392,10 @@ VARC::get_path_at (const hb_varc_context_t &c,
hb_ubytes_t record = (this+glyphRecords)[idx];
VarRegionList::cache_t static_cache[sizeof (void *) * 16];
VarRegionList::cache_t *cache = parent_cache ?
hb_scalar_cache_t static_cache;
hb_scalar_cache_t *cache = parent_cache ?
parent_cache :
(this+varStore).create_cache (hb_array (static_cache));
(this+varStore).create_cache (&static_cache);
transform.scale (c.font->x_multf, c.font->y_multf);
@ -406,7 +406,7 @@ VARC::get_path_at (const hb_varc_context_t &c,
cache);
if (cache != parent_cache)
(this+varStore).destroy_cache (cache, hb_array (static_cache));
(this+varStore).destroy_cache (cache, &static_cache);
return true;
}

View file

@ -32,7 +32,7 @@ struct hb_varc_context_t
{
hb_font_t *font;
hb_draw_session_t *draw_session;
hb_extents_t *extents;
hb_extents_t<> *extents;
mutable hb_decycler_t decycler;
mutable signed edges_left;
mutable signed depth_left;
@ -65,9 +65,9 @@ struct VarComponent
get_path_at (const hb_varc_context_t &c,
hb_codepoint_t parent_gid,
hb_array_t<const int> coords,
hb_transform_t transform,
hb_transform_t<> transform,
hb_ubytes_t record,
VarRegionList::cache_t *cache = nullptr) const;
hb_scalar_cache_t *cache = nullptr) const;
};
struct VarCompositeGlyph
@ -76,9 +76,9 @@ struct VarCompositeGlyph
get_path_at (const hb_varc_context_t &c,
hb_codepoint_t gid,
hb_array_t<const int> coords,
hb_transform_t transform,
hb_transform_t<> transform,
hb_ubytes_t record,
VarRegionList::cache_t *cache)
hb_scalar_cache_t *cache)
{
while (record)
{
@ -104,9 +104,9 @@ struct VARC
get_path_at (const hb_varc_context_t &c,
hb_codepoint_t gid,
hb_array_t<const int> coords,
hb_transform_t transform = HB_TRANSFORM_IDENTITY,
hb_transform_t<> transform = HB_TRANSFORM_IDENTITY,
hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID,
VarRegionList::cache_t *parent_cache = nullptr) const;
hb_scalar_cache_t *parent_cache = nullptr) const;
bool
get_path (hb_font_t *font,
@ -129,7 +129,7 @@ struct VARC
bool
get_extents (hb_font_t *font,
hb_codepoint_t gid,
hb_extents_t *extents,
hb_extents_t<> *extents,
hb_varc_scratch_t &scratch) const
{
hb_varc_context_t c {font,
@ -196,7 +196,7 @@ struct VARC
{
if (!table->has_data ()) return false;
hb_extents_t f_extents;
hb_extents_t<> f_extents;
auto *scratch = acquire_scratch ();
if (unlikely (!scratch)) return true;

View file

@ -102,17 +102,15 @@ struct Glyph
if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
hb_array_t<contour_point_t> phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
{
// Duplicated code.
int lsb = 0;
int h_delta = face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ?
(int) header->xMin - lsb : 0;
face->table.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
int h_delta = (int) header->xMin - lsb;
HB_UNUSED int tsb = 0;
int v_orig = (int) header->yMax +
#ifndef HB_NO_VERTICAL
((void) face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb)
#else
0
face->table.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb);
#endif
;
int v_orig = (int) header->yMax + tsb;
unsigned h_adv = face->table.hmtx->get_advance_without_var_unscaled (gid);
unsigned v_adv =
#ifndef HB_NO_VERTICAL
@ -314,6 +312,7 @@ struct Glyph
bool use_my_metrics = true,
bool phantom_only = false,
hb_array_t<const int> coords = hb_array_t<const int> (),
hb_scalar_cache_t *gvar_cache = nullptr,
unsigned int depth = 0,
unsigned *edge_count = nullptr) const
{
@ -328,7 +327,7 @@ struct Glyph
head_maxp_info->maxComponentDepth = hb_max (head_maxp_info->maxComponentDepth, depth);
}
if (!coords)
if (!coords && font->has_nonzero_coords)
coords = hb_array (font->coords, font->num_coords);
contour_point_vector_t &points = type == SIMPLE ? all_points : scratch.comp_points;
@ -357,25 +356,23 @@ struct Glyph
if (unlikely (!points.resize (points.length + PHANTOM_COUNT))) return false;
hb_array_t<contour_point_t> phantoms = points.as_array ().sub_array (points.length - PHANTOM_COUNT, PHANTOM_COUNT);
{
// Duplicated code.
int lsb = 0;
int h_delta = glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb) ?
(int) header->xMin - lsb : 0;
glyf_accelerator.hmtx->get_leading_bearing_without_var_unscaled (gid, &lsb);
int h_delta = (int) header->xMin - lsb;
HB_UNUSED int tsb = 0;
int v_orig = (int) header->yMax +
#ifndef HB_NO_VERTICAL
((void) glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb), tsb)
#else
0
glyf_accelerator.vmtx->get_leading_bearing_without_var_unscaled (gid, &tsb);
#endif
;
int v_orig = (int) header->yMax + tsb;
unsigned h_adv = glyf_accelerator.hmtx->get_advance_without_var_unscaled (gid);
unsigned v_adv =
#ifndef HB_NO_VERTICAL
glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid)
glyf_accelerator.vmtx->get_advance_without_var_unscaled (gid)
#else
- font->face->get_upem ()
- font->face->get_upem ()
#endif
;
;
phantoms[PHANTOM_LEFT].x = h_delta;
phantoms[PHANTOM_RIGHT].x = (int) h_adv + h_delta;
phantoms[PHANTOM_TOP].y = v_orig;
@ -383,7 +380,7 @@ struct Glyph
}
#ifndef HB_NO_VAR
if (coords)
if (hb_any (coords))
{
#ifndef HB_NO_BEYOND_64K
if (glyf_accelerator.GVAR->has_data ())
@ -391,6 +388,7 @@ struct Glyph
coords,
points.as_array ().sub_array (old_length),
scratch,
gvar_cache,
phantom_only && type == SIMPLE);
else
#endif
@ -398,6 +396,7 @@ struct Glyph
coords,
points.as_array ().sub_array (old_length),
scratch,
gvar_cache,
phantom_only && type == SIMPLE);
}
#endif
@ -447,6 +446,7 @@ struct Glyph
use_my_metrics,
phantom_only,
coords,
gvar_cache,
depth + 1,
edge_count)))
{

View file

@ -220,7 +220,8 @@ struct glyf_accelerator_t
template<typename T>
bool get_points (hb_font_t *font, hb_codepoint_t gid, T consumer,
hb_array_t<const int> coords,
hb_glyf_scratch_t &scratch) const
hb_glyf_scratch_t &scratch,
hb_scalar_cache_t *gvar_cache = nullptr) const
{
if (gid >= num_glyphs) return false;
@ -228,7 +229,7 @@ struct glyf_accelerator_t
all_points.resize (0);
bool phantom_only = !consumer.is_consuming_contour_points ();
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords)))
if (unlikely (!glyph_for_gid (gid).get_points (font, *this, all_points, scratch, nullptr, nullptr, nullptr, true, true, phantom_only, coords, gvar_cache)))
return false;
unsigned count = all_points.length;
@ -371,28 +372,28 @@ struct glyf_accelerator_t
contour_point_t *get_phantoms_sink () { return phantoms; }
};
#ifndef HB_NO_VAR
unsigned
get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical) const
get_advance_with_var_unscaled (hb_codepoint_t gid,
hb_font_t *font,
bool is_vertical,
hb_glyf_scratch_t &scratch,
hb_scalar_cache_t *gvar_cache = nullptr) const
{
if (unlikely (gid >= num_glyphs)) return 0;
bool success = false;
contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
if (font->num_coords)
{
hb_glyf_scratch_t scratch;
success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
hb_array (font->coords, font->num_coords),
scratch);
}
success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
hb_array (font->coords,
font->has_nonzero_coords ? font->num_coords : 0),
scratch, gvar_cache);
if (unlikely (!success))
return
#ifndef HB_NO_VERTICAL
is_vertical ? vmtx->get_advance_without_var_unscaled (gid) :
#endif
hmtx->get_advance_without_var_unscaled (gid);
{
unsigned upem = font->face->get_upem ();
return is_vertical ? upem : upem / 2;
}
float result = is_vertical
? phantoms[glyf_impl::PHANTOM_TOP].y - phantoms[glyf_impl::PHANTOM_BOTTOM].y
@ -400,40 +401,38 @@ struct glyf_accelerator_t
return hb_clamp (roundf (result), 0.f, (float) UINT_MAX / 2);
}
bool get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t gid, bool is_vertical, int *lsb) const
float
get_v_origin_with_var_unscaled (hb_codepoint_t gid,
hb_font_t *font,
hb_glyf_scratch_t &scratch,
hb_scalar_cache_t *gvar_cache = nullptr) const
{
if (unlikely (gid >= num_glyphs)) return false;
if (unlikely (gid >= num_glyphs)) return 0;
bool success = false;
hb_glyph_extents_t extents;
hb_glyf_scratch_t scratch;
contour_point_t phantoms[glyf_impl::PHANTOM_COUNT];
if (unlikely (!get_points (font, gid, points_aggregator_t (font, &extents, phantoms, false),
hb_array (font->coords, font->num_coords),
scratch)))
return false;
success = get_points (font, gid, points_aggregator_t (font, nullptr, phantoms, false),
hb_array (font->coords,
font->has_nonzero_coords ? font->num_coords : 0),
scratch, gvar_cache);
if (unlikely (!success))
{
return font->face->get_upem ();
}
*lsb = is_vertical
? roundf (phantoms[glyf_impl::PHANTOM_TOP].y) - extents.y_bearing
: roundf (phantoms[glyf_impl::PHANTOM_LEFT].x);
return true;
return phantoms[glyf_impl::PHANTOM_TOP].y;
}
#endif
bool get_leading_bearing_without_var_unscaled (hb_codepoint_t gid, bool is_vertical, int *lsb) const
{
if (unlikely (gid >= num_glyphs)) return false;
if (is_vertical) return false; // TODO Humm, what to do here?
*lsb = glyph_for_gid (gid).get_header ()->xMin;
return true;
}
#endif
public:
bool get_extents (hb_font_t *font,
hb_codepoint_t gid,
hb_glyph_extents_t *extents) const
{ return get_extents_at (font, gid, extents, hb_array (font->coords, font->num_coords)); }
{ return get_extents_at (font, gid, extents, hb_array (font->coords,
font->has_nonzero_coords ? font->num_coords : 0)); }
bool get_extents_at (hb_font_t *font,
hb_codepoint_t gid,
@ -445,12 +444,16 @@ struct glyf_accelerator_t
#ifndef HB_NO_VAR
if (coords)
{
hb_glyf_scratch_t scratch;
return get_points (font,
gid,
points_aggregator_t (font, extents, nullptr, true),
coords,
scratch);
hb_glyf_scratch_t *scratch = acquire_scratch ();
if (unlikely (!scratch))
return false;
bool ret = get_points (font,
gid,
points_aggregator_t (font, extents, nullptr, true),
coords,
*scratch);
release_scratch (scratch);
return ret;
}
#endif
return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
@ -485,33 +488,21 @@ struct glyf_accelerator_t
}
bool
get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session, hb_scalar_cache_t *gvar_cache = nullptr) const
{
if (!has_data ()) return false;
hb_glyf_scratch_t *scratch;
// Borrow the cached strach buffer.
{
scratch = cached_scratch.get_acquire ();
if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
{
scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
if (unlikely (!scratch))
return true;
}
}
hb_glyf_scratch_t *scratch = acquire_scratch ();
if (unlikely (!scratch))
return true;
bool ret = get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
hb_array (font->coords, font->num_coords),
*scratch);
hb_array (font->coords,
font->has_nonzero_coords ? font->num_coords : 0),
*scratch,
gvar_cache);
// Put it back.
if (!cached_scratch.cmpexch (nullptr, scratch))
{
scratch->~hb_glyf_scratch_t ();
hb_free (scratch);
}
release_scratch (scratch);
return ret;
}
@ -519,12 +510,35 @@ struct glyf_accelerator_t
bool
get_path_at (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session,
hb_array_t<const int> coords,
hb_glyf_scratch_t &scratch) const
hb_glyf_scratch_t &scratch,
hb_scalar_cache_t *gvar_cache = nullptr) const
{
if (!has_data ()) return false;
return get_points (font, gid, glyf_impl::path_builder_t (font, draw_session),
coords,
scratch);
scratch,
gvar_cache);
}
hb_glyf_scratch_t *acquire_scratch () const
{
hb_glyf_scratch_t *scratch = cached_scratch.get_acquire ();
if (!scratch || unlikely (!cached_scratch.cmpexch (scratch, nullptr)))
{
scratch = (hb_glyf_scratch_t *) hb_calloc (1, sizeof (hb_glyf_scratch_t));
if (unlikely (!scratch))
return nullptr;
}
return scratch;
}
void release_scratch (hb_glyf_scratch_t *scratch) const
{
if (!cached_scratch.cmpexch (nullptr, scratch))
{
scratch->~hb_glyf_scratch_t ();
hb_free (scratch);
}
}
#ifndef HB_NO_VAR

View file

@ -74,7 +74,7 @@ struct ClassDef : public OT::ClassDef
class_def_link->width = SmallTypes::size;
class_def_link->objidx = class_def_prime_id;
class_def_link->position = link_position;
class_def_prime_vertex.add_parent (parent_id);
class_def_prime_vertex.add_parent (parent_id, false);
return true;
}

View file

@ -98,11 +98,33 @@ struct Coverage : public OT::Layout::Common::Coverage
coverage_link->width = SmallTypes::size;
coverage_link->objidx = coverage_prime_id;
coverage_link->position = link_position;
coverage_prime_vertex.add_parent (parent_id);
coverage_prime_vertex.add_parent (parent_id, false);
return (Coverage*) coverage_prime_vertex.obj.head;
}
// Filter an existing coverage table to glyphs at indices [start, end) and replace it with the filtered version.
static bool filter_coverage (gsubgpos_graph_context_t& c,
unsigned existing_coverage,
unsigned start, unsigned end) {
unsigned coverage_size = c.graph.vertices_[existing_coverage].table_size ();
auto& coverage_v = c.graph.vertices_[existing_coverage];
Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
if (!coverage_table || !coverage_table->sanitize (coverage_v))
return false;
auto new_coverage =
+ hb_zip (coverage_table->iter (), hb_range ())
| hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) {
return p.second >= start && p.second < end;
})
| hb_map_retains_sorting (hb_first)
;
return make_coverage (c, new_coverage, existing_coverage, coverage_size * 2 + 100);
}
// Replace the coverage table at dest obj with one covering 'glyphs'.
template<typename It>
static bool make_coverage (gsubgpos_graph_context_t& c,
It glyphs,

View file

@ -50,6 +50,7 @@ struct graph_t
private:
unsigned incoming_edges_ = 0;
unsigned single_parent = (unsigned) -1;
bool has_incoming_virtual_edges_ = false;
hb_hashmap_t<unsigned, unsigned> parents;
public:
@ -66,6 +67,11 @@ struct graph_t
return parents.in_error ();
}
bool has_incoming_virtual_edges () const
{
return has_incoming_virtual_edges_;
}
bool link_positions_valid (unsigned num_objects, bool removed_nil)
{
hb_set_t assigned_bytes;
@ -121,7 +127,9 @@ struct graph_t
}
}
bool equals (const vertex_t& other,
bool equals (unsigned this_index,
unsigned other_index,
const vertex_t& other,
const graph_t& graph,
const graph_t& other_graph,
unsigned depth) const
@ -129,8 +137,10 @@ struct graph_t
if (!(as_bytes () == other.as_bytes ()))
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
"vertex [%lu] bytes != [%lu] bytes, depth = %u",
"vertex %u [%lu bytes] != %u [%lu bytes], depth = %u",
this_index,
(unsigned long) table_size (),
other_index,
(unsigned long) other.table_size (),
depth);
@ -162,6 +172,7 @@ struct graph_t
hb_swap (a.single_parent, b.single_parent);
hb_swap (a.parents, b.parents);
hb_swap (a.incoming_edges_, b.incoming_edges_);
hb_swap (a.has_incoming_virtual_edges_, b.has_incoming_virtual_edges_);
hb_swap (a.start, b.start);
hb_swap (a.end, b.end);
hb_swap (a.priority, b.priority);
@ -207,13 +218,16 @@ struct graph_t
void reset_parents ()
{
incoming_edges_ = 0;
has_incoming_virtual_edges_ = false;
single_parent = (unsigned) -1;
parents.reset ();
}
void add_parent (unsigned parent_index)
void add_parent (unsigned parent_index, bool is_virtual)
{
assert (parent_index != (unsigned) -1);
has_incoming_virtual_edges_ |= is_virtual;
if (incoming_edges_ == 0)
{
single_parent = parent_index;
@ -408,7 +422,7 @@ struct graph_t
link_a.bias != link_b.bias)
return false;
if (!graph.vertices_[link_a.objidx].equals (
if (!graph.vertices_[link_a.objidx].equals (link_a.objidx, link_b.objidx,
other_graph.vertices_[link_b.objidx], graph, other_graph, depth + 1))
return false;
@ -490,7 +504,7 @@ struct graph_t
bool operator== (const graph_t& other) const
{
return root ().equals (other.root (), *this, other, 0);
return root ().equals (root_idx(), other.root_idx(), other.root (), *this, other, 0);
}
void print () const {
@ -501,6 +515,9 @@ struct graph_t
for (const auto &l : v.obj.real_links) {
printf("%u, ", l.objidx);
}
for (const auto &l : v.obj.virtual_links) {
printf("v%u, ", l.objidx);
}
printf("]\n");
}
}
@ -556,7 +573,7 @@ struct graph_t
link->width = 2;
link->objidx = child_id;
link->position = (char*) offset - (char*) v.obj.head;
vertices_[child_id].add_parent (parent_id);
vertices_[child_id].add_parent (parent_id, false);
}
/*
@ -943,9 +960,11 @@ struct graph_t
/*
* Moves the child of old_parent_idx pointed to by old_offset to a new
* vertex at the new_offset.
*
* Returns the id of the child node that was moved.
*/
template<typename O>
void move_child (unsigned old_parent_idx,
unsigned move_child (unsigned old_parent_idx,
const O* old_offset,
unsigned new_parent_idx,
const O* new_offset)
@ -965,10 +984,12 @@ struct graph_t
new_link->position = (const char*) new_offset - (const char*) new_v.obj.head;
auto& child = vertices_[child_id];
child.add_parent (new_parent_idx);
child.add_parent (new_parent_idx, false);
old_v.remove_real_link (child_id, old_offset);
child.remove_parent (old_parent_idx);
return child_id;
}
/*
@ -1015,12 +1036,12 @@ struct graph_t
for (const auto& l : child.obj.real_links)
{
clone->obj.real_links.push (l);
vertices_[l.objidx].add_parent (clone_idx);
vertices_[l.objidx].add_parent (clone_idx, false);
}
for (const auto& l : child.obj.virtual_links)
{
clone->obj.virtual_links.push (l);
vertices_[l.objidx].add_parent (clone_idx);
vertices_[l.objidx].add_parent (clone_idx, true);
}
check_success (!clone->obj.real_links.in_error ());
@ -1073,10 +1094,15 @@ struct graph_t
const auto& child = vertices_[child_idx];
unsigned links_to_child = child.incoming_edges_from_parent(parent_idx);
if (child.incoming_edges () <= links_to_child)
if (child.incoming_edges () <= links_to_child || child.has_incoming_virtual_edges())
{
// Can't duplicate this node, doing so would orphan the original one as all remaining links
// to child are from parent.
//
// We don't allow duplication of nodes with incoming virtual edges because we don't track
// the number of virtual vs real incoming edges. As a result we can't tell if a node
// with virtual edges may end up orphaned by duplication (ie. where one copy is only pointed
// to by virtual edges).
DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u => %u",
parent_idx, child_idx);
return -1;
@ -1091,12 +1117,15 @@ struct graph_t
if (parent_idx == clone_idx) parent_idx++;
auto& parent = vertices_[parent_idx];
unsigned count = 0;
unsigned num_real = parent.obj.real_links.length;
for (auto& l : parent.obj.all_links_writer ())
{
count++;
if (l.objidx != child_idx)
continue;
reassign_link (l, parent_idx, clone_idx);
reassign_link (l, parent_idx, clone_idx, count > num_real);
}
return clone_idx;
@ -1129,10 +1158,15 @@ struct graph_t
links_to_child += child.incoming_edges_from_parent(parent_idx);
}
if (child.incoming_edges () <= links_to_child)
if (child.incoming_edges () <= links_to_child || child.has_incoming_virtual_edges())
{
// Can't duplicate this node, doing so would orphan the original one as all remaining links
// to child are from parent.
//
// We don't allow duplication of nodes with incoming virtual edges because we don't track
// the number of virtual vs real incoming edges. As a result we can't tell if a node
// with virtual edges may end up orphaned by duplication (ie. where one copy is only pointed
// to by virtual edges).
DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx);
return -1;
}
@ -1146,12 +1180,15 @@ struct graph_t
// duplicate shifts the root node idx, so if parent_idx was root update it.
if (parent_idx == clone_idx) parent_idx++;
auto& parent = vertices_[parent_idx];
unsigned count = 0;
unsigned num_real = parent.obj.real_links.length;
for (auto& l : parent.obj.all_links_writer ())
{
count++;
if (l.objidx != child_idx)
continue;
reassign_link (l, parent_idx, clone_idx);
reassign_link (l, parent_idx, clone_idx, count > num_real);
}
}
@ -1279,6 +1316,7 @@ struct graph_t
if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected.");
parents_invalid = true;
update_parents();
@ -1398,8 +1436,11 @@ struct graph_t
for (unsigned p = 0; p < count; p++)
{
for (auto& l : vertices_.arrayZ[p].obj.all_links ())
vertices_[l.objidx].add_parent (p);
for (auto& l : vertices_.arrayZ[p].obj.real_links)
vertices_[l.objidx].add_parent (p, false);
for (auto& l : vertices_.arrayZ[p].obj.virtual_links)
vertices_[l.objidx].add_parent (p, true);
}
for (unsigned i = 0; i < count; i++)
@ -1502,12 +1543,13 @@ struct graph_t
*/
void reassign_link (hb_serialize_context_t::object_t::link_t& link,
unsigned parent_idx,
unsigned new_idx)
unsigned new_idx,
bool is_virtual)
{
unsigned old_idx = link.objidx;
link.objidx = new_idx;
vertices_[old_idx].remove_parent (parent_idx);
vertices_[new_idx].add_parent (parent_idx);
vertices_[new_idx].add_parent (parent_idx, is_virtual);
}
/*
@ -1521,13 +1563,16 @@ struct graph_t
if (!id_map) return;
for (unsigned i : subgraph)
{
unsigned num_real = vertices_[i].obj.real_links.length;
unsigned count = 0;
for (auto& link : vertices_[i].obj.all_links_writer ())
{
count++;
const uint32_t *v;
if (!id_map.has (link.objidx, &v)) continue;
if (only_wide && !(link.width == 4 && !link.is_signed)) continue;
reassign_link (link, i, *v);
reassign_link (link, i, *v, count > num_real);
}
}
}

View file

@ -27,9 +27,11 @@
#include "graph.hh"
#include "../hb-ot-layout-gsubgpos.hh"
#include "../OT/Layout/GSUB/ExtensionSubst.hh"
#include "../OT/Layout/GSUB/SubstLookupSubTable.hh"
#include "gsubgpos-context.hh"
#include "pairpos-graph.hh"
#include "markbasepos-graph.hh"
#include "ligature-graph.hh"
#ifndef GRAPH_GSUBGPOS_GRAPH_HH
#define GRAPH_GSUBGPOS_GRAPH_HH
@ -120,12 +122,10 @@ struct Lookup : public OT::Lookup
unsigned type = lookupType;
bool is_ext = is_extension (c.table_tag);
if (c.table_tag != HB_OT_TAG_GPOS)
if (c.table_tag != HB_OT_TAG_GPOS && c.table_tag != HB_OT_TAG_GSUB)
return true;
if (!is_ext &&
type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair &&
type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase)
if (!is_ext && !is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c))
return true;
hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>> all_new_subtables;
@ -144,21 +144,32 @@ struct Lookup : public OT::Lookup
subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index);
type = extension->get_lookup_type ();
if (type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair
&& type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase)
if (!is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c))
continue;
}
hb_vector_t<unsigned> new_sub_tables;
switch (type)
{
case 2:
new_sub_tables = split_subtable<PairPos> (c, parent_index, subtable_index); break;
case 4:
new_sub_tables = split_subtable<MarkBasePos> (c, parent_index, subtable_index); break;
default:
break;
if (c.table_tag == HB_OT_TAG_GPOS) {
switch (type)
{
case 2:
new_sub_tables = split_subtable<PairPos> (c, parent_index, subtable_index); break;
case 4:
new_sub_tables = split_subtable<MarkBasePos> (c, parent_index, subtable_index); break;
default:
break;
}
} else if (c.table_tag == HB_OT_TAG_GSUB) {
switch (type)
{
case 4:
new_sub_tables = split_subtable<graph::LigatureSubst> (c, parent_index, subtable_index); break;
default:
break;
}
}
if (new_sub_tables.in_error ()) return false;
if (!new_sub_tables) continue;
hb_pair_t<unsigned, hb_vector_t<unsigned>>* entry = all_new_subtables.push ();
@ -191,14 +202,14 @@ struct Lookup : public OT::Lookup
hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids)
{
bool is_ext = is_extension (c.table_tag);
auto& v = c.graph.vertices_[this_index];
auto* v = &c.graph.vertices_[this_index];
fix_existing_subtable_links (c, this_index, subtable_ids);
unsigned new_subtable_count = 0;
for (const auto& p : subtable_ids)
new_subtable_count += p.second.length;
size_t new_size = v.table_size ()
size_t new_size = v->table_size ()
+ new_subtable_count * OT::Offset16::static_size;
char* buffer = (char*) hb_calloc (1, new_size);
if (!buffer) return false;
@ -207,10 +218,10 @@ struct Lookup : public OT::Lookup
hb_free (buffer);
return false;
}
hb_memcpy (buffer, v.obj.head, v.table_size());
hb_memcpy (buffer, v->obj.head, v->table_size());
v.obj.head = buffer;
v.obj.tail = buffer + new_size;
v->obj.head = buffer;
v->obj.tail = buffer + new_size;
Lookup* new_lookup = (Lookup*) buffer;
@ -226,21 +237,23 @@ struct Lookup : public OT::Lookup
if (is_ext)
{
unsigned ext_id = create_extension_subtable (c, subtable_id, type);
c.graph.vertices_[subtable_id].add_parent (ext_id);
c.graph.vertices_[subtable_id].add_parent (ext_id, false);
subtable_id = ext_id;
// the reference to v may have changed on adding a node, so reassign it.
v = &c.graph.vertices_[this_index];
}
auto* link = v.obj.real_links.push ();
auto* link = v->obj.real_links.push ();
link->width = 2;
link->objidx = subtable_id;
link->position = (char*) &new_lookup->subTable[offset_index++] -
(char*) new_lookup;
c.graph.vertices_[subtable_id].add_parent (this_index);
c.graph.vertices_[subtable_id].add_parent (this_index, false);
}
}
// Repacker sort order depends on link order, which we've messed up so resort it.
v.obj.real_links.qsort ();
v->obj.real_links.qsort ();
// The head location of the lookup has changed, invalidating the lookups map entry
// in the context. Update the map.
@ -326,7 +339,7 @@ struct Lookup : public OT::Lookup
// Make extension point at the subtable.
auto& ext_vertex = c.graph.vertices_[ext_index];
ext_vertex.add_parent (lookup_index);
ext_vertex.add_parent (lookup_index, false);
if (!existing_ext_index)
subtable_vertex.remap_parent (lookup_index, ext_index);
@ -334,6 +347,19 @@ struct Lookup : public OT::Lookup
}
private:
bool is_supported_gsub_type(unsigned type, gsubgpos_graph_context_t& c) const {
return (c.table_tag == HB_OT_TAG_GSUB) && (
type == OT::Layout::GSUB_impl::SubstLookupSubTable::Type::Ligature
);
}
bool is_supported_gpos_type(unsigned type, gsubgpos_graph_context_t& c) const {
return (c.table_tag == HB_OT_TAG_GPOS) && (
type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair ||
type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase
);
}
unsigned extension_type (hb_tag_t table_tag) const
{
switch (table_tag)

View file

@ -0,0 +1,480 @@
/*
* Copyright © 2025 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Garret Rieger
*/
#ifndef GRAPH_LIGATURE_GRAPH_HH
#define GRAPH_LIGATURE_GRAPH_HH
#include "graph.hh"
#include "../OT/Layout/GSUB/LigatureSubst.hh"
#include "../OT/Layout/GSUB/LigatureSubstFormat1.hh"
#include "../OT/Layout/GSUB/LigatureSet.hh"
#include "../OT/Layout/types.hh"
#include <algorithm>
#include <utility>
namespace graph {
struct LigatureSet : public OT::Layout::GSUB_impl::LigatureSet<SmallTypes>
{
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size) return false;
hb_barrier ();
int64_t total_len = ligature.get_size() + OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size - ligature.len.get_size();
if (vertex_len < total_len) {
return false;
}
return true;
}
};
struct LigatureSubstFormat1 : public OT::Layout::GSUB_impl::LigatureSubstFormat1_2<SmallTypes>
{
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
unsigned min_size = OT::Layout::GSUB_impl::LigatureSubstFormat1_2<SmallTypes>::min_size;
if (vertex_len < min_size) return false;
hb_barrier ();
return vertex_len >=
min_size + ligatureSet.get_size() - ligatureSet.len.get_size();
}
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
auto split_points = compute_split_points(c, parent_index, this_index);
split_context_t split_context {
c,
this,
c.graph.duplicate_if_shared (parent_index, this_index),
total_number_ligas(c, this_index),
liga_counts(c, this_index),
};
return actuate_subtable_split<split_context_t> (split_context, split_points);
}
private:
unsigned total_number_ligas(gsubgpos_graph_context_t& c, unsigned this_index) const {
unsigned total = 0;
for (unsigned i = 0; i < ligatureSet.len; i++)
{
auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]);
if (!liga_set.table) {
return 0;
}
total += liga_set.table->ligature.len;
}
return total;
}
hb_vector_t<unsigned> liga_counts(gsubgpos_graph_context_t& c, unsigned this_index) const {
hb_vector_t<unsigned> result;
for (unsigned i = 0; i < ligatureSet.len; i++)
{
auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]);
result.push(!liga_set.table ? 0 : liga_set.table->ligature.len);
}
return result;
}
hb_vector_t<unsigned> compute_split_points(gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index) const
{
// For ligature subst coverage is always packed last, and as a result is where an overflow
// will happen if there is one, so we can check the estimate length of the
// LigatureSubstFormat1 -> Coverage offset length which is the sum of all data in the
// retained sub graph except for the coverage table itself.
const unsigned base_size = OT::Layout::GSUB_impl::LigatureSubstFormat1_2<SmallTypes>::min_size;
unsigned accumulated = base_size;
unsigned ligature_index = 0;
hb_vector_t<unsigned> split_points;
for (unsigned i = 0; i < ligatureSet.len; i++)
{
accumulated += OT::HBUINT16::static_size; // for ligature set offset
accumulated += OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size; // for ligature set table
auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]);
if (!liga_set.table) {
return hb_vector_t<unsigned> {};
}
for (unsigned j = 0; j < liga_set.table->ligature.len; j++)
{
const unsigned liga_id = c.graph.index_for_offset (liga_set.index, &liga_set.table->ligature[j]);
const unsigned liga_size = c.graph.vertices_[liga_id].table_size ();
accumulated += OT::HBUINT16::static_size; // for ligature offset
accumulated += liga_size; // for the ligature table
if (accumulated >= (1 << 16))
{
split_points.push(ligature_index);
// We're going to split such that the current ligature will be in the new sub table.
// That means we'll have one ligature subst (base_base), one ligature set, and one liga table
accumulated = base_size + // for liga subst subtable
(OT::HBUINT16::static_size * 2) + // for liga set and liga offset
OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size + // for liga set subtable
liga_size; // for liga sub table
}
ligature_index++;
}
}
return split_points;
}
struct split_context_t
{
gsubgpos_graph_context_t& c;
LigatureSubstFormat1* thiz;
unsigned this_index;
unsigned original_count_;
hb_vector_t<unsigned> liga_counts;
unsigned original_count ()
{
return original_count_;
}
unsigned clone_range (unsigned start, unsigned end)
{
return thiz->clone_range (c, this_index, liga_counts, start, end);
}
bool shrink (unsigned count)
{
return thiz->shrink (c, this_index, original_count(), liga_counts, count);
}
};
hb_pair_t<unsigned, LigatureSet*> new_liga_set(gsubgpos_graph_context_t& c, unsigned count) const {
unsigned prime_size = OT::Layout::GSUB_impl::LigatureSet<SmallTypes>::min_size
+ count * SmallTypes::size;
unsigned prime_id = c.create_node (prime_size);
if (prime_id == (unsigned) -1) return hb_pair(-1, nullptr);
LigatureSet* prime = (LigatureSet*) c.graph.object (prime_id).head;
prime->ligature.len = count;
return hb_pair(prime_id, prime);
}
void clear_virtual_links (gsubgpos_graph_context_t& c, unsigned node_index) const
{
auto& obj = c.graph.vertices_[node_index].obj;
for (const auto& l : obj.virtual_links)
{
auto& child = c.graph.vertices_[l.objidx];
child.remove_parent(node_index);
}
obj.virtual_links.clear();
}
void add_virtual_link(gsubgpos_graph_context_t& c, unsigned from, unsigned to) const {
auto& from_obj = c.graph.vertices_[from].obj;
c.graph.vertices_[to].add_parent(from, true);
auto& link = *from_obj.virtual_links.push ();
link.objidx = to;
}
hb_pair_t<unsigned, unsigned> current_liga_set_bounds (gsubgpos_graph_context_t& c,
unsigned liga_set_index,
const hb_serialize_context_t::object_t& liga_set) const
{
// Finds the actual liga indices present in the liga set currently. Takes
// into account those that have been removed by processing.
unsigned min_index = (unsigned) -1;
unsigned max_index = 0;
for (const auto& l : liga_set.real_links) {
if (l.position < 2) continue;
unsigned liga_index = (l.position - 2) / 2;
min_index = hb_min(min_index, liga_index);
max_index = hb_max(max_index, liga_index);
}
return hb_pair(min_index, max_index + 1);
}
void compact_liga_set (gsubgpos_graph_context_t& c, LigatureSet* table, hb_serialize_context_t::object_t& obj) const
{
if (table->ligature.len <= obj.real_links.length) return;
// compact the remaining linked liga offsets into a continous array and shrink the node as needed.
unsigned to_remove = table->ligature.len - obj.real_links.length;
unsigned new_position = SmallTypes::size;
obj.real_links.qsort(); // for this to work we need to process links in order of position.
for (auto& l : obj.real_links)
{
l.position = new_position;
new_position += SmallTypes::size;
}
table->ligature.len = obj.real_links.length;
obj.tail -= to_remove * SmallTypes::size;
}
unsigned clone_range (gsubgpos_graph_context_t& c,
unsigned this_index,
hb_vector_t<unsigned> liga_counts,
unsigned start, unsigned end) const
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
" Cloning LigatureSubstFormat1 (%u) range [%u, %u).", this_index, start, end);
// Create an oversized new liga subst, we'll adjust the size down later. We don't know
// the final size until we process it but we also need it to exist while we're processing
// so that nodes can be moved to it as needed.
unsigned prime_size = OT::Layout::GSUB_impl::LigatureSubstFormat1_2<SmallTypes>::min_size
+ ligatureSet.get_size() - ligatureSet.len.get_size();
unsigned liga_subst_prime_id = c.create_node (prime_size);
if (liga_subst_prime_id == (unsigned) -1) return -1;
LigatureSubstFormat1* liga_subst_prime = (LigatureSubstFormat1*) c.graph.object (liga_subst_prime_id).head;
liga_subst_prime->format = this->format;
liga_subst_prime->ligatureSet.len = this->ligatureSet.len;
// Create a place holder coverage prime id since we need to add virtual links to it while
// generating liga and liga sets. Afterwards it will be updated to have the correct coverage.
unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage);
unsigned coverage_prime_id = c.graph.duplicate(coverage_id);
auto& coverage_prime_vertex = c.graph.vertices_[coverage_prime_id];
auto* coverage_prime_link = c.graph.vertices_[liga_subst_prime_id].obj.real_links.push ();
coverage_prime_link->width = SmallTypes::size;
coverage_prime_link->objidx = coverage_prime_id;
coverage_prime_link->position = 2;
coverage_prime_vertex.add_parent (liga_subst_prime_id, false);
// Locate all liga sets with ligas between start and end.
// Clone or move them as needed.
unsigned count = 0;
unsigned liga_set_count = 0;
unsigned liga_set_start = -1;
unsigned liga_set_end = 0; // inclusive
for (unsigned i = 0; i < liga_counts.length; i++)
{
unsigned num_ligas = liga_counts[i];
unsigned current_start = count;
unsigned current_end = count + num_ligas;
if (current_start >= end || start >= current_end) {
// No intersection, so just skip
count += num_ligas;
continue;
}
auto liga_set_index = c.graph.index_for_offset(this_index, &ligatureSet[i]);
auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]);
if (!liga_set.table) {
return -1;
}
// Bounds may need to be adjusted if some ligas have been previously removed.
hb_pair_t<unsigned, unsigned> liga_bounds = current_liga_set_bounds(c, liga_set_index, liga_set.vertex->obj);
current_start = hb_max(count + liga_bounds.first, current_start);
current_end = hb_min(count + liga_bounds.second, current_end);
unsigned liga_set_prime_id;
if (current_start >= start && current_end <= end) {
// This liga set is fully contined within [start, end)
// We can move the entire ligaset to the new liga subset object.
liga_set_end = i;
if (i < liga_set_start) liga_set_start = i;
liga_set_prime_id = c.graph.move_child<> (this_index,
&ligatureSet[i],
liga_subst_prime_id,
&liga_subst_prime->ligatureSet[liga_set_count++]);
compact_liga_set(c, liga_set.table, liga_set.vertex->obj);
}
else
{
// This liga set partially overlaps [start, end). We'll need to create
// a new liga set sub table and move the intersecting ligas to it.
unsigned liga_count = hb_min(end, current_end) - hb_max(start, current_start);
auto result = new_liga_set(c, liga_count);
liga_set_prime_id = result.first;
LigatureSet* prime = result.second;
if (liga_set_prime_id == (unsigned) -1) return -1;
unsigned new_index = 0;
for (unsigned j = hb_max(start, current_start) - count; j < hb_min(end, current_end) - count; j++) {
c.graph.move_child<> (liga_set_index,
&liga_set.table->ligature[j],
liga_set_prime_id,
&prime->ligature[new_index++]);
}
liga_set_end = i;
if (i < liga_set_start) liga_set_start = i;
c.graph.add_link(&liga_subst_prime->ligatureSet[liga_set_count++], liga_subst_prime_id, liga_set_prime_id);
}
// The new liga and all children set needs to have a virtual link to the new coverage table:
auto& liga_set_prime = c.graph.vertices_[liga_set_prime_id].obj;
clear_virtual_links(c, liga_set_prime_id);
add_virtual_link(c, liga_set_prime_id, coverage_prime_id);
for (const auto& l : liga_set_prime.real_links) {
clear_virtual_links(c, l.objidx);
add_virtual_link(c, l.objidx, coverage_prime_id);
}
count += num_ligas;
}
c.graph.vertices_[liga_subst_prime_id].obj.tail -= (liga_subst_prime->ligatureSet.len - liga_set_count) * SmallTypes::size;
liga_subst_prime->ligatureSet.len = liga_set_count;
if (!Coverage::filter_coverage (c,
coverage_prime_id,
liga_set_start, liga_set_end + 1))
return -1;
return liga_subst_prime_id;
}
bool shrink (gsubgpos_graph_context_t& c,
unsigned this_index,
unsigned old_count,
hb_vector_t<unsigned> liga_counts,
unsigned count)
{
DEBUG_MSG (SUBSET_REPACK, nullptr,
" Shrinking LigatureSubstFormat1 (%u) to [0, %u).",
this_index,
count);
if (count >= old_count)
return true;
hb_set_t retained_indices;
unsigned new_liga_set_count = 0;
for (unsigned i = 0; i < liga_counts.length; i++)
{
auto liga_set = c.graph.as_table<LigatureSet>(this_index, &ligatureSet[i]);
if (!liga_set.table) {
return false;
}
// We need the virtual links to coverage removed from all descendants on this liga subst.
// If any are left when we try to mutate the coverage table later it will be unnessecarily
// duplicated. Code later on will re-add the virtual links as needed (via retained_indices).
clear_virtual_links(c, liga_set.index);
retained_indices.add(liga_set.index);
for (const auto& liga_offset : liga_set.table->ligature) {
unsigned liga_index = c.graph.index_for_offset(liga_set.index, &liga_offset);
if (liga_index != (unsigned) -1) {
clear_virtual_links(c, liga_index);
retained_indices.add(liga_index);
}
}
unsigned num_ligas = liga_counts[i];
if (num_ligas >= count) {
// drop the trailing liga's from this set and all subsequent liga sets
unsigned num_ligas_to_remove = num_ligas - count;
new_liga_set_count = i + 1;
c.graph.vertices_[liga_set.index].obj.tail -= num_ligas_to_remove * SmallTypes::size;
liga_set.table->ligature.len = count;
break;
} else {
count -= num_ligas;
}
}
// Adjust liga set array
c.graph.vertices_[this_index].obj.tail -= (ligatureSet.len - new_liga_set_count) * SmallTypes::size;
ligatureSet.len = new_liga_set_count;
// Coverage matches the number of liga sets so rebuild as needed
auto coverage = c.graph.as_mutable_table<Coverage> (this_index, &this->coverage);
if (!coverage) return false;
for (unsigned i : retained_indices.iter())
add_virtual_link(c, i, coverage.index);
unsigned coverage_size = coverage.vertex->table_size ();
auto new_coverage =
+ hb_zip (coverage.table->iter (), hb_range ())
| hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) {
return p.second < new_liga_set_count;
})
| hb_map_retains_sorting (hb_first)
;
return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size);
}
};
struct LigatureSubst : public OT::Layout::GSUB_impl::LigatureSubst
{
hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
unsigned parent_index,
unsigned this_index)
{
switch (u.format) {
case 1:
return ((LigatureSubstFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
#ifndef HB_NO_BEYOND_64K
case 2: HB_FALLTHROUGH;
// Don't split 24bit Ligature Subs
#endif
default:
return hb_vector_t<unsigned> ();
}
}
bool sanitize (graph_t::vertex_t& vertex) const
{
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
if (vertex_len < u.format.get_size ()) return false;
hb_barrier ();
switch (u.format) {
case 1:
return ((LigatureSubstFormat1*)(&u.format1))->sanitize (vertex);
#ifndef HB_NO_BEYOND_64K
case 2: HB_FALLTHROUGH;
#endif
default:
// We don't handle format 2 here.
return false;
}
}
};
}
#endif // GRAPH_LIGATURE_GRAPH_HH

View file

@ -423,7 +423,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
class_def_link->width = SmallTypes::size;
class_def_link->objidx = class_def_2_id;
class_def_link->position = 10;
graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id);
graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id, false);
graph.duplicate (pair_pos_prime_id, class_def_2_id);
return pair_pos_prime_id;

View file

@ -172,8 +172,11 @@ void print_overflows (graph_t& graph,
template <typename O> inline void
serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
char* head,
unsigned size,
hb_serialize_context_t* c)
{
assert(link.position + link.width <= size);
OT::Offset<O>* offset = reinterpret_cast<OT::Offset<O>*> (head + link.position);
*offset = 0;
c->add_link (*offset,
@ -187,6 +190,7 @@ serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
inline
void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
char* head,
unsigned size,
hb_serialize_context_t* c)
{
switch (link.width)
@ -197,21 +201,21 @@ void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
case 4:
if (link.is_signed)
{
serialize_link_of_type<OT::HBINT32> (link, head, c);
serialize_link_of_type<OT::HBINT32> (link, head, size, c);
} else {
serialize_link_of_type<OT::HBUINT32> (link, head, c);
serialize_link_of_type<OT::HBUINT32> (link, head, size, c);
}
return;
case 2:
if (link.is_signed)
{
serialize_link_of_type<OT::HBINT16> (link, head, c);
serialize_link_of_type<OT::HBINT16> (link, head, size, c);
} else {
serialize_link_of_type<OT::HBUINT16> (link, head, c);
serialize_link_of_type<OT::HBUINT16> (link, head, size, c);
}
return;
case 3:
serialize_link_of_type<OT::HBUINT24> (link, head, c);
serialize_link_of_type<OT::HBUINT24> (link, head, size, c);
return;
default:
// Unexpected link width.
@ -251,7 +255,7 @@ inline hb_blob_t* serialize (const graph_t& graph)
// Only real links needs to be serialized.
for (const auto& link : vertices[i].obj.real_links)
serialize_link (link, start, &c);
serialize_link (link, start, size, &c);
// All duplications are already encoded in the graph, so don't
// enable sharing during packing.

View file

@ -78,129 +78,220 @@
/*
* Big-endian integers.
* Fixed-endian integers / floats.
*/
/* Endian swap, used in Windows related backends */
static inline constexpr uint16_t hb_uint16_swap (uint16_t v)
{ return (v >> 8) | (v << 8); }
static inline constexpr uint32_t hb_uint32_swap (uint32_t v)
{ return (hb_uint16_swap (v) << 16) | hb_uint16_swap (v >> 16); }
#ifndef HB_FAST_INT_ACCESS
template <typename Type>
struct __attribute__((packed)) hb_packed_t { Type v; };
#ifndef HB_FAST_NUM_ACCESS
#if defined(__OPTIMIZE__) && \
defined(__BYTE_ORDER) && \
(__BYTE_ORDER == __BIG_ENDIAN || \
(__BYTE_ORDER == __LITTLE_ENDIAN && \
hb_has_builtin(__builtin_bswap16) && \
hb_has_builtin(__builtin_bswap32)))
#define HB_FAST_INT_ACCESS 1
#define HB_FAST_NUM_ACCESS 1
#else
#define HB_FAST_INT_ACCESS 0
#define HB_FAST_NUM_ACCESS 0
#endif
#endif
template <typename Type, int Bytes = sizeof (Type)>
struct BEInt;
template <typename Type>
struct BEInt<Type, 1>
template <bool BE, typename Type, int Bytes = sizeof (Type)>
struct HBInt;
template <bool BE, typename Type>
struct HBInt<BE, Type, 1>
{
public:
BEInt () = default;
constexpr BEInt (Type V) : v {uint8_t (V)} {}
HBInt () = default;
constexpr HBInt (Type V) : v {uint8_t (V)} {}
constexpr operator Type () const { return v; }
private: uint8_t v;
};
template <typename Type>
struct BEInt<Type, 2>
template <bool BE, typename Type>
struct HBInt<BE, Type, 2>
{
struct __attribute__((packed)) packed_uint16_t { uint16_t v; };
public:
BEInt () = default;
HBInt () = default;
BEInt (Type V)
#if HB_FAST_INT_ACCESS
#if __BYTE_ORDER == __LITTLE_ENDIAN
{ ((packed_uint16_t *) v)->v = __builtin_bswap16 (V); }
#else /* __BYTE_ORDER == __BIG_ENDIAN */
{ ((packed_uint16_t *) v)->v = V; }
#endif
HBInt (Type V)
#if HB_FAST_NUM_ACCESS
{
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
((hb_packed_t<uint16_t> *) v)->v = V;
else
((hb_packed_t<uint16_t> *) v)->v = __builtin_bswap16 (V);
}
#else
: v {uint8_t ((V >> 8) & 0xFF),
uint8_t ((V ) & 0xFF)} {}
: v {BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V ) & 0xFF),
BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V >> 8) & 0xFF)} {}
#endif
constexpr operator Type () const {
#if HB_FAST_INT_ACCESS
#if __BYTE_ORDER == __LITTLE_ENDIAN
return __builtin_bswap16 (((packed_uint16_t *) v)->v);
#else /* __BYTE_ORDER == __BIG_ENDIAN */
return ((packed_uint16_t *) v)->v;
#endif
constexpr operator Type () const
{
#if HB_FAST_NUM_ACCESS
return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ?
((const hb_packed_t<uint16_t> *) v)->v
:
__builtin_bswap16 (((const hb_packed_t<uint16_t> *) v)->v)
;
#else
return (v[0] << 8)
+ (v[1] );
return (BE ? (v[0] << 8) : (v[0] ))
+ (BE ? (v[1] ) : (v[1] << 8));
#endif
}
private: uint8_t v[2];
};
template <typename Type>
struct BEInt<Type, 3>
template <bool BE, typename Type>
struct HBInt<BE, Type, 3>
{
static_assert (!std::is_signed<Type>::value, "");
public:
BEInt () = default;
constexpr BEInt (Type V) : v {uint8_t ((V >> 16) & 0xFF),
uint8_t ((V >> 8) & 0xFF),
uint8_t ((V ) & 0xFF)} {}
HBInt () = default;
constexpr HBInt (Type V) : v {BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 16) & 0xFF),
BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V >> 8) & 0xFF),
BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V ) & 0xFF)} {}
constexpr operator Type () const { return (v[0] << 16)
+ (v[1] << 8)
+ (v[2] ); }
constexpr operator Type () const { return (BE ? (v[0] << 16) : (v[0] ))
+ (BE ? (v[1] << 8) : (v[1] << 8))
+ (BE ? (v[2] ) : (v[2] << 16)); }
private: uint8_t v[3];
};
template <typename Type>
struct BEInt<Type, 4>
template <bool BE, typename Type>
struct HBInt<BE, Type, 4>
{
struct __attribute__((packed)) packed_uint32_t { uint32_t v; };
template <bool, typename, int>
friend struct HBFloat;
public:
BEInt () = default;
HBInt () = default;
BEInt (Type V)
#if HB_FAST_INT_ACCESS
#if __BYTE_ORDER == __LITTLE_ENDIAN
{ ((packed_uint32_t *) v)->v = __builtin_bswap32 (V); }
#else /* __BYTE_ORDER == __BIG_ENDIAN */
{ ((packed_uint32_t *) v)->v = V; }
#endif
HBInt (Type V)
#if HB_FAST_NUM_ACCESS
{
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
((hb_packed_t<uint32_t> *) v)->v = V;
else
((hb_packed_t<uint32_t> *) v)->v = __builtin_bswap32 (V);
}
#else
: v {uint8_t ((V >> 24) & 0xFF),
uint8_t ((V >> 16) & 0xFF),
uint8_t ((V >> 8) & 0xFF),
uint8_t ((V ) & 0xFF)} {}
: v {BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V ) & 0xFF),
BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 8) & 0xFF),
BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V >> 16) & 0xFF),
BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V >> 24) & 0xFF)} {}
#endif
constexpr operator Type () const {
#if HB_FAST_INT_ACCESS
#if __BYTE_ORDER == __LITTLE_ENDIAN
return __builtin_bswap32 (((packed_uint32_t *) v)->v);
#else /* __BYTE_ORDER == __BIG_ENDIAN */
return ((packed_uint32_t *) v)->v;
#endif
#if HB_FAST_NUM_ACCESS
return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ?
((const hb_packed_t<uint32_t> *) v)->v
:
__builtin_bswap32 (((const hb_packed_t<uint32_t> *) v)->v)
;
#else
return (v[0] << 24)
+ (v[1] << 16)
+ (v[2] << 8)
+ (v[3] );
return (BE ? (v[0] << 24) : (v[0] ))
+ (BE ? (v[1] << 16) : (v[1] << 8))
+ (BE ? (v[2] << 8) : (v[2] << 16))
+ (BE ? (v[3] ) : (v[3] << 24));
#endif
}
private: uint8_t v[4];
};
template <bool BE, typename Type>
struct HBInt<BE, Type, 8>
{
template <bool, typename, int>
friend struct HBFloat;
public:
HBInt () = default;
HBInt (Type V)
: v {BE ? uint8_t ((V >> 56) & 0xFF) : uint8_t ((V ) & 0xFF),
BE ? uint8_t ((V >> 48) & 0xFF) : uint8_t ((V >> 8) & 0xFF),
BE ? uint8_t ((V >> 40) & 0xFF) : uint8_t ((V >> 16) & 0xFF),
BE ? uint8_t ((V >> 32) & 0xFF) : uint8_t ((V >> 24) & 0xFF),
BE ? uint8_t ((V >> 24) & 0xFF) : uint8_t ((V >> 32) & 0xFF),
BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 40) & 0xFF),
BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V >> 48) & 0xFF),
BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V >> 56) & 0xFF)} {}
constexpr operator Type () const {
return (BE ? (uint64_t (v[0]) << 56) : (uint64_t (v[0]) ))
+ (BE ? (uint64_t (v[1]) << 48) : (uint64_t (v[1]) << 8))
+ (BE ? (uint64_t (v[2]) << 40) : (uint64_t (v[2]) << 16))
+ (BE ? (uint64_t (v[3]) << 32) : (uint64_t (v[3]) << 24))
+ (BE ? (uint64_t (v[4]) << 24) : (uint64_t (v[4]) << 32))
+ (BE ? (uint64_t (v[5]) << 16) : (uint64_t (v[5]) << 40))
+ (BE ? (uint64_t (v[6]) << 8) : (uint64_t (v[6]) << 48))
+ (BE ? (uint64_t (v[7]) ) : (uint64_t (v[7]) << 56));
}
private: uint8_t v[8];
};
/* Floats. */
template <bool BE, typename Type, int Bytes>
struct HBFloat
{
using IntType = typename std::conditional<Bytes == 4, uint32_t, uint64_t>::type;
public:
HBFloat () = default;
HBFloat (Type V)
{
#if HB_FAST_NUM_ACCESS
{
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
{
((hb_packed_t<Type> *) v)->v = V;
return;
}
}
#endif
union {
hb_packed_t<Type> f;
hb_packed_t<IntType> i;
} u = {{V}};
const HBInt<BE, IntType> I = u.i.v;
for (unsigned i = 0; i < Bytes; i++)
v[i] = I.v[i];
}
/* c++14 constexpr */ operator Type () const
{
#if HB_FAST_NUM_ACCESS
{
if (BE == (__BYTE_ORDER == __BIG_ENDIAN))
return ((const hb_packed_t<Type> *) v)->v;
}
#endif
HBInt<BE, IntType> I;
for (unsigned i = 0; i < Bytes; i++)
I.v[i] = v[i];
union {
hb_packed_t<IntType> i;
hb_packed_t<Type> f;
} u = {{I}};
return u.f.v;
}
private: uint8_t v[Bytes];
};
/* We want our rounding towards +infinity. */
static inline double
_hb_roundf (double x) { return floor (x + .5); }
@ -210,6 +301,27 @@ _hb_roundf (float x) { return floorf (x + .5f); }
#define roundf(x) _hb_roundf(x)
static inline void
hb_sincos (float rotation, float &s, float &c)
{
#ifdef HAVE_SINCOSF
sincosf (rotation, &s, &c);
#else
c = cosf (rotation);
s = sinf (rotation);
#endif
}
static inline void
hb_sincos (double rotation, double &s, double &c)
{
#ifdef HAVE_SINCOS
sincos (rotation, &s, &c);
#else
c = cos (rotation);
s = sin (rotation);
#endif
}
/* Encodes three unsigned integers in one 64-bit number. If the inputs have more than 21 bits,
* values will be truncated / overlap, and might not decode exactly. */
@ -1070,6 +1182,7 @@ _hb_cmp_operator (const void *pkey, const void *pval)
}
template <typename V, typename K, typename ...Ts>
HB_HOT
static inline bool
hb_bsearch_impl (unsigned *pos, /* Out */
const K& key,

View file

@ -83,6 +83,7 @@ struct hb_cache_t
v = -1;
}
HB_HOT
bool get (unsigned int key, unsigned int *value) const
{
unsigned int k = key & ((1u<<cache_bits)-1);
@ -94,14 +95,14 @@ struct hb_cache_t
return true;
}
bool set (unsigned int key, unsigned int value)
HB_HOT
void set (unsigned int key, unsigned int value)
{
if (unlikely ((key >> key_bits) || (value >> value_bits)))
return false; /* Overflows */
return; /* Overflows */
unsigned int k = key & ((1u<<cache_bits)-1);
unsigned int v = ((key>>cache_bits)<<value_bits) | value;
values[k] = v;
return true;
}
private:

View file

@ -77,7 +77,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs>
coords = coords_;
num_coords = num_coords_;
varStore = acc.varStore;
do_blend = num_coords && coords && varStore->size;
do_blend = num_coords && varStore->size;
set_ivs (acc.privateDicts[fd].ivs);
}

View file

@ -545,8 +545,11 @@ hb_script_to_iso15924_tag (hb_script_t script)
* Fetches the #hb_direction_t of a script when it is
* set horizontally. All right-to-left scripts will return
* #HB_DIRECTION_RTL. All left-to-right scripts will return
* #HB_DIRECTION_LTR. Scripts that can be written either
* horizontally or vertically will return #HB_DIRECTION_INVALID.
* #HB_DIRECTION_LTR.
*
* Scripts that can be written either right-to-left or
* left-to-right will return #HB_DIRECTION_INVALID.
*
* Unknown scripts will return #HB_DIRECTION_LTR.
*
* Return value: The horizontal #hb_direction_t of @script

View file

@ -73,9 +73,7 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
return nullptr;
}
unsigned num_axes = hb_ot_var_get_axis_count (face);
// https://github.com/harfbuzz/harfbuzz/issues/5163
if (num_axes)
if (font->num_coords)
{
CFMutableDictionaryRef variations =
CFDictionaryCreateMutable (kCFAllocatorDefault,
@ -83,15 +81,14 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
unsigned count = hb_max (num_axes, font->num_coords);
unsigned count = font->num_coords;
for (unsigned i = 0; i < count; i++)
{
hb_ot_var_axis_info_t info;
unsigned int c = 1;
hb_ot_var_get_axis_infos (font->face, i, &c, &info);
float v = i < font->num_coords ?
hb_clamp (font->design_coords[i], info.min_value, info.max_value) :
info.default_value;
float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value);
CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag);
CFNumberRef value_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberFloatType, &v);

View file

@ -206,8 +206,9 @@ create_cg_font (hb_blob_t *blob, unsigned int index)
if (unlikely (named_instance_index != 0))
{
// https://github.com/harfbuzz/harfbuzz/issues/5300
// https://github.com/harfbuzz/harfbuzz/issues/5354
#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 110000) || \
(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101300) || \
(defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) || \
(defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED >= 110000) || \
(defined(__WATCH_OS_VERSION_MIN_REQUIRED) && __WATCH_OS_VERSION_MIN_REQUIRED >= 40000) || \
(defined(__MACCATALYST_VERSION_MIN_REQUIRED) && __MACCATALYST_VERSION_MIN_REQUIRED >= 130100) || \

View file

@ -287,7 +287,7 @@ typedef void (*hb_font_get_glyph_shape_func_t) (hb_font_t *font, void *font_data
* A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
*
* Since: 7.0.0
* XDeprecated: REPLACEME: Use hb_font_draw_glyph_func_or_fail_t instead.
* Deprecated: 11.2.0: Use hb_font_draw_glyph_func_or_fail_t instead.
**/
typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data,
hb_codepoint_t glyph,
@ -308,7 +308,7 @@ typedef void (*hb_font_draw_glyph_func_t) (hb_font_t *font, void *font_data,
* A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
*
* Since: 7.0.0
* XDeprecated: REPLACEME: Use hb_font_paint_glyph_or_fail_func_t instead.
* Deprecated: 11.2.0: Use hb_font_paint_glyph_or_fail_func_t instead.
*/
typedef hb_bool_t (*hb_font_paint_glyph_func_t) (hb_font_t *font, void *font_data,
hb_codepoint_t glyph,
@ -346,7 +346,7 @@ hb_font_funcs_set_glyph_shape_func (hb_font_funcs_t *ffuncs,
* Sets the implementation function for #hb_font_draw_glyph_func_t.
*
* Since: 7.0.0
* XDeprecated: REPLACEME: Use hb_font_funcs_set_draw_glyph_or_fail_func instead.
* Deprecated: 11.2.0: Use hb_font_funcs_set_draw_glyph_or_fail_func instead.
**/
HB_DEPRECATED_FOR (hb_font_funcs_set_draw_glyph_or_fail_func)
HB_EXTERN void
@ -364,7 +364,7 @@ hb_font_funcs_set_draw_glyph_func (hb_font_funcs_t *ffuncs,
* Sets the implementation function for #hb_font_paint_glyph_func_t.
*
* Since: 7.0.0
* XDeprecated: REPLACEME: Use hb_font_funcs_set_paint_glyph_or_fail_func() instead.
* Deprecated: 11.2.0: Use hb_font_funcs_set_paint_glyph_or_fail_func() instead.
*/
HB_DEPRECATED_FOR (hb_font_funcs_set_paint_glyph_or_fail_func)
HB_EXTERN void

View file

@ -161,6 +161,58 @@ _hb_directwrite_table_data_release (void *data)
hb_free (context);
}
static hb_blob_t *
_hb_directwrite_get_file_blob (IDWriteFontFace *dw_face)
{
UINT32 file_count;
if (FAILED (dw_face->GetFiles(&file_count, NULL)))
return nullptr;
IDWriteFontFile **files = new IDWriteFontFile*[file_count];
if (FAILED (dw_face->GetFiles(&file_count, files)))
{
delete [] files;
return nullptr;
}
hb_blob_t *blob = nullptr;
for (UINT32 i = 0; i < file_count; i++)
{
LPCVOID reference_key;
UINT32 reference_key_size;
if (FAILED (files[i]->GetReferenceKey(&reference_key, &reference_key_size)))
continue;
IDWriteFontFileLoader *loader;
if (FAILED (files[i]->GetLoader(&loader)))
continue;
IDWriteFontFileStream *stream;
if (FAILED (loader->CreateStreamFromKey (reference_key, reference_key_size, &stream)))
{
loader->Release ();
continue;
}
UINT64 file_size;
const void *fragment;
void *context;
if (FAILED (stream->GetFileSize(&file_size)) ||
FAILED (stream->ReadFileFragment (&fragment, 0, file_size, &context)))
{
loader->Release ();
continue;
}
blob = hb_blob_create ((const char *) fragment, file_size, HB_MEMORY_MODE_DUPLICATE, NULL, NULL);
stream->ReleaseFileFragment (context);
loader->Release ();
break;
}
delete [] files;
return blob;
}
static hb_blob_t *
_hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
{
@ -169,6 +221,9 @@ _hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *
uint32_t length;
void *table_context;
BOOL exists;
if (tag == HB_TAG_NONE)
return _hb_directwrite_get_file_blob (dw_face);
if (FAILED (dw_face->TryGetFontTable (hb_uint32_swap (tag), &data,
&length, &table_context, &exists)))
return nullptr;

View file

@ -63,14 +63,14 @@ hb_draw_quadratic_to_nil (hb_draw_funcs_t *dfuncs, void *draw_data,
float to_x, float to_y,
void *user_data HB_UNUSED)
{
#define HB_ONE_THIRD 0.33333333f
#define HB_TWO_THIRD 0.66666666666666666666666667f
dfuncs->emit_cubic_to (draw_data, *st,
(st->current_x + 2.f * control_x) * HB_ONE_THIRD,
(st->current_y + 2.f * control_y) * HB_ONE_THIRD,
(to_x + 2.f * control_x) * HB_ONE_THIRD,
(to_y + 2.f * control_y) * HB_ONE_THIRD,
st->current_x + (control_x - st->current_x) * HB_TWO_THIRD,
st->current_y + (control_y - st->current_y) * HB_TWO_THIRD,
to_x + (control_x - to_x) * HB_TWO_THIRD,
to_y + (control_y - to_y) * HB_TWO_THIRD,
to_x, to_y);
#undef HB_ONE_THIRD
#undef HB_TWO_THIRD
}
static void
@ -277,7 +277,7 @@ hb_draw_funcs_destroy (hb_draw_funcs_t *dfuncs)
* @destroy: (nullable): A callback to call when @data is not needed anymore
* @replace: Whether to replace an existing data with the same key
*
* Attaches a user-data key/data pair to the specified draw-functions structure.
* Attaches a user-data key/data pair to the specified draw-functions structure.
*
* Return value: `true` if success, `false` otherwise
*
@ -467,7 +467,7 @@ hb_draw_extents_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
float to_x, float to_y,
void *user_data HB_UNUSED)
{
hb_extents_t *extents = (hb_extents_t *) data;
hb_extents_t<> *extents = (hb_extents_t<> *) data;
extents->add_point (to_x, to_y);
}
@ -479,7 +479,7 @@ hb_draw_extents_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
float to_x, float to_y,
void *user_data HB_UNUSED)
{
hb_extents_t *extents = (hb_extents_t *) data;
hb_extents_t<> *extents = (hb_extents_t<> *) data;
extents->add_point (to_x, to_y);
}
@ -492,7 +492,7 @@ hb_draw_extents_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
float to_x, float to_y,
void *user_data HB_UNUSED)
{
hb_extents_t *extents = (hb_extents_t *) data;
hb_extents_t<> *extents = (hb_extents_t<> *) data;
extents->add_point (control_x, control_y);
extents->add_point (to_x, to_y);
@ -507,7 +507,7 @@ hb_draw_extents_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
float to_x, float to_y,
void *user_data HB_UNUSED)
{
hb_extents_t *extents = (hb_extents_t *) data;
hb_extents_t<> *extents = (hb_extents_t<> *) data;
extents->add_point (control1_x, control1_y);
extents->add_point (control2_x, control2_y);

View file

@ -169,8 +169,7 @@ _hb_face_builder_get_table_tags (const hb_face_t *face HB_UNUSED,
if (unlikely (start_offset >= population))
{
if (table_count)
*table_count = 0;
*table_count = 0;
return population;
}

View file

@ -329,7 +329,7 @@ hb_face_create_from_file_or_fail (const char *file_name,
return face;
}
static struct supported_face_loaders_t {
static const struct supported_face_loaders_t {
char name[16];
hb_face_t * (*from_file) (const char *font_file, unsigned face_index);
hb_face_t * (*from_blob) (hb_blob_t *blob, unsigned face_index);

View file

@ -246,7 +246,6 @@ hb_font_get_glyph_v_advance_nil (hb_font_t *font,
hb_codepoint_t glyph HB_UNUSED,
void *user_data HB_UNUSED)
{
/* TODO use font_extents.ascender+descender */
return -font->y_scale;
}
@ -352,6 +351,10 @@ hb_font_get_glyph_h_origin_default (hb_font_t *font,
hb_position_t *y,
void *user_data HB_UNUSED)
{
if (font->has_glyph_h_origins_func_set ())
{
return font->get_glyph_h_origins (1, &glyph, 0, x, 0, y, 0, false);
}
hb_bool_t ret = font->parent->get_glyph_h_origin (glyph, x, y);
if (ret)
font->parent_scale_position (x, y);
@ -366,7 +369,6 @@ hb_font_get_glyph_v_origin_nil (hb_font_t *font HB_UNUSED,
hb_position_t *y,
void *user_data HB_UNUSED)
{
*x = *y = 0;
return false;
}
@ -378,12 +380,100 @@ hb_font_get_glyph_v_origin_default (hb_font_t *font,
hb_position_t *y,
void *user_data HB_UNUSED)
{
if (font->has_glyph_v_origins_func_set ())
{
return font->get_glyph_v_origins (1, &glyph, 0, x, 0, y, 0, false);
}
hb_bool_t ret = font->parent->get_glyph_v_origin (glyph, x, y);
if (ret)
font->parent_scale_position (x, y);
return ret;
}
#define hb_font_get_glyph_h_origins_nil hb_font_get_glyph_h_origins_default
static hb_bool_t
hb_font_get_glyph_h_origins_default (hb_font_t *font HB_UNUSED,
void *font_data HB_UNUSED,
unsigned int count,
const hb_codepoint_t *first_glyph HB_UNUSED,
unsigned glyph_stride HB_UNUSED,
hb_position_t *first_x,
unsigned x_stride,
hb_position_t *first_y,
unsigned y_stride,
void *user_data HB_UNUSED)
{
if (font->has_glyph_h_origin_func_set ())
{
for (unsigned int i = 0; i < count; i++)
{
font->get_glyph_h_origin (*first_glyph, first_x, first_y, false);
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
}
return true;
}
hb_bool_t ret = font->parent->get_glyph_h_origins (count,
first_glyph, glyph_stride,
first_x, x_stride,
first_y, y_stride);
if (ret)
{
for (unsigned i = 0; i < count; i++)
{
font->parent_scale_position (first_x, first_y);
first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
}
}
return ret;
}
#define hb_font_get_glyph_v_origins_nil hb_font_get_glyph_v_origins_default
static hb_bool_t
hb_font_get_glyph_v_origins_default (hb_font_t *font HB_UNUSED,
void *font_data HB_UNUSED,
unsigned int count,
const hb_codepoint_t *first_glyph HB_UNUSED,
unsigned glyph_stride HB_UNUSED,
hb_position_t *first_x,
unsigned x_stride,
hb_position_t *first_y,
unsigned y_stride,
void *user_data HB_UNUSED)
{
if (font->has_glyph_v_origin_func_set ())
{
for (unsigned int i = 0; i < count; i++)
{
font->get_glyph_v_origin (*first_glyph, first_x, first_y, false);
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
}
return true;
}
hb_bool_t ret = font->parent->get_glyph_v_origins (count,
first_glyph, glyph_stride,
first_x, x_stride,
first_y, y_stride);
if (ret)
{
for (unsigned i = 0; i < count; i++)
{
font->parent_scale_position (first_x, first_y);
first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
}
}
return ret;
}
static hb_position_t
hb_font_get_glyph_h_kerning_nil (hb_font_t *font HB_UNUSED,
void *font_data HB_UNUSED,
@ -1256,6 +1346,77 @@ hb_font_get_glyph_v_origin (hb_font_t *font,
return font->get_glyph_v_origin (glyph, x, y);
}
/**
* hb_font_get_glyph_h_origins:
* @font: #hb_font_t to work upon
* @count: The number of glyph IDs in the sequence queried
* @first_glyph: The first glyph ID to query
* @glyph_stride: The stride between successive glyph IDs
* @first_x: (out): The first X coordinate of the origin retrieved
* @x_stride: The stride between successive X coordinates
* @first_y: (out): The first Y coordinate of the origin retrieved
* @y_stride: The stride between successive Y coordinates
*
* Fetches the (X,Y) coordinates of the origin for requested glyph IDs
* in the specified font, for horizontal text segments.
*
* Return value: `true` if data found, `false` otherwise
*
* Since: 11.3.0
**/
hb_bool_t
hb_font_get_glyph_h_origins (hb_font_t *font,
unsigned int count,
const hb_codepoint_t *first_glyph,
unsigned int glyph_stride,
hb_position_t *first_x,
unsigned int x_stride,
hb_position_t *first_y,
unsigned int y_stride)
{
return font->get_glyph_h_origins (count,
first_glyph, glyph_stride,
first_x, x_stride,
first_y, y_stride);
}
/**
* hb_font_get_glyph_v_origins:
* @font: #hb_font_t to work upon
* @count: The number of glyph IDs in the sequence queried
* @first_glyph: The first glyph ID to query
* @glyph_stride: The stride between successive glyph IDs
* @first_x: (out): The first X coordinate of the origin retrieved
* @x_stride: The stride between successive X coordinates
* @first_y: (out): The first Y coordinate of the origin retrieved
* @y_stride: The stride between successive Y coordinates
*
* Fetches the (X,Y) coordinates of the origin for requested glyph IDs
* in the specified font, for vertical text segments.
*
* Return value: `true` if data found, `false` otherwise
*
* Since: 11.3.0
**/
hb_bool_t
hb_font_get_glyph_v_origins (hb_font_t *font,
unsigned int count,
const hb_codepoint_t *first_glyph,
unsigned int glyph_stride,
hb_position_t *first_x,
unsigned int x_stride,
hb_position_t *first_y,
unsigned int y_stride)
{
return font->get_glyph_v_origins (count,
first_glyph, glyph_stride,
first_x, x_stride,
first_y, y_stride);
}
/**
* hb_font_get_glyph_h_kerning:
* @font: #hb_font_t to work upon
@ -1443,7 +1604,7 @@ hb_font_get_glyph_shape (hb_font_t *font,
*
* Return value: `true` if glyph was drawn, `false` otherwise
*
* XSince: REPLACEME
* Since: 11.2.0
**/
hb_bool_t
hb_font_draw_glyph_or_fail (hb_font_t *font,
@ -1480,7 +1641,7 @@ hb_font_draw_glyph_or_fail (hb_font_t *font,
*
* Return value: `true` if glyph was painted, `false` otherwise
*
* XSince: REPLACEME
* Since: 11.2.0
*/
hb_bool_t
hb_font_paint_glyph_or_fail (hb_font_t *font,
@ -1883,6 +2044,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) =
1000, /* x_scale */
1000, /* y_scale */
false, /* is_synthetic */
0.f, /* x_embolden */
0.f, /* y_embolden */
true, /* embolden_in_place */
@ -1900,6 +2062,7 @@ DEFINE_NULL_INSTANCE (hb_font_t) =
0, /* ptem */
HB_FONT_NO_VAR_NAMED_INSTANCE, /* instance_index */
false, /* has_nonzero_coords */
0, /* num_coords */
nullptr, /* coords */
nullptr, /* design_coords */
@ -1960,8 +2123,14 @@ hb_font_create (hb_face_t *face)
hb_font_set_funcs_using (font, nullptr);
#ifndef HB_NO_VAR
if (face && face->index >> 16)
hb_font_set_var_named_instance (font, (face->index >> 16) - 1);
// Initialize variations.
if (likely (face))
{
if (face->index >> 16)
hb_font_set_var_named_instance (font, (face->index >> 16) - 1);
else
hb_font_set_variations (font, nullptr, 0);
}
#endif
return font;
@ -1979,6 +2148,7 @@ _hb_font_adopt_var_coords (hb_font_t *font,
font->coords = coords;
font->design_coords = design_coords;
font->num_coords = coords_length;
font->has_nonzero_coords = hb_any (hb_array (coords, coords_length));
font->changed ();
font->serial_coords = font->serial;
@ -2393,7 +2563,7 @@ hb_font_set_funcs_data (hb_font_t *font,
font->changed ();
}
static struct supported_font_funcs_t {
static const struct supported_font_funcs_t {
char name[16];
void (*func) (hb_font_t *);
} supported_font_funcs[] =
@ -2450,6 +2620,9 @@ hb_bool_t
hb_font_set_funcs_using (hb_font_t *font,
const char *name)
{
if (unlikely (hb_object_is_immutable (font)))
return false;
bool retry = false;
if (!name || !*name)
@ -2704,12 +2877,12 @@ hb_font_get_ptem (hb_font_t *font)
*
* Return value: `true` if the font is synthetic, `false` otherwise.
*
* XSince: REPLACEME
* Since: 11.2.0
*/
hb_bool_t
hb_font_is_synthetic (hb_font_t *font)
{
return font->is_synthetic ();
return font->is_synthetic;
}
/**
@ -2858,12 +3031,6 @@ hb_font_set_variations (hb_font_t *font,
if (hb_object_is_immutable (font))
return;
if (!variations_length && font->instance_index == HB_FONT_NO_VAR_NAMED_INSTANCE)
{
hb_font_set_var_coords_normalized (font, nullptr, 0);
return;
}
const OT::fvar &fvar = *font->face->table.fvar;
auto axes = fvar.get_axes ();
const unsigned coords_length = axes.length;
@ -2970,7 +3137,6 @@ hb_font_set_variation (hb_font_t *font,
hb_ot_var_normalize_coords (font->face, coords_length, design_coords, normalized);
_hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
}
/**
@ -2991,11 +3157,16 @@ hb_font_set_variation (hb_font_t *font,
void
hb_font_set_var_coords_design (hb_font_t *font,
const float *coords,
unsigned int coords_length)
unsigned int input_coords_length)
{
if (hb_object_is_immutable (font))
return;
const OT::fvar &fvar = *font->face->table.fvar;
auto axes = fvar.get_axes ();
const unsigned coords_length = axes.length;
input_coords_length = hb_min (input_coords_length, coords_length);
int *normalized = coords_length ? (int *) hb_calloc (coords_length, sizeof (int)) : nullptr;
float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (float)) : nullptr;
@ -3006,8 +3177,11 @@ hb_font_set_var_coords_design (hb_font_t *font,
return;
}
if (coords_length)
hb_memcpy (design_coords, coords, coords_length * sizeof (font->design_coords[0]));
if (input_coords_length)
hb_memcpy (design_coords, coords, input_coords_length * sizeof (font->design_coords[0]));
// Fill in the rest with default values
for (unsigned int i = input_coords_length; i < coords_length; i++)
design_coords[i] = axes[i].get_default ();
hb_ot_var_normalize_coords (font->face, coords_length, coords, normalized);
_hb_font_adopt_var_coords (font, normalized, design_coords, coords_length);
@ -3072,34 +3246,31 @@ hb_font_get_var_named_instance (hb_font_t *font)
void
hb_font_set_var_coords_normalized (hb_font_t *font,
const int *coords, /* 2.14 normalized */
unsigned int coords_length)
unsigned int input_coords_length)
{
if (hb_object_is_immutable (font))
return;
const OT::fvar &fvar = *font->face->table.fvar;
auto axes = fvar.get_axes ();
unsigned coords_length = axes.length;
input_coords_length = hb_min (input_coords_length, coords_length);
int *copy = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr;
int *unmapped = coords_length ? (int *) hb_calloc (coords_length, sizeof (coords[0])) : nullptr;
float *design_coords = coords_length ? (float *) hb_calloc (coords_length, sizeof (design_coords[0])) : nullptr;
if (unlikely (coords_length && !(copy && unmapped && design_coords)))
if (unlikely (coords_length && !(copy && design_coords)))
{
hb_free (copy);
hb_free (unmapped);
hb_free (design_coords);
return;
}
if (coords_length)
{
hb_memcpy (copy, coords, coords_length * sizeof (coords[0]));
hb_memcpy (unmapped, coords, coords_length * sizeof (coords[0]));
}
if (input_coords_length)
hb_memcpy (copy, coords, input_coords_length * sizeof (coords[0]));
/* Best effort design coords simulation */
font->face->table.avar->unmap_coords (unmapped, coords_length);
for (unsigned int i = 0; i < coords_length; ++i)
design_coords[i] = font->face->table.fvar->unnormalize_axis_value (i, unmapped[i]);
hb_free (unmapped);
design_coords[i] = NAN;
_hb_font_adopt_var_coords (font, copy, design_coords, coords_length);
}
@ -3112,8 +3283,8 @@ hb_font_set_var_coords_normalized (hb_font_t *font,
* Fetches the list of normalized variation coordinates currently
* set on a font.
*
* Note that this returned array may only contain values for some
* (or none) of the axes; omitted axes effectively have zero values.
* <note>Note that if no variation coordinates are set, this function may
* return %NULL.</note>
*
* Return value is valid as long as variation coordinates of the font
* are not modified.
@ -3140,9 +3311,12 @@ hb_font_get_var_coords_normalized (hb_font_t *font,
* Fetches the list of variation coordinates (in design-space units) currently
* set on a font.
*
* Note that this returned array may only contain values for some
* (or none) of the axes; omitted axes effectively have their default
* values.
* <note>Note that if no variation coordinates are set, this function may
* return %NULL.</note>
*
* <note>If variations have been set on the font using normalized coordinates
* (i.e. via hb_font_set_var_coords_normalized()), the design coordinates will
* have NaN (Not a Number) values.</note>
*
* Return value is valid as long as variation coordinates of the font
* are not modified.

View file

@ -97,7 +97,7 @@ hb_font_funcs_is_immutable (hb_font_funcs_t *ffuncs);
* @descender: The depth of typographic descenders.
* @line_gap: The suggested line-spacing gap.
*
* Font-wide extent values, measured in font units.
* Font-wide extent values, measured in scaled units.
*
* Note that typically @ascender is positive and @descender
* negative, in coordinate systems that grow up.
@ -332,7 +332,7 @@ typedef hb_font_get_glyph_advances_func_t hb_font_get_glyph_v_advances_func_t;
*
* A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
*
* This method should retrieve the (X,Y) coordinates (in font units) of the
* This method should retrieve the (X,Y) coordinates (in scaled units) of the
* origin for a glyph. Each coordinate must be returned in an #hb_position_t
* output parameter.
*
@ -349,7 +349,7 @@ typedef hb_bool_t (*hb_font_get_glyph_origin_func_t) (hb_font_t *font, void *fon
*
* A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
*
* This method should retrieve the (X,Y) coordinates (in font units) of the
* This method should retrieve the (X,Y) coordinates (in scaled units) of the
* origin for a glyph, for horizontal-direction text segments. Each
* coordinate must be returned in an #hb_position_t output parameter.
*
@ -361,13 +361,72 @@ typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_h_origin_func_t;
*
* A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
*
* This method should retrieve the (X,Y) coordinates (in font units) of the
* This method should retrieve the (X,Y) coordinates (in scaled units) of the
* origin for a glyph, for vertical-direction text segments. Each coordinate
* must be returned in an #hb_position_t output parameter.
*
**/
typedef hb_font_get_glyph_origin_func_t hb_font_get_glyph_v_origin_func_t;
/**
* hb_font_get_glyph_origins_func_t:
* @font: #hb_font_t to work upon
* @font_data: @font user data pointer
* @first_glyph: The first glyph ID to query
* @count: number of glyphs to query
* @glyph_stride: The stride between successive glyph IDs
* @first_x: (out): The first origin X coordinate retrieved
* @x_stride: The stride between successive origin X coordinates
* @first_y: (out): The first origin Y coordinate retrieved
* @y_stride: The stride between successive origin Y coordinates
* @user_data: User data pointer passed by the caller
*
* A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
*
* This method should retrieve the (X,Y) coordinates (in scaled units) of the
* origin for each requested glyph. Each coordinate value must be returned in
* an #hb_position_t in the two output parameters.
*
* Return value: `true` if data found, `false` otherwise
*
* Since: 11.3.0
**/
typedef hb_bool_t (*hb_font_get_glyph_origins_func_t) (hb_font_t *font, void *font_data,
unsigned int count,
const hb_codepoint_t *first_glyph,
unsigned glyph_stride,
hb_position_t *first_x,
unsigned x_stride,
hb_position_t *first_y,
unsigned y_stride,
void *user_data);
/**
* hb_font_get_glyph_h_origins_func_t:
*
* A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
*
* This method should retrieve the (X,Y) coordinates (in scaled units) of the
* origin for requested glyph, for horizontal-direction text segments. Each
* coordinate must be returned in a the x/y #hb_position_t output parameters.
*
* Since: 11.3.0
**/
typedef hb_font_get_glyph_origins_func_t hb_font_get_glyph_h_origins_func_t;
/**
* hb_font_get_glyph_v_origins_func_t:
*
* A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
*
* This method should retrieve the (X,Y) coordinates (in scaled units) of the
* origin for requested glyph, for vertical-direction text segments. Each
* coordinate must be returned in a the x/y #hb_position_t output parameters.
*
* Since: 11.3.0
**/
typedef hb_font_get_glyph_origins_func_t hb_font_get_glyph_v_origins_func_t;
/**
* hb_font_get_glyph_kerning_func_t:
* @font: #hb_font_t to work upon
@ -428,7 +487,7 @@ typedef hb_bool_t (*hb_font_get_glyph_extents_func_t) (hb_font_t *font, void *fo
*
* A virtual method for the #hb_font_funcs_t of an #hb_font_t object.
*
* This method should retrieve the (X,Y) coordinates (in font units) for a
* This method should retrieve the (X,Y) coordinates (in scaled units) for a
* specified contour point in a glyph. Each coordinate must be returned as
* an #hb_position_t output parameter.
*
@ -498,7 +557,7 @@ typedef hb_bool_t (*hb_font_get_glyph_from_name_func_t) (hb_font_t *font, void *
*
* Return value: `true` if glyph was drawn, `false` otherwise
*
* XSince: REPLACEME
* Since: 11.2.0
**/
typedef hb_bool_t (*hb_font_draw_glyph_or_fail_func_t) (hb_font_t *font, void *font_data,
hb_codepoint_t glyph,
@ -520,7 +579,7 @@ typedef hb_bool_t (*hb_font_draw_glyph_or_fail_func_t) (hb_font_t *font, void *f
*
* Return value: `true` if glyph was painted, `false` otherwise
*
* XSince: REPLACEME
* Since: 11.2.0
*/
typedef hb_bool_t (*hb_font_paint_glyph_or_fail_func_t) (hb_font_t *font, void *font_data,
hb_codepoint_t glyph,
@ -707,6 +766,38 @@ hb_font_funcs_set_glyph_v_origin_func (hb_font_funcs_t *ffuncs,
hb_font_get_glyph_v_origin_func_t func,
void *user_data, hb_destroy_func_t destroy);
/**
* hb_font_funcs_set_glyph_h_origins_func:
* @ffuncs: A font-function structure
* @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
* @user_data: Data to pass to @func
* @destroy: (nullable): The function to call when @user_data is not needed anymore
*
* Sets the implementation function for #hb_font_get_glyph_h_origins_func_t.
*
* Since: 11.3.0
**/
HB_EXTERN void
hb_font_funcs_set_glyph_h_origins_func (hb_font_funcs_t *ffuncs,
hb_font_get_glyph_h_origins_func_t func,
void *user_data, hb_destroy_func_t destroy);
/**
* hb_font_funcs_set_glyph_v_origins_func:
* @ffuncs: A font-function structure
* @func: (closure user_data) (destroy destroy) (scope notified): The callback function to assign
* @user_data: Data to pass to @func
* @destroy: (nullable): The function to call when @user_data is not needed anymore
*
* Sets the implementation function for #hb_font_get_glyph_v_origins_func_t.
*
* Since: 11.3.0
**/
HB_EXTERN void
hb_font_funcs_set_glyph_v_origins_func (hb_font_funcs_t *ffuncs,
hb_font_get_glyph_v_origins_func_t func,
void *user_data, hb_destroy_func_t destroy);
/**
* hb_font_funcs_set_glyph_h_kerning_func:
* @ffuncs: A font-function structure
@ -796,7 +887,7 @@ hb_font_funcs_set_glyph_from_name_func (hb_font_funcs_t *ffuncs,
*
* Sets the implementation function for #hb_font_draw_glyph_or_fail_func_t.
*
* XSince: REPLACEME
* Since: 11.2.0
**/
HB_EXTERN void
hb_font_funcs_set_draw_glyph_or_fail_func (hb_font_funcs_t *ffuncs,
@ -812,7 +903,7 @@ hb_font_funcs_set_draw_glyph_or_fail_func (hb_font_funcs_t *ffuncs,
*
* Sets the implementation function for #hb_font_paint_glyph_or_fail_func_t.
*
* XSince: REPLACEME
* Since: 11.2.0
*/
HB_EXTERN void
hb_font_funcs_set_paint_glyph_or_fail_func (hb_font_funcs_t *ffuncs,
@ -876,6 +967,26 @@ hb_font_get_glyph_v_origin (hb_font_t *font,
hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y);
HB_EXTERN hb_bool_t
hb_font_get_glyph_h_origins (hb_font_t *font,
unsigned int count,
const hb_codepoint_t *first_glyph,
unsigned glyph_stride,
hb_position_t *first_x,
unsigned x_stride,
hb_position_t *first_y,
unsigned y_stride);
HB_EXTERN hb_bool_t
hb_font_get_glyph_v_origins (hb_font_t *font,
unsigned int count,
const hb_codepoint_t *first_glyph,
unsigned glyph_stride,
hb_position_t *first_x,
unsigned x_stride,
hb_position_t *first_y,
unsigned y_stride);
HB_EXTERN hb_position_t
hb_font_get_glyph_h_kerning (hb_font_t *font,
hb_codepoint_t left_glyph, hb_codepoint_t right_glyph);

View file

@ -55,6 +55,8 @@
HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_advances) \
HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origin) \
HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origin) \
HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_origins) \
HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_origins) \
HB_FONT_FUNC_IMPLEMENT (get_,glyph_h_kerning) \
HB_IF_NOT_DEPRECATED (HB_FONT_FUNC_IMPLEMENT (get_,glyph_v_kerning)) \
HB_FONT_FUNC_IMPLEMENT (get_,glyph_extents) \
@ -118,6 +120,8 @@ struct hb_font_t
int32_t x_scale;
int32_t y_scale;
bool is_synthetic;
float x_embolden;
float y_embolden;
bool embolden_in_place;
@ -139,6 +143,7 @@ struct hb_font_t
/* Font variation coordinates. */
unsigned int instance_index;
bool has_nonzero_coords;
unsigned int num_coords;
int *coords;
float *design_coords;
@ -430,21 +435,135 @@ struct hb_font_t
}
hb_bool_t get_glyph_h_origin (hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y)
hb_position_t *x, hb_position_t *y,
bool synthetic = true)
{
*x = *y = 0;
return klass->get.f.glyph_h_origin (this, user_data,
glyph, x, y,
!klass->user_data ? nullptr : klass->user_data->glyph_h_origin);
bool ret = klass->get.f.glyph_h_origin (this, user_data,
glyph, x, y,
!klass->user_data ? nullptr : klass->user_data->glyph_h_origin);
if (synthetic && ret)
{
/* Slant */
if (slant_xy)
*x += roundf (*y * slant_xy);
/* Embolden */
if (!embolden_in_place)
{
*x += x_scale < 0 ? -x_strength : x_strength;
*y += y_scale < 0 ? -y_strength : y_strength;
}
}
return ret;
}
hb_bool_t get_glyph_v_origin (hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y)
hb_position_t *x, hb_position_t *y,
bool synthetic = true)
{
*x = *y = 0;
return klass->get.f.glyph_v_origin (this, user_data,
glyph, x, y,
!klass->user_data ? nullptr : klass->user_data->glyph_v_origin);
bool ret = klass->get.f.glyph_v_origin (this, user_data,
glyph, x, y,
!klass->user_data ? nullptr : klass->user_data->glyph_v_origin);
if (synthetic && ret)
{
/* Slant */
if (slant_xy)
*x += roundf (*y * slant_xy);
/* Embolden */
if (!embolden_in_place)
{
*x += x_scale < 0 ? -x_strength : x_strength;
*y += y_scale < 0 ? -y_strength : y_strength;
}
}
return ret;
}
hb_bool_t get_glyph_h_origins (unsigned int count,
const hb_codepoint_t *first_glyph,
unsigned int glyph_stride,
hb_position_t *first_x,
unsigned int x_stride,
hb_position_t *first_y,
unsigned int y_stride,
bool synthetic = true)
{
bool ret = klass->get.f.glyph_h_origins (this, user_data,
count,
first_glyph, glyph_stride,
first_x, x_stride, first_y, y_stride,
!klass->user_data ? nullptr : klass->user_data->glyph_h_origins);
if (synthetic && ret)
{
hb_position_t x_shift = x_scale < 0 ? -x_strength : x_strength;
hb_position_t y_shift = y_scale < 0 ? -y_strength : y_strength;
for (unsigned i = 0; i < count; i++)
{
/* Slant */
if (slant_xy)
*first_x += roundf (*first_y * slant_xy);
/* Embolden */
if (!embolden_in_place)
{
*first_x += x_shift;
*first_y += y_shift;
}
}
first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
}
return ret;
}
hb_bool_t get_glyph_v_origins (unsigned int count,
const hb_codepoint_t *first_glyph,
unsigned int glyph_stride,
hb_position_t *first_x,
unsigned int x_stride,
hb_position_t *first_y,
unsigned int y_stride,
bool synthetic = true)
{
bool ret = klass->get.f.glyph_v_origins (this, user_data,
count,
first_glyph, glyph_stride,
first_x, x_stride, first_y, y_stride,
!klass->user_data ? nullptr : klass->user_data->glyph_v_origins);
if (synthetic && is_synthetic && ret)
{
hb_position_t x_shift = x_scale < 0 ? -x_strength : x_strength;
hb_position_t y_shift = y_scale < 0 ? -y_strength : y_strength;
for (unsigned i = 0; i < count; i++)
{
/* Slant */
if (slant_xy)
*first_x += roundf (*first_y * slant_xy);
/* Embolden */
if (!embolden_in_place)
{
*first_x += x_shift;
*first_y += y_shift;
}
}
first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
}
return ret;
}
hb_position_t get_glyph_h_kerning (hb_codepoint_t left_glyph,
@ -486,7 +605,7 @@ struct hb_font_t
extents,
!klass->user_data ? nullptr : klass->user_data->glyph_extents);
}
if (!is_synthetic () &&
if (!is_synthetic &&
klass->get.f.glyph_extents (this, user_data,
glyph,
extents,
@ -508,7 +627,7 @@ struct hb_font_t
#endif
#ifndef HB_NO_DRAW
hb_extents_t draw_extents;
hb_extents_t<> draw_extents;
if (draw_glyph_or_fail (glyph,
hb_draw_extents_get_funcs (), &draw_extents))
{
@ -714,6 +833,28 @@ struct hb_font_t
get_glyph_v_advances (count, first_glyph, glyph_stride, first_advance, advance_stride);
}
void apply_offset (hb_position_t *x, hb_position_t *y,
hb_position_t dx, hb_position_t dy,
signed mult)
{
assert (mult == -1 || mult == +1);
*x += dx * mult;
*y += dy * mult;
}
void add_offset (hb_position_t *x, hb_position_t *y,
hb_position_t dx, hb_position_t dy)
{
*x += dx;
*y += dy;
}
void subtract_offset (hb_position_t *x, hb_position_t *y,
hb_position_t dx, hb_position_t dy)
{
*x -= dx;
*y -= dy;
}
void guess_v_origin_minus_h_origin (hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y)
{
@ -724,6 +865,141 @@ struct hb_font_t
*y = extents.ascender;
}
void apply_glyph_h_origins_with_fallback (hb_buffer_t *buf, int mult)
{
bool has_ascender = false;
hb_position_t ascender = 0;
struct { hb_position_t x, y; } origins[32];
unsigned int offset = 0;
unsigned int count = buf->len;
while (offset < count)
{
unsigned n = hb_min (count - offset, ARRAY_LENGTH (origins));
if (!get_glyph_h_origins (n,
&buf->info[offset].codepoint, sizeof (hb_glyph_info_t),
&origins[0].x, sizeof (origins[0]),
&origins[0].y, sizeof (origins[0])))
{
if (get_glyph_v_origins (n,
&buf->info[offset].codepoint, sizeof (hb_glyph_info_t),
&origins[0].x, sizeof (origins[0]),
&origins[0].y, sizeof (origins[0])))
{
if (!has_ascender)
{
hb_font_extents_t extents;
get_h_extents_with_fallback (&extents);
ascender = extents.ascender;
has_ascender = true;
}
/* We got the v_origins, adjust them to h_origins. */
for (unsigned j = 0; j < n; j++)
{
hb_codepoint_t glyph = buf->info[offset + j].codepoint;
origins[j].x -= get_glyph_h_advance (glyph) / 2;
origins[j].y -= ascender;
}
}
else
{
for (unsigned j = 0; j < n; j++)
{
origins[j].x = 0;
origins[j].y = 0;
}
}
}
assert (mult == -1 || mult == +1);
if (mult == +1)
for (unsigned j = 0; j < n; j++)
{
hb_glyph_position_t *pos = &buf->pos[offset + j];
add_offset (&pos->x_offset, &pos->y_offset,
origins[j].x, origins[j].y);
}
else /* mult == -1 */
for (unsigned j = 0; j < n; j++)
{
hb_glyph_position_t *pos = &buf->pos[offset + j];
subtract_offset (&pos->x_offset, &pos->y_offset,
origins[j].x, origins[j].y);
}
offset += n;
}
}
void apply_glyph_v_origins_with_fallback (hb_buffer_t *buf, int mult)
{
bool has_ascender = false;
hb_position_t ascender = 0;
struct { hb_position_t x, y; } origins[32];
unsigned int offset = 0;
unsigned int count = buf->len;
while (offset < count)
{
unsigned n = hb_min (count - offset, ARRAY_LENGTH (origins));
if (!get_glyph_v_origins (n,
&buf->info[offset].codepoint, sizeof (hb_glyph_info_t),
&origins[0].x, sizeof (origins[0]),
&origins[0].y, sizeof (origins[0])))
{
if (get_glyph_h_origins (n,
&buf->info[offset].codepoint, sizeof (hb_glyph_info_t),
&origins[0].x, sizeof (origins[0]),
&origins[0].y, sizeof (origins[0])))
{
if (!has_ascender)
{
hb_font_extents_t extents;
get_h_extents_with_fallback (&extents);
ascender = extents.ascender;
has_ascender = true;
}
/* We got the h_origins, adjust them to v_origins. */
for (unsigned j = 0; j < n; j++)
{
hb_codepoint_t glyph = buf->info[offset + j].codepoint;
origins[j].x += get_glyph_h_advance (glyph) / 2;
origins[j].y += ascender;
}
}
else
{
for (unsigned j = 0; j < n; j++)
{
origins[j].x = 0;
origins[j].y = 0;
}
}
}
assert (mult == -1 || mult == +1);
if (mult == +1)
for (unsigned j = 0; j < n; j++)
{
hb_glyph_position_t *pos = &buf->pos[offset + j];
add_offset (&pos->x_offset, &pos->y_offset,
origins[j].x, origins[j].y);
}
else /* mult == -1 */
for (unsigned j = 0; j < n; j++)
{
hb_glyph_position_t *pos = &buf->pos[offset + j];
subtract_offset (&pos->x_offset, &pos->y_offset,
origins[j].x, origins[j].y);
}
offset += n;
}
}
void get_glyph_h_origin_with_fallback (hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y)
{
@ -732,7 +1008,7 @@ struct hb_font_t
{
hb_position_t dx, dy;
guess_v_origin_minus_h_origin (glyph, &dx, &dy);
*x -= dx; *y -= dy;
subtract_offset (x, y, dx, dy);
}
}
void get_glyph_v_origin_with_fallback (hb_codepoint_t glyph,
@ -743,7 +1019,7 @@ struct hb_font_t
{
hb_position_t dx, dy;
guess_v_origin_minus_h_origin (glyph, &dx, &dy);
*x += dx; *y += dy;
add_offset (x, y, dx, dy);
}
}
@ -757,68 +1033,38 @@ struct hb_font_t
get_glyph_v_origin_with_fallback (glyph, x, y);
}
void add_glyph_h_origin (hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y)
void add_glyph_h_origins (hb_buffer_t *buf)
{
hb_position_t origin_x, origin_y;
get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y);
*x += origin_x;
*y += origin_y;
apply_glyph_h_origins_with_fallback (buf, +1);
}
void add_glyph_v_origin (hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y)
void add_glyph_v_origins (hb_buffer_t *buf)
{
hb_position_t origin_x, origin_y;
get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y);
*x += origin_x;
*y += origin_y;
apply_glyph_v_origins_with_fallback (buf, +1);
}
void add_glyph_origin_for_direction (hb_codepoint_t glyph,
hb_direction_t direction,
hb_position_t *x, hb_position_t *y)
{
hb_position_t origin_x, origin_y;
get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y);
*x += origin_x;
*y += origin_y;
add_offset (x, y, origin_x, origin_y);
}
void subtract_glyph_h_origin (hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y)
void subtract_glyph_h_origins (hb_buffer_t *buf)
{
hb_position_t origin_x, origin_y;
get_glyph_h_origin_with_fallback (glyph, &origin_x, &origin_y);
*x -= origin_x;
*y -= origin_y;
apply_glyph_h_origins_with_fallback (buf, -1);
}
void subtract_glyph_v_origin (hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y)
void subtract_glyph_v_origins (hb_buffer_t *buf)
{
hb_position_t origin_x, origin_y;
get_glyph_v_origin_with_fallback (glyph, &origin_x, &origin_y);
*x -= origin_x;
*y -= origin_y;
apply_glyph_v_origins_with_fallback (buf, -1);
}
void subtract_glyph_origin_for_direction (hb_codepoint_t glyph,
hb_direction_t direction,
hb_position_t *x, hb_position_t *y)
{
hb_position_t origin_x, origin_y;
get_glyph_origin_for_direction (glyph, direction, &origin_x, &origin_y);
*x -= origin_x;
*y -= origin_y;
subtract_offset (x, y, origin_x, origin_y);
}
void get_glyph_kerning_for_direction (hb_codepoint_t first_glyph, hb_codepoint_t second_glyph,
@ -900,11 +1146,6 @@ struct hb_font_t
return false;
}
bool is_synthetic () const
{
return x_embolden || y_embolden || slant;
}
void changed ()
{
float upem = face->get_upem ();
@ -916,6 +1157,8 @@ struct hb_font_t
bool y_neg = y_scale < 0;
y_mult = (y_neg ? -((int64_t) -y_scale << 16) : ((int64_t) y_scale << 16)) / upem;
is_synthetic = x_embolden || y_embolden || slant;
x_strength = roundf (abs (x_scale) * x_embolden);
y_strength = roundf (abs (y_scale) * y_embolden);

View file

@ -90,7 +90,7 @@ struct hb_ft_paint_context_t
funcs (paint_funcs), data (paint_data),
palette (palette), palette_index (palette_index), foreground (foreground)
{
if (font->is_synthetic ())
if (font->is_synthetic)
{
font = hb_font_create_sub_font (font);
hb_font_set_synthetic_bold (font, 0, 0, true);
@ -412,9 +412,9 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
float dx = paint.u.translate.dx / 65536.f;
float dy = paint.u.translate.dy / 65536.f;
bool p1 = c->funcs->push_translate (c->data, dx, dy);
c->funcs->push_translate (c->data, dx, dy);
c->recurse (paint.u.translate.paint);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
break;
case FT_COLR_PAINTFORMAT_SCALE:
@ -424,13 +424,9 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
float sx = paint.u.scale.scale_x / 65536.f;
float sy = paint.u.scale.scale_y / 65536.f;
bool p1 = c->funcs->push_translate (c->data, +dx, +dy);
bool p2 = c->funcs->push_scale (c->data, sx, sy);
bool p3 = c->funcs->push_translate (c->data, -dx, -dy);
c->funcs->push_scale_around_center (c->data, sx, sy, dx, dy);
c->recurse (paint.u.scale.paint);
if (p3) c->funcs->pop_transform (c->data);
if (p2) c->funcs->pop_transform (c->data);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
break;
case FT_COLR_PAINTFORMAT_ROTATE:
@ -439,13 +435,9 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
float dy = paint.u.rotate.center_y / 65536.f;
float a = paint.u.rotate.angle / 65536.f;
bool p1 = c->funcs->push_translate (c->data, +dx, +dy);
bool p2 = c->funcs->push_rotate (c->data, a);
bool p3 = c->funcs->push_translate (c->data, -dx, -dy);
c->funcs->push_rotate_around_center (c->data, a, dx, dy);
c->recurse (paint.u.rotate.paint);
if (p3) c->funcs->pop_transform (c->data);
if (p2) c->funcs->pop_transform (c->data);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
break;
case FT_COLR_PAINTFORMAT_SKEW:
@ -455,13 +447,9 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
float sx = paint.u.skew.x_skew_angle / 65536.f;
float sy = paint.u.skew.y_skew_angle / 65536.f;
bool p1 = c->funcs->push_translate (c->data, +dx, +dy);
bool p2 = c->funcs->push_skew (c->data, sx, sy);
bool p3 = c->funcs->push_translate (c->data, -dx, -dy);
c->funcs->push_skew_around_center (c->data, sx, sy, dx, dy);
c->recurse (paint.u.skew.paint);
if (p3) c->funcs->pop_transform (c->data);
if (p2) c->funcs->pop_transform (c->data);
if (p1) c->funcs->pop_transform (c->data);
c->funcs->pop_transform (c->data);
}
break;
case FT_COLR_PAINTFORMAT_COMPOSITE:

View file

@ -143,6 +143,9 @@ _hb_ft_font_destroy (void *data)
/* hb_font changed, update FT_Face. */
static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face)
{
if (unlikely (font->destroy != (hb_destroy_func_t) _hb_ft_font_destroy))
return;
hb_ft_font_t *ft_font = (hb_ft_font_t *) font->user_data;
float x_mult = 1.f, y_mult = 1.f;
@ -184,12 +187,14 @@ static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face)
FT_Set_Transform (ft_face, &matrix, nullptr);
ft_font->transform = true;
}
else
FT_Set_Transform (ft_face, nullptr, nullptr);
#if defined(HAVE_FT_GET_VAR_BLEND_COORDINATES) && !defined(HB_NO_VAR)
unsigned int num_coords;
const float *coords = hb_font_get_var_coords_design (font, &num_coords);
if (num_coords)
if (font->has_nonzero_coords)
{
unsigned int num_coords;
const float *coords = hb_font_get_var_coords_design (font, &num_coords);
FT_Fixed *ft_coords = (FT_Fixed *) hb_calloc (num_coords, sizeof (FT_Fixed));
if (ft_coords)
{
@ -199,6 +204,12 @@ static void _hb_ft_hb_font_changed (hb_font_t *font, FT_Face ft_face)
hb_free (ft_coords);
}
}
else if (font->num_coords)
{
// Some old versions of FreeType crash if we
// call this function on non-variable fonts.
FT_Set_Var_Design_Coordinates (ft_face, 0, nullptr);
}
#endif
}
@ -1093,6 +1104,10 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data
FT_ULong length = 0;
FT_Error error;
/* In new FreeType, a tag value of 1 loads the SFNT table directory. Reject it. */
if (tag == 1)
return nullptr;
/* Note: FreeType like HarfBuzz uses the NONE tag for fetching the entire blob */
error = FT_Load_Sfnt_Table (ft_face, tag, 0, nullptr, &length);
@ -1366,7 +1381,7 @@ hb_ft_font_changed (hb_font_t *font)
for (unsigned int i = 0; i < mm_var->num_axis; ++i)
{
coords[i] = ft_coords[i] >>= 2;
coords[i] = (ft_coords[i] + 2) >> 2;
nonzero = nonzero || coords[i];
}
@ -1717,7 +1732,12 @@ hb_ft_font_set_funcs (hb_font_t *font)
ft_face->generic.finalizer = _release_blob;
// And the FT_Library to the blob
hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true);
if (unlikely (!hb_blob_set_user_data (blob, &ft_library_key, ft_library, destroy_ft_library, true)))
{
DEBUG_MSG (FT, font, "hb_blob_set_user_data() failed");
FT_Done_Face (ft_face);
return;
}
_hb_ft_font_set_funcs (font, ft_face, true);
hb_ft_font_set_load_flags (font, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING);

View file

@ -26,7 +26,10 @@
#include "hb.hh"
#include "hb-algs.hh"
template <typename Float = float>
struct hb_extents_t
{
hb_extents_t () {}
@ -35,7 +38,7 @@ struct hb_extents_t
ymin (hb_min (extents.y_bearing, extents.y_bearing + extents.height)),
xmax (hb_max (extents.x_bearing, extents.x_bearing + extents.width)),
ymax (hb_max (extents.y_bearing, extents.y_bearing + extents.height)) {}
hb_extents_t (float xmin, float ymin, float xmax, float ymax) :
hb_extents_t (Float xmin, Float ymin, Float xmax, Float ymax) :
xmin (xmin), ymin (ymin), xmax (xmax), ymax (ymax) {}
bool is_empty () const { return xmin >= xmax || ymin >= ymax; }
@ -69,7 +72,7 @@ struct hb_extents_t
}
void
add_point (float x, float y)
add_point (Float x, Float y)
{
if (unlikely (is_void ()))
{
@ -97,62 +100,69 @@ struct hb_extents_t
yneg ? y1 - y0 : y0 - y1};
}
float xmin = 0.f;
float ymin = 0.f;
float xmax = -1.f;
float ymax = -1.f;
Float xmin = 0;
Float ymin = 0;
Float xmax = -1;
Float ymax = -1;
};
template <typename Float = float>
struct hb_transform_t
{
hb_transform_t () {}
hb_transform_t (float xx, float yx,
float xy, float yy,
float x0, float y0) :
hb_transform_t (Float xx, Float yx,
Float xy, Float yy,
Float x0, Float y0) :
xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {}
bool is_identity () const
{
return xx == 1.f && yx == 0.f &&
xy == 0.f && yy == 1.f &&
x0 == 0.f && y0 == 0.f;
return xx == 1 && yx == 0 &&
xy == 0 && yy == 1 &&
x0 == 0 && y0 == 0;
}
bool is_translation () const
{
return xx == 1 && yx == 0 &&
xy == 0 && yy == 1;
}
void multiply (const hb_transform_t &o)
void multiply (const hb_transform_t &o, bool before=false)
{
/* Copied from cairo, with "o" being "a" there and "this" being "b" there. */
hb_transform_t r;
r.xx = o.xx * xx + o.yx * xy;
r.yx = o.xx * yx + o.yx * yy;
r.xy = o.xy * xx + o.yy * xy;
r.yy = o.xy * yx + o.yy * yy;
r.x0 = o.x0 * xx + o.y0 * xy + x0;
r.y0 = o.x0 * yx + o.y0 * yy + y0;
*this = r;
// Copied from cairo-matrix.c
const hb_transform_t &a = before ? o : *this;
const hb_transform_t &b = before ? *this : o;
*this = {
a.xx * b.xx + a.xy * b.yx,
a.yx * b.xx + a.yy * b.yx,
a.xx * b.xy + a.xy * b.yy,
a.yx * b.xy + a.yy * b.yy,
a.xx * b.x0 + a.xy * b.y0 + a.x0,
a.yx * b.x0 + a.yy * b.y0 + a.y0
};
}
void transform_distance (float &dx, float &dy) const
HB_ALWAYS_INLINE
void transform_distance (Float &dx, Float &dy) const
{
float new_x = xx * dx + xy * dy;
float new_y = yx * dx + yy * dy;
Float new_x = xx * dx + xy * dy;
Float new_y = yx * dx + yy * dy;
dx = new_x;
dy = new_y;
}
void transform_point (float &x, float &y) const
HB_ALWAYS_INLINE
void transform_point (Float &x, Float &y) const
{
transform_distance (x, y);
x += x0;
y += y0;
Float new_x = x0 + xx * x + xy * y;
Float new_y = y0 + yx * x + yy * y;
x = new_x;
y = new_y;
}
void transform_extents (hb_extents_t &extents) const
void transform_extents (hb_extents_t<Float> &extents) const
{
float quad_x[4], quad_y[4];
Float quad_x[4], quad_y[4];
quad_x[0] = extents.xmin;
quad_y[0] = extents.ymin;
@ -163,7 +173,7 @@ struct hb_transform_t
quad_x[3] = extents.xmax;
quad_y[3] = extents.ymax;
extents = hb_extents_t {};
extents = hb_extents_t<Float> {};
for (unsigned i = 0; i < 4; i++)
{
transform_point (quad_x[i], quad_y[i]);
@ -171,20 +181,36 @@ struct hb_transform_t
}
}
void transform (const hb_transform_t &o) { multiply (o); }
void transform (const hb_transform_t &o, bool before=false) { multiply (o, before); }
void translate (float x, float y)
static hb_transform_t translation (Float x, Float y)
{
if (x == 0.f && y == 0.f)
return;
return {1, 0, 0, 1, x, y};
}
void translate (Float x, Float y, bool before=false)
{
if (before)
{
x0 += x;
y0 += y;
}
else
{
if (x == 0 && y == 0)
return;
x0 += xx * x + xy * y;
y0 += yx * x + yy * y;
x0 += xx * x + xy * y;
y0 += yx * x + yy * y;
}
}
void scale (float scaleX, float scaleY)
static hb_transform_t scaling (Float scaleX, Float scaleY)
{
if (scaleX == 1.f && scaleY == 1.f)
return {scaleX, 0, 0, scaleY, 0, 0};
}
void scale (Float scaleX, Float scaleY)
{
if (scaleX == 1 && scaleY == 1)
return;
xx *= scaleX;
@ -192,52 +218,94 @@ struct hb_transform_t
xy *= scaleY;
yy *= scaleY;
}
void rotate (float rotation)
static hb_transform_t scaling_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y)
{
if (rotation == 0.f)
return {scaleX, 0, 0, scaleY,
center_x ? (1 - scaleX) * center_x : 0,
center_y ? (1 - scaleY) * center_y : 0};
}
void scale_around_center (Float scaleX, Float scaleY, Float center_x, Float center_y)
{
if (scaleX == 1 && scaleY == 1)
return;
transform (scaling_around_center (scaleX, scaleY, center_x, center_y));
}
static hb_transform_t rotation (Float radians)
{
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L240
rotation = rotation * HB_PI;
float c;
float s;
#ifdef HAVE_SINCOSF
sincosf (rotation, &s, &c);
#else
c = cosf (rotation);
s = sinf (rotation);
#endif
auto other = hb_transform_t{c, s, -s, c, 0.f, 0.f};
transform (other);
Float c;
Float s;
hb_sincos (radians, s, c);
return {c, s, -s, c, 0, 0};
}
void skew (float skewX, float skewY)
void rotate (Float radians, bool before=false)
{
if (skewX == 0.f && skewY == 0.f)
if (radians == 0)
return;
// https://github.com/fonttools/fonttools/blob/f66ee05f71c8b57b5f519ee975e95edcd1466e14/Lib/fontTools/misc/transform.py#L255
skewX = skewX * HB_PI;
skewY = skewY * HB_PI;
auto other = hb_transform_t{1.f,
skewY ? tanf (skewY) : 0.f,
skewX ? tanf (skewX) : 0.f,
1.f,
0.f, 0.f};
transform (other);
transform (rotation (radians), before);
}
float xx = 1.f;
float yx = 0.f;
float xy = 0.f;
float yy = 1.f;
float x0 = 0.f;
float y0 = 0.f;
static hb_transform_t rotation_around_center (Float radians, Float center_x, Float center_y)
{
Float s, c;
hb_sincos (radians, s, c);
return {
c, s, -s, c,
(1 - c) * center_x + s * center_y,
-s * center_x + (1 - c) * center_y
};
}
void rotate_around_center (Float radians, Float center_x, Float center_y, bool before=false)
{
if (radians == 0)
return;
transform (rotation_around_center (radians, center_x, center_y), before);
}
static hb_transform_t skewing (Float skewX, Float skewY)
{
return {1, skewY ? tanf (skewY) : 0, skewX ? tanf (skewX) : 0, 1, 0, 0};
}
void skew (Float skewX, Float skewY)
{
if (skewX == 0 && skewY == 0)
return;
transform (skewing (skewX, skewY));
}
static hb_transform_t skewing_around_center (Float skewX, Float skewY, Float center_x, Float center_y)
{
skewX = skewX ? tanf (skewX) : 0;
skewY = skewY ? tanf (skewY) : 0;
return {
1, skewY, skewX, 1,
center_y ? -skewX * center_y : 0,
center_x ? -skewY * center_x : 0
};
}
void skew_around_center (Float skewX, Float skewY, Float center_x, Float center_y)
{
if (skewX == 0 && skewY == 0)
return;
transform (skewing_around_center (skewX, skewY, center_x, center_y));
}
Float xx = 1;
Float yx = 0;
Float xy = 0;
Float yy = 1;
Float x0 = 0;
Float y0 = 0;
};
#define HB_TRANSFORM_IDENTITY hb_transform_t{1.f, 0.f, 0.f, 1.f, 0.f, 0.f}
#define HB_TRANSFORM_IDENTITY {1, 0, 0, 1, 0, 0}
template <typename Float = float>
struct hb_bounds_t
{
enum status_t {
@ -247,7 +315,7 @@ struct hb_bounds_t
};
hb_bounds_t (status_t status = UNBOUNDED) : status (status) {}
hb_bounds_t (const hb_extents_t &extents) :
hb_bounds_t (const hb_extents_t<Float> &extents) :
status (extents.is_empty () ? EMPTY : BOUNDED), extents (extents) {}
void union_ (const hb_bounds_t &o)
@ -281,20 +349,21 @@ struct hb_bounds_t
}
status_t status;
hb_extents_t extents;
hb_extents_t<Float> extents;
};
template <typename Float = float>
struct hb_transform_decomposed_t
{
float translateX = 0;
float translateY = 0;
float rotation = 0; // in degrees, counter-clockwise
float scaleX = 1;
float scaleY = 1;
float skewX = 0; // in degrees, counter-clockwise
float skewY = 0; // in degrees, counter-clockwise
float tCenterX = 0;
float tCenterY = 0;
Float translateX = 0;
Float translateY = 0;
Float rotation = 0; // in radians, counter-clockwise
Float scaleX = 1;
Float scaleY = 1;
Float skewX = 0; // in radians, counter-clockwise
Float skewY = 0; // in radians, counter-clockwise
Float tCenterX = 0;
Float tCenterY = 0;
operator bool () const
{
@ -305,9 +374,9 @@ struct hb_transform_decomposed_t
tCenterX || tCenterY;
}
hb_transform_t to_transform () const
hb_transform_t<Float> to_transform () const
{
hb_transform_t t;
hb_transform_t<Float> t;
t.translate (translateX + tCenterX, translateY + tCenterY);
t.rotate (rotation);
t.scale (scaleX, scaleY);

View file

@ -127,11 +127,7 @@ hb_glib_unicode_compose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
hb_codepoint_t *ab,
void *user_data HB_UNUSED)
{
#if GLIB_CHECK_VERSION(2,29,12)
return g_unichar_compose (a, b, ab);
#else
return false;
#endif
}
static hb_bool_t
@ -141,11 +137,7 @@ hb_glib_unicode_decompose (hb_unicode_funcs_t *ufuncs HB_UNUSED,
hb_codepoint_t *b,
void *user_data HB_UNUSED)
{
#if GLIB_CHECK_VERSION(2,29,12)
return g_unichar_decompose (ab, a, b);
#else
return false;
#endif
}

View file

@ -51,12 +51,6 @@
/* g++ didn't like older gtype.h gcc-only code path. */
#include <glib.h>
#if !GLIB_CHECK_VERSION(2,29,16)
#undef __GNUC__
#undef __GNUC_MINOR__
#define __GNUC__ 2
#define __GNUC_MINOR__ 6
#endif
#include "hb-gobject.h"

View file

@ -66,13 +66,15 @@ static inline Type& StructAtOffsetUnaligned(void *P, unsigned int offset)
}
/* StructAfter<T>(X) returns the struct T& that is placed after X.
* Works with X of variable size also. X must implement get_size() */
template<typename Type, typename TObject>
static inline const Type& StructAfter(const TObject &X)
{ return StructAtOffset<Type>(&X, X.get_size()); }
template<typename Type, typename TObject>
static inline Type& StructAfter(TObject &X)
{ return StructAtOffset<Type>(&X, X.get_size()); }
* Works with X of variable size also. X must implement get_size().
* Any extra arguments are forwarded to get_size, so for example
* it can work with UnsizedArrayOf<> as well. */
template <typename Type, typename TObject, typename ...Ts>
static inline const Type& StructAfter(const TObject &X, Ts... args)
{ return StructAtOffset<Type>(&X, X.get_size(args...)); }
template <typename Type, typename TObject, typename ...Ts>
static inline Type& StructAfter(TObject &X, Ts... args)
{ return StructAtOffset<Type>(&X, X.get_size(args...)); }
/*
@ -132,7 +134,6 @@ static inline Type& StructAfter(TObject &X)
DEFINE_SIZE_ARRAY(size, array)
/*
* Lazy loaders.
*

View file

@ -54,35 +54,40 @@ namespace OT {
*/
/* Integer types in big-endian order and no alignment requirement */
template <typename Type,
template <bool BE,
typename Type,
unsigned int Size = sizeof (Type)>
struct IntType
struct NumType
{
typedef Type type;
IntType () = default;
explicit constexpr IntType (Type V) : v {V} {}
IntType& operator = (Type i) { v = i; return *this; }
/* For reason we define cast out operator for signed/unsigned, instead of Type, see:
* https://github.com/harfbuzz/harfbuzz/pull/2875/commits/09836013995cab2b9f07577a179ad7b024130467 */
operator typename std::conditional<std::is_signed<Type>::value, signed, unsigned>::type () const { return v; }
typedef typename std::conditional<std::is_integral<Type>::value,
typename std::conditional<std::is_signed<Type>::value, signed, unsigned>::type,
Type>::type WideType;
bool operator == (const IntType &o) const { return (Type) v == (Type) o.v; }
bool operator != (const IntType &o) const { return !(*this == o); }
NumType () = default;
explicit constexpr NumType (Type V) : v {V} {}
NumType& operator = (Type V) { v = V; return *this; }
IntType& operator += (unsigned count) { *this = *this + count; return *this; }
IntType& operator -= (unsigned count) { *this = *this - count; return *this; }
IntType& operator ++ () { *this += 1; return *this; }
IntType& operator -- () { *this -= 1; return *this; }
IntType operator ++ (int) { IntType c (*this); ++*this; return c; }
IntType operator -- (int) { IntType c (*this); --*this; return c; }
operator WideType () const { return v; }
HB_INTERNAL static int cmp (const IntType *a, const IntType *b)
bool operator == (const NumType &o) const { return (Type) v == (Type) o.v; }
bool operator != (const NumType &o) const { return !(*this == o); }
NumType& operator += (WideType count) { *this = *this + count; return *this; }
NumType& operator -= (WideType count) { *this = *this - count; return *this; }
NumType& operator ++ () { *this += 1; return *this; }
NumType& operator -- () { *this -= 1; return *this; }
NumType operator ++ (int) { NumType c (*this); ++*this; return c; }
NumType operator -- (int) { NumType c (*this); --*this; return c; }
HB_INTERNAL static int cmp (const NumType *a, const NumType *b)
{ return b->cmp (*a); }
HB_INTERNAL static int cmp (const void *a, const void *b)
{
IntType *pa = (IntType *) a;
IntType *pb = (IntType *) b;
NumType *pa = (NumType *) a;
NumType *pb = (NumType *) b;
return pb->cmp (*pa);
}
@ -99,20 +104,32 @@ struct IntType
return_trace (c->check_struct (this));
}
protected:
BEInt<Type, Size> v;
typename std::conditional<std::is_integral<Type>::value,
HBInt<BE, Type, Size>,
HBFloat<BE, Type, Size>>::type v;
public:
DEFINE_SIZE_STATIC (Size);
};
typedef IntType<uint8_t> HBUINT8; /* 8-bit unsigned integer. */
typedef IntType<int8_t> HBINT8; /* 8-bit signed integer. */
typedef IntType<uint16_t> HBUINT16; /* 16-bit unsigned integer. */
typedef IntType<int16_t> HBINT16; /* 16-bit signed integer. */
typedef IntType<uint32_t> HBUINT32; /* 32-bit unsigned integer. */
typedef IntType<int32_t> HBINT32; /* 32-bit signed integer. */
typedef NumType<true, uint8_t> HBUINT8; /* 8-bit big-endian unsigned integer. */
typedef NumType<true, int8_t> HBINT8; /* 8-bit big-endian signed integer. */
typedef NumType<true, uint16_t> HBUINT16; /* 16-bit big-endian unsigned integer. */
typedef NumType<true, int16_t> HBINT16; /* 16-bit big-endian signed integer. */
typedef NumType<true, uint32_t> HBUINT32; /* 32-bit big-endian unsigned integer. */
typedef NumType<true, int32_t> HBINT32; /* 32-bit big-endian signed integer. */
/* Note: we cannot defined a signed HBINT24 because there's no corresponding C type.
* Works for unsigned, but not signed, since we rely on compiler for sign-extension. */
typedef IntType<uint32_t, 3> HBUINT24; /* 24-bit unsigned integer. */
typedef NumType<true, uint32_t, 3> HBUINT24; /* 24-bit big-endian unsigned integer. */
typedef NumType<false, uint16_t> HBUINT16LE; /* 16-bit little-endian unsigned integer. */
typedef NumType<false, int16_t> HBINT16LE; /* 16-bit little-endian signed integer. */
typedef NumType<false, uint32_t> HBUINT32LE; /* 32-bit little-endian unsigned integer. */
typedef NumType<false, int32_t> HBINT32LE; /* 32-bit little-endian signed integer. */
typedef NumType<true, float> HBFLOAT32BE; /* 32-bit little-endian floating point number. */
typedef NumType<true, double> HBFLOAT64BE; /* 64-bit little-endian floating point number. */
typedef NumType<false, float> HBFLOAT32LE; /* 32-bit little-endian floating point number. */
typedef NumType<false, double> HBFLOAT64LE; /* 64-bit little-endian floating point number. */
/* 15-bit unsigned number; top bit used for extension. */
struct HBUINT15 : HBUINT16
@ -218,7 +235,7 @@ typedef HBUINT16 UFWORD;
template <typename Type, unsigned fraction_bits>
struct HBFixed : Type
{
static constexpr float shift = (float) (1 << fraction_bits);
static constexpr float mult = 1.f / (1 << fraction_bits);
static_assert (Type::static_size * 8 > fraction_bits, "");
operator signed () const = delete;
@ -226,8 +243,8 @@ struct HBFixed : Type
explicit operator float () const { return to_float (); }
typename Type::type to_int () const { return Type::v; }
void set_int (typename Type::type i ) { Type::v = i; }
float to_float (float offset = 0) const { return ((int32_t) Type::v + offset) / shift; }
void set_float (float f) { Type::v = roundf (f * shift); }
float to_float (float offset = 0) const { return ((int32_t) Type::v + offset) * mult; }
void set_float (float f) { Type::v = roundf (f / mult); }
public:
DEFINE_SIZE_STATIC (Type::static_size);
};
@ -1707,7 +1724,8 @@ struct TupleValues
static bool decompile (const HBUINT8 *&p /* IN/OUT */,
hb_vector_t<T> &values /* IN/OUT */,
const HBUINT8 *end,
bool consume_all = false)
bool consume_all = false,
unsigned start = 0)
{
unsigned i = 0;
unsigned count = consume_all ? UINT_MAX : values.length;
@ -1725,6 +1743,10 @@ struct TupleValues
}
unsigned stop = i + run_count;
if (unlikely (stop > count)) return false;
unsigned skip = i < start ? hb_min (start - i, run_count) : 0;
i += skip;
if ((control & VALUES_SIZE_MASK) == VALUES_ARE_ZEROS)
{
for (; i < stop; i++)
@ -1733,6 +1755,7 @@ struct TupleValues
else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_WORDS)
{
if (unlikely (p + run_count * HBINT16::static_size > end)) return false;
p += skip * HBINT16::static_size;
#ifndef HB_OPTIMIZE_SIZE
for (; i + 3 < stop; i += 4)
{
@ -1755,6 +1778,7 @@ struct TupleValues
else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_LONGS)
{
if (unlikely (p + run_count * HBINT32::static_size > end)) return false;
p += skip * HBINT32::static_size;
for (; i < stop; i++)
{
values.arrayZ[i] = * (const HBINT32 *) p;
@ -1764,6 +1788,7 @@ struct TupleValues
else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_BYTES)
{
if (unlikely (p + run_count > end)) return false;
p += skip * HBINT8::static_size;
#ifndef HB_OPTIMIZE_SIZE
for (; i + 3 < stop; i += 4)
{
@ -2038,6 +2063,23 @@ struct TupleList : CFF2Index
};
// Alignment
template <unsigned int alignment>
struct Align
{
unsigned get_size (const void *base) const
{
unsigned offset = (const char *) this - (const char *) base;
return (alignment - offset) & (alignment - 1);
}
public:
DEFINE_SIZE_MIN (0);
};
} /* namespace OT */

View file

@ -202,7 +202,11 @@ struct cff2_cs_opset_path_t : cff2_cs_opset_t<cff2_cs_opset_path_t, cff2_path_pa
bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const
{
return get_path_at (font, glyph, draw_session, hb_array (font->coords, font->num_coords));
return get_path_at (font,
glyph,
draw_session,
hb_array (font->coords,
font->has_nonzero_coords ? font->num_coords : 0));
}
bool OT::cff2::accelerator_t::get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t<const int> coords) const

View file

@ -501,10 +501,6 @@ struct CmapSubtableFormat4
this->length = c->length () - table_initpos;
if ((long long) this->length != (long long) c->length () - table_initpos)
{
// Length overflowed. Discard the current object before setting the error condition, otherwise
// discard is a noop which prevents the higher level code from reverting the serializer to the
// pre-error state in cmap4 overflow handling code.
c->pop_discard ();
c->err (HB_SERIALIZE_ERROR_INT_OVERFLOW);
return;
}
@ -1646,7 +1642,7 @@ struct EncodingRecord
CmapSubtable *cmapsubtable = c->push<CmapSubtable> ();
unsigned origin_length = c->length ();
cmapsubtable->serialize (c, it, format, plan, &(base+subtable));
if (c->length () - origin_length > 0) *objidx = c->pop_pack ();
if (c->length () - origin_length > 0 && !c->in_error()) *objidx = c->pop_pack ();
else c->pop_discard ();
}

View file

@ -37,6 +37,7 @@
#include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh"
#include "hb-ot-var-gvar-table.hh"
#include "hb-ot-cff2-table.hh"
#include "hb-ot-cff1-table.hh"
#include "hb-ot-hmtx-table.hh"
@ -64,18 +65,22 @@
using hb_ot_font_advance_cache_t = hb_cache_t<24, 16>;
static_assert (sizeof (hb_ot_font_advance_cache_t) == 1024, "");
using hb_ot_font_origin_cache_t = hb_cache_t<20, 20>;
static_assert (sizeof (hb_ot_font_origin_cache_t) == 1024, "");
struct hb_ot_font_t
{
const hb_ot_face_t *ot_face;
/* h_advance caching */
mutable hb_atomic_t<int> cached_serial;
mutable hb_atomic_t<int> cached_coords_serial;
struct advance_cache_t
struct direction_cache_t
{
mutable hb_atomic_t<hb_ot_font_advance_cache_t *> advance_cache;
mutable hb_atomic_t<OT::ItemVariationStore::cache_t *> varStore_cache;
mutable hb_atomic_t<OT::hb_scalar_cache_t *> varStore_cache;
~advance_cache_t ()
~direction_cache_t ()
{
clear ();
}
@ -116,7 +121,7 @@ struct hb_ot_font_t
goto retry;
}
OT::ItemVariationStore::cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const
OT::hb_scalar_cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const
{
retry:
auto *cache = varStore_cache.get_acquire ();
@ -127,7 +132,7 @@ struct hb_ot_font_t
else
goto retry;
}
void release_varStore_cache (OT::ItemVariationStore::cache_t *cache) const
void release_varStore_cache (OT::hb_scalar_cache_t *cache) const
{
if (!cache)
return;
@ -154,17 +159,157 @@ struct hb_ot_font_t
} h, v;
struct origin_cache_t
{
mutable hb_atomic_t<hb_ot_font_origin_cache_t *> origin_cache;
mutable hb_atomic_t<OT::hb_scalar_cache_t *> varStore_cache;
~origin_cache_t ()
{
clear ();
}
hb_ot_font_origin_cache_t *acquire_origin_cache () const
{
retry:
auto *cache = origin_cache.get_acquire ();
if (!cache)
{
cache = (hb_ot_font_origin_cache_t *) hb_malloc (sizeof (hb_ot_font_origin_cache_t));
if (!cache)
return nullptr;
new (cache) hb_ot_font_origin_cache_t;
return cache;
}
if (origin_cache.cmpexch (cache, nullptr))
return cache;
else
goto retry;
}
void release_origin_cache (hb_ot_font_origin_cache_t *cache) const
{
if (!cache)
return;
if (!origin_cache.cmpexch (nullptr, cache))
hb_free (cache);
}
void clear_origin_cache () const
{
retry:
auto *cache = origin_cache.get_acquire ();
if (!cache)
return;
if (origin_cache.cmpexch (cache, nullptr))
hb_free (cache);
else
goto retry;
}
OT::hb_scalar_cache_t *acquire_varStore_cache (const OT::ItemVariationStore &varStore) const
{
retry:
auto *cache = varStore_cache.get_acquire ();
if (!cache)
return varStore.create_cache ();
if (varStore_cache.cmpexch (cache, nullptr))
return cache;
else
goto retry;
}
void release_varStore_cache (OT::hb_scalar_cache_t *cache) const
{
if (!cache)
return;
if (!varStore_cache.cmpexch (nullptr, cache))
OT::ItemVariationStore::destroy_cache (cache);
}
void clear_varStore_cache () const
{
retry:
auto *cache = varStore_cache.get_acquire ();
if (!cache)
return;
if (varStore_cache.cmpexch (cache, nullptr))
OT::ItemVariationStore::destroy_cache (cache);
else
goto retry;
}
void clear () const
{
clear_origin_cache ();
clear_varStore_cache ();
}
} v_origin;
struct draw_cache_t
{
mutable hb_atomic_t<OT::hb_scalar_cache_t *> gvar_cache;
~draw_cache_t ()
{
clear ();
}
OT::hb_scalar_cache_t *acquire_gvar_cache (const OT::gvar_accelerator_t &gvar) const
{
retry:
auto *cache = gvar_cache.get_acquire ();
if (!cache)
return gvar.create_cache ();
if (gvar_cache.cmpexch (cache, nullptr))
return cache;
else
goto retry;
}
void release_gvar_cache (OT::hb_scalar_cache_t *cache) const
{
if (!cache)
return;
if (!gvar_cache.cmpexch (nullptr, cache))
OT::gvar_accelerator_t::destroy_cache (cache);
}
void clear_gvar_cache () const
{
retry:
auto *cache = gvar_cache.get_acquire ();
if (!cache)
return;
if (gvar_cache.cmpexch (cache, nullptr))
OT::gvar_accelerator_t::destroy_cache (cache);
else
goto retry;
}
void clear () const
{
clear_gvar_cache ();
}
} draw;
void check_serial (hb_font_t *font) const
{
int font_serial = font->serial_coords.get_acquire ();
if (cached_serial.get_acquire () != font_serial)
{
/* These caches are dependent on scale and synthetic settings.
* Any change to the font invalidates them. */
v_origin.clear ();
if (cached_coords_serial.get_acquire () == font_serial)
return;
cached_serial.set_release (font_serial);
}
h.clear ();
v.clear ();
int font_serial_coords = font->serial_coords.get_acquire ();
if (cached_coords_serial.get_acquire () != font_serial_coords)
{
/* These caches are independent of scale or synthetic settings.
* Just variation changes will invalidate them. */
h.clear ();
v.clear ();
draw.clear ();
cached_coords_serial.set_release (font_serial);
cached_coords_serial.set_release (font_serial_coords);
}
}
};
@ -242,37 +387,59 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
unsigned advance_stride,
void *user_data HB_UNUSED)
{
// Duplicated in v_advances. Ugly. Keep in sync'ish.
const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
const hb_ot_face_t *ot_face = ot_font->ot_face;
const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
ot_font->check_serial (font);
const OT::HVAR &HVAR = *hmtx.var_table;
const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore;
OT::ItemVariationStore::cache_t *varStore_cache = ot_font->h.acquire_varStore_cache (varStore);
hb_ot_font_advance_cache_t *advance_cache = nullptr;
bool use_cache = font->num_coords;
if (use_cache)
{
advance_cache = ot_font->h.acquire_advance_cache ();
if (!advance_cache)
use_cache = false;
}
if (!use_cache)
if (unlikely (!hmtx.has_data ()))
{
hb_position_t advance = font->face->get_upem () / 2;
advance = font->em_scale_x (advance);
for (unsigned int i = 0; i < count; i++)
{
*first_advance = font->em_scale_x (hmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache));
*first_advance = advance;
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
return;
}
#ifndef HB_NO_VAR
if (!font->has_nonzero_coords)
{
fallback:
#else
{
#endif
// Just plain htmx data. No need to cache.
for (unsigned int i = 0; i < count; i++)
{
*first_advance = font->em_scale_x (hmtx.get_advance_without_var_unscaled (*first_glyph));
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
return;
}
else
{ /* Use cache. */
#ifndef HB_NO_VAR
/* has_nonzero_coords. */
ot_font->check_serial (font);
hb_ot_font_advance_cache_t *advance_cache = ot_font->h.acquire_advance_cache ();
if (!advance_cache)
{
// malloc failure. Just use the fallback non-variable path.
goto fallback;
}
/* If HVAR is present, use it.*/
const OT::HVAR &HVAR = *hmtx.var_table;
if (HVAR.has_data ())
{
const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore;
OT::hb_scalar_cache_t *varStore_cache = ot_font->h.acquire_varStore_cache (varStore);
for (unsigned int i = 0; i < count; i++)
{
hb_position_t v;
@ -289,10 +456,44 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
ot_font->h.release_varStore_cache (varStore_cache);
ot_font->h.release_advance_cache (advance_cache);
return;
}
ot_font->h.release_varStore_cache (varStore_cache);
const auto &gvar = *ot_face->gvar;
if (gvar.has_data ())
{
const auto &glyf = *ot_face->glyf;
auto *scratch = glyf.acquire_scratch ();
OT::hb_scalar_cache_t *gvar_cache = ot_font->draw.acquire_gvar_cache (gvar);
for (unsigned int i = 0; i < count; i++)
{
hb_position_t v;
unsigned cv;
if (advance_cache->get (*first_glyph, &cv))
v = cv;
else
{
v = glyf.get_advance_with_var_unscaled (*first_glyph, font, false, *scratch, gvar_cache);
advance_cache->set (*first_glyph, v);
}
*first_advance = font->em_scale_x (v);
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
ot_font->draw.release_gvar_cache (gvar_cache);
glyf.release_scratch (scratch);
ot_font->h.release_advance_cache (advance_cache);
return;
}
ot_font->h.release_advance_cache (advance_cache);
// No HVAR or GVAR. Just use the fallback non-variable path.
goto fallback;
#endif
}
#ifndef HB_NO_VERTICAL
@ -305,99 +506,281 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
unsigned advance_stride,
void *user_data HB_UNUSED)
{
// Duplicated from h_advances. Ugly. Keep in sync'ish.
const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
const hb_ot_face_t *ot_face = ot_font->ot_face;
const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
if (vmtx.has_data ())
if (unlikely (!vmtx.has_data ()))
{
hb_font_extents_t font_extents;
font->get_h_extents_with_fallback (&font_extents);
hb_position_t advance = font_extents.ascender - font_extents.descender;
advance = font->em_scale_y (- (int) advance);
for (unsigned int i = 0; i < count; i++)
{
*first_advance = advance;
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
return;
}
#ifndef HB_NO_VAR
if (!font->has_nonzero_coords)
{
fallback:
#else
{
#endif
// Just plain vtmx data. No need to cache.
for (unsigned int i = 0; i < count; i++)
{
*first_advance = font->em_scale_y (- (int) vmtx.get_advance_without_var_unscaled (*first_glyph));
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
return;
}
#ifndef HB_NO_VAR
/* has_nonzero_coords. */
ot_font->check_serial (font);
hb_ot_font_advance_cache_t *advance_cache = ot_font->v.acquire_advance_cache ();
if (!advance_cache)
{
// malloc failure. Just use the fallback non-variable path.
goto fallback;
}
/* If VVAR is present, use it.*/
const OT::VVAR &VVAR = *vmtx.var_table;
if (VVAR.has_data ())
{
ot_font->check_serial (font);
const OT::VVAR &VVAR = *vmtx.var_table;
const OT::ItemVariationStore &varStore = &VVAR + VVAR.varStore;
OT::ItemVariationStore::cache_t *varStore_cache = ot_font->v.acquire_varStore_cache (varStore);
// TODO Use advance_cache.
OT::hb_scalar_cache_t *varStore_cache = ot_font->v.acquire_varStore_cache (varStore);
for (unsigned int i = 0; i < count; i++)
{
*first_advance = font->em_scale_y (-(int) vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache));
hb_position_t v;
unsigned cv;
if (advance_cache->get (*first_glyph, &cv))
v = cv;
else
{
v = vmtx.get_advance_with_var_unscaled (*first_glyph, font, varStore_cache);
advance_cache->set (*first_glyph, v);
}
*first_advance = font->em_scale_y (- (int) v);
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
ot_font->v.release_varStore_cache (varStore_cache);
ot_font->v.release_advance_cache (advance_cache);
return;
}
else
const auto &gvar = *ot_face->gvar;
if (gvar.has_data ())
{
hb_font_extents_t font_extents;
font->get_h_extents_with_fallback (&font_extents);
hb_position_t advance = -(font_extents.ascender - font_extents.descender);
const auto &glyf = *ot_face->glyf;
auto *scratch = glyf.acquire_scratch ();
OT::hb_scalar_cache_t *gvar_cache = ot_font->draw.acquire_gvar_cache (gvar);
for (unsigned int i = 0; i < count; i++)
{
*first_advance = advance;
hb_position_t v;
unsigned cv;
if (advance_cache->get (*first_glyph, &cv))
v = cv;
else
{
v = glyf.get_advance_with_var_unscaled (*first_glyph, font, true, *scratch, gvar_cache);
advance_cache->set (*first_glyph, v);
}
*first_advance = font->em_scale_y (- (int) v);
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
ot_font->draw.release_gvar_cache (gvar_cache);
glyf.release_scratch (scratch);
ot_font->v.release_advance_cache (advance_cache);
return;
}
ot_font->v.release_advance_cache (advance_cache);
// No VVAR or GVAR. Just use the fallback non-variable path.
goto fallback;
#endif
}
#endif
#ifndef HB_NO_VERTICAL
HB_HOT
static hb_bool_t
hb_ot_get_glyph_v_origin (hb_font_t *font,
void *font_data,
hb_codepoint_t glyph,
hb_position_t *x,
hb_position_t *y,
void *user_data HB_UNUSED)
hb_ot_get_glyph_v_origins (hb_font_t *font,
void *font_data,
unsigned int count,
const hb_codepoint_t *first_glyph,
unsigned glyph_stride,
hb_position_t *first_x,
unsigned x_stride,
hb_position_t *first_y,
unsigned y_stride,
void *user_data HB_UNUSED)
{
const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
const hb_ot_face_t *ot_face = ot_font->ot_face;
*x = font->get_glyph_h_advance (glyph) / 2;
const OT::VORG &VORG = *ot_face->VORG;
if (VORG.has_data ())
/* First, set all the x values to half the advance width. */
font->get_glyph_h_advances (count,
first_glyph, glyph_stride,
first_x, x_stride);
for (unsigned i = 0; i < count; i++)
{
float delta = 0;
*first_x /= 2;
first_x = &StructAtOffsetUnaligned<hb_position_t> (first_x, x_stride);
}
/* The vertical origin business is messy...
*
* We allocate the cache, then have various code paths that use the cache.
* Each one is responsible to free it before returning.
*/
hb_ot_font_origin_cache_t *origin_cache = ot_font->v_origin.acquire_origin_cache ();
/* If there is VORG, always use it. It uses VVAR for variations if necessary. */
const OT::VORG &VORG = *ot_face->VORG;
if (origin_cache && VORG.has_data ())
{
#ifndef HB_NO_VAR
const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
const OT::VVAR &VVAR = *vmtx.var_table;
if (font->num_coords)
VVAR.get_vorg_delta_unscaled (glyph,
font->coords, font->num_coords,
&delta);
if (!font->has_nonzero_coords)
#endif
{
for (unsigned i = 0; i < count; i++)
{
hb_position_t origin;
unsigned cv;
if (origin_cache->get (*first_glyph, &cv))
origin = font->y_scale < 0 ? -cv : cv;
else
{
origin = font->em_scalef_y (VORG.get_y_origin (*first_glyph));
origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin);
}
*y = font->em_scalef_y (VORG.get_y_origin (glyph) + delta);
*first_y = origin;
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
}
}
#ifndef HB_NO_VAR
else
{
const OT::VVAR &VVAR = *ot_face->vmtx->var_table;
const auto &varStore = &VVAR + VVAR.varStore;
auto *varStore_cache = ot_font->v_origin.acquire_varStore_cache (varStore);
for (unsigned i = 0; i < count; i++)
{
hb_position_t origin;
unsigned cv;
if (origin_cache->get (*first_glyph, &cv))
origin = font->y_scale < 0 ? -cv : cv;
else
{
origin = font->em_scalef_y (VORG.get_y_origin (*first_glyph) +
VVAR.get_vorg_delta_unscaled (*first_glyph,
font->coords, font->num_coords,
varStore_cache));
origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin);
}
*first_y = origin;
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
}
ot_font->v_origin.release_varStore_cache (varStore_cache);
}
#endif
ot_font->v_origin.release_origin_cache (origin_cache);
return true;
}
hb_glyph_extents_t extents = {0};
if (hb_font_get_glyph_extents (font, glyph, &extents))
/* If and only if `vmtx` is present and it's a `glyf` font,
* we use the top phantom point, deduced from vmtx,glyf[,gvar]. */
const auto &vmtx = *ot_face->vmtx;
const auto &glyf = *ot_face->glyf;
if (origin_cache && vmtx.has_data() && glyf.has_data ())
{
const OT::vmtx_accelerator_t &vmtx = *ot_face->vmtx;
int tsb = 0;
if (vmtx.get_leading_bearing_with_var_unscaled (font, glyph, &tsb))
auto *scratch = glyf.acquire_scratch ();
OT::hb_scalar_cache_t *gvar_cache = font->has_nonzero_coords ?
ot_font->draw.acquire_gvar_cache (*ot_face->gvar) :
nullptr;
for (unsigned i = 0; i < count; i++)
{
*y = extents.y_bearing + font->em_scale_y (tsb);
return true;
hb_position_t origin;
unsigned cv;
if (origin_cache->get (*first_glyph, &cv))
origin = font->y_scale < 0 ? -cv : cv;
else
{
origin = font->em_scalef_y (glyf.get_v_origin_with_var_unscaled (*first_glyph, font, *scratch, gvar_cache));
origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin);
}
*first_y = origin;
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
}
hb_font_extents_t font_extents;
font->get_h_extents_with_fallback (&font_extents);
hb_position_t advance = font_extents.ascender - font_extents.descender;
hb_position_t diff = advance - -extents.height;
*y = extents.y_bearing + (diff >> 1);
if (gvar_cache)
ot_font->draw.release_gvar_cache (gvar_cache);
glyf.release_scratch (scratch);
ot_font->v_origin.release_origin_cache (origin_cache);
return true;
}
hb_font_extents_t font_extents;
font->get_h_extents_with_fallback (&font_extents);
*y = font_extents.ascender;
/* Otherwise, use glyph extents to center the glyph vertically.
* If getting glyph extents failed, just use the font ascender. */
if (origin_cache && font->has_glyph_extents_func ())
{
hb_font_extents_t font_extents;
font->get_h_extents_with_fallback (&font_extents);
hb_position_t font_advance = font_extents.ascender - font_extents.descender;
for (unsigned i = 0; i < count; i++)
{
hb_position_t origin;
unsigned cv;
if (origin_cache->get (*first_glyph, &cv))
origin = font->y_scale < 0 ? -cv : cv;
else
{
hb_glyph_extents_t extents = {0};
if (likely (font->get_glyph_extents (*first_glyph, &extents)))
origin = extents.y_bearing + ((font_advance - -extents.height) >> 1);
else
origin = font_extents.ascender;
origin_cache->set (*first_glyph, font->y_scale < 0 ? -origin : origin);
}
*first_y = origin;
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_y = &StructAtOffsetUnaligned<hb_position_t> (first_y, y_stride);
}
}
ot_font->v_origin.release_origin_cache (origin_cache);
return true;
}
#endif
@ -498,17 +881,33 @@ hb_ot_draw_glyph_or_fail (hb_font_t *font,
hb_draw_funcs_t *draw_funcs, void *draw_data,
void *user_data)
{
const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
hb_draw_session_t draw_session {draw_funcs, draw_data};
bool ret = false;
OT::hb_scalar_cache_t *gvar_cache = nullptr;
if (font->num_coords)
{
ot_font->check_serial (font);
gvar_cache = ot_font->draw.acquire_gvar_cache (*ot_font->ot_face->gvar);
}
#ifndef HB_NO_VAR_COMPOSITES
if (font->face->table.VARC->get_path (font, glyph, draw_session)) return true;
if (font->face->table.VARC->get_path (font, glyph, draw_session)) { ret = true; goto done; }
#endif
// Keep the following in synch with VARC::get_path_at()
if (font->face->table.glyf->get_path (font, glyph, draw_session)) return true;
if (font->face->table.glyf->get_path (font, glyph, draw_session, gvar_cache)) { ret = true; goto done; }
#ifndef HB_NO_CFF
if (font->face->table.cff2->get_path (font, glyph, draw_session)) return true;
if (font->face->table.cff1->get_path (font, glyph, draw_session)) return true;
if (font->face->table.cff2->get_path (font, glyph, draw_session)) { ret = true; goto done; }
if (font->face->table.cff1->get_path (font, glyph, draw_session)) { ret = true; goto done; }
#endif
return false;
done:
ot_font->draw.release_gvar_cache (gvar_cache);
return ret;
}
#endif
@ -548,12 +947,11 @@ static struct hb_ot_font_funcs_lazy_loader_t : hb_font_funcs_lazy_loader_t<hb_ot
hb_font_funcs_set_font_h_extents_func (funcs, hb_ot_get_font_h_extents, nullptr, nullptr);
hb_font_funcs_set_glyph_h_advances_func (funcs, hb_ot_get_glyph_h_advances, nullptr, nullptr);
//hb_font_funcs_set_glyph_h_origin_func (funcs, hb_ot_get_glyph_h_origin, nullptr, nullptr);
#ifndef HB_NO_VERTICAL
hb_font_funcs_set_font_v_extents_func (funcs, hb_ot_get_font_v_extents, nullptr, nullptr);
hb_font_funcs_set_glyph_v_advances_func (funcs, hb_ot_get_glyph_v_advances, nullptr, nullptr);
hb_font_funcs_set_glyph_v_origin_func (funcs, hb_ot_get_glyph_v_origin, nullptr, nullptr);
hb_font_funcs_set_glyph_v_origins_func (funcs, hb_ot_get_glyph_v_origins, nullptr, nullptr);
#endif
#ifndef HB_NO_DRAW

View file

@ -45,16 +45,6 @@
#define HB_OT_TAG_vmtx HB_TAG('v','m','t','x')
HB_INTERNAL bool
_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical, int *lsb);
HB_INTERNAL unsigned
_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical);
HB_INTERNAL bool
_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb);
namespace OT {
@ -237,7 +227,7 @@ struct hmtxvmtx
auto it =
+ hb_iter (c->plan->new_to_old_gid_list)
| hb_map ([c, &_mtx, mtx_map] (hb_codepoint_pair_t _)
| hb_map ([&_mtx, mtx_map] (hb_codepoint_pair_t _)
{
hb_codepoint_t new_gid = _.first;
hb_codepoint_t old_gid = _.second;
@ -246,8 +236,7 @@ struct hmtxvmtx
if (!mtx_map->has (new_gid, &v))
{
int lsb = 0;
if (!_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
(void) _glyf_get_leading_bearing_without_var_unscaled (c->plan->source, old_gid, !T::is_horizontal, &lsb);
_mtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb);
return hb_pair (_mtx.get_advance_without_var_unscaled (old_gid), +lsb);
}
return *v;
@ -326,49 +315,23 @@ struct hmtxvmtx
bool has_data () const { return (bool) num_bearings; }
bool get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph,
void get_leading_bearing_without_var_unscaled (hb_codepoint_t glyph,
int *lsb) const
{
if (glyph < num_long_metrics)
{
*lsb = table->longMetricZ[glyph].sb;
return true;
return;
}
if (unlikely (glyph >= num_bearings))
return false;
{
*lsb = 0;
return;
}
const FWORD *bearings = (const FWORD *) &table->longMetricZ[num_long_metrics];
*lsb = bearings[glyph - num_long_metrics];
return true;
}
bool get_leading_bearing_with_var_unscaled (hb_font_t *font,
hb_codepoint_t glyph,
int *lsb) const
{
if (!font->num_coords)
return get_leading_bearing_without_var_unscaled (glyph, lsb);
#ifndef HB_NO_VAR
float delta;
if (var_table->get_lsb_delta_unscaled (glyph, font->coords, font->num_coords, &delta) &&
get_leading_bearing_without_var_unscaled (glyph, lsb))
{
*lsb += roundf (delta);
return true;
}
// If there's no vmtx data, the phantom points from glyf table are not accurate,
// so we cannot take the next path.
bool is_vertical = T::tableTag == HB_OT_TAG_vmtx;
if (is_vertical && !has_data ())
return false;
return _glyf_get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb);
#else
return false;
#endif
}
unsigned int get_advance_without_var_unscaled (hb_codepoint_t glyph) const
@ -402,27 +365,17 @@ struct hmtxvmtx
return advances[hb_min (glyph - num_bearings, num_advances - num_bearings - 1)];
}
unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph,
hb_font_t *font,
ItemVariationStore::cache_t *store_cache = nullptr) const
#ifndef HB_NO_VAR
unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph,
hb_font_t *font,
hb_scalar_cache_t *store_cache = nullptr) const
{
unsigned int advance = get_advance_without_var_unscaled (glyph);
#ifndef HB_NO_VAR
if (unlikely (glyph >= num_bearings) || !font->num_coords)
return advance;
if (var_table.get_length ())
return advance + roundf (var_table->get_advance_delta_unscaled (glyph,
font->coords, font->num_coords,
store_cache));
unsigned glyf_advance = _glyf_get_advance_with_var_unscaled (font, glyph, T::tableTag == HB_OT_TAG_vmtx);
return glyf_advance ? glyf_advance : advance;
#else
return advance;
#endif
return advance + roundf (var_table->get_advance_delta_unscaled (glyph,
font->coords, font->num_coords,
store_cache));
}
#endif
protected:
// 0 <= num_long_metrics <= num_bearings <= num_advances <= num_glyphs

View file

@ -2548,32 +2548,94 @@ struct SparseVarRegionAxis
DEFINE_SIZE_STATIC (8);
};
#define REGION_CACHE_ITEM_CACHE_INVALID INT_MIN
#define REGION_CACHE_ITEM_MULTIPLIER (float (1 << ((sizeof (int) * 8) - 2)))
#define REGION_CACHE_ITEM_DIVISOR (1.f / float (1 << ((sizeof (int) * 8) - 2)))
struct hb_scalar_cache_t
{
private:
static constexpr unsigned STATIC_LENGTH = 16;
static constexpr int INVALID = INT_MIN;
static constexpr float MULTIPLIER = 1 << ((sizeof (int) * 8) - 2);
static constexpr float DIVISOR = 1.f / MULTIPLIER;
public:
hb_scalar_cache_t () : length (STATIC_LENGTH) { clear (); }
hb_scalar_cache_t (const hb_scalar_cache_t&) = delete;
hb_scalar_cache_t (hb_scalar_cache_t&&) = delete;
hb_scalar_cache_t& operator= (const hb_scalar_cache_t&) = delete;
hb_scalar_cache_t& operator= (hb_scalar_cache_t&&) = delete;
static hb_scalar_cache_t *create (unsigned int count,
hb_scalar_cache_t *scratch_cache = nullptr)
{
if (!count) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t);
if (scratch_cache && count <= scratch_cache->length)
{
scratch_cache->clear ();
return scratch_cache;
}
auto *cache = (hb_scalar_cache_t *) hb_malloc (sizeof (hb_scalar_cache_t) - sizeof (values) + sizeof (values[0]) * count);
if (unlikely (!cache)) return (hb_scalar_cache_t *) &Null(hb_scalar_cache_t);
cache->length = count;
cache->clear ();
return cache;
}
static void destroy (hb_scalar_cache_t *cache,
hb_scalar_cache_t *scratch_cache = nullptr)
{
if (cache != &Null(hb_scalar_cache_t) && cache != scratch_cache)
hb_free (cache);
}
void clear ()
{
for (unsigned i = 0; i < length; i++)
values[i] = INVALID;
}
HB_ALWAYS_INLINE
bool get (unsigned i, float *value) const
{
if (unlikely (i >= length))
{
*value = 0.f;
return true;
}
auto *cached_value = &values[i];
if (*cached_value != INVALID)
{
*value = *cached_value ? *cached_value * DIVISOR : 0.f;
return true;
}
return false;
}
HB_ALWAYS_INLINE
void set (unsigned i, float value)
{
if (unlikely (i >= length)) return;
auto *cached_value = &values[i];
*cached_value = roundf(value * MULTIPLIER);
}
private:
unsigned length;
mutable hb_atomic_t<int> values[STATIC_LENGTH];
};
struct VarRegionList
{
using cache_t = hb_atomic_t<int>;
float evaluate (unsigned int region_index,
const int *coords, unsigned int coord_len,
cache_t *cache = nullptr) const
private:
float evaluate_impl (unsigned int region_index,
const int *coords, unsigned int coord_len) const
{
if (unlikely (region_index >= regionCount))
return 0.;
cache_t *cached_value = nullptr;
if (cache)
{
cached_value = &(cache[region_index]);
if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID)
return *cached_value * REGION_CACHE_ITEM_DIVISOR;
}
const VarRegionAxis *axes = axesZ.arrayZ + (region_index * axisCount);
float v = 1.f;
float v = 1.;
unsigned int count = axisCount;
for (unsigned int i = 0; i < count; i++)
{
@ -2581,15 +2643,32 @@ struct VarRegionList
float factor = axes[i].evaluate (coord);
if (factor == 0.f)
{
if (cache)
*cached_value = 0.;
return 0.;
v = 0.f;
break;
}
v *= factor;
}
return v;
}
public:
HB_ALWAYS_INLINE
float evaluate (unsigned int region_index,
const int *coords, unsigned int coord_len,
hb_scalar_cache_t *cache = nullptr) const
{
if (unlikely (region_index >= regionCount))
return 0.;
float v;
if (cache && cache->get (region_index, &v))
return v;
v = evaluate_impl (region_index, coords, coord_len);
if (cache)
*cached_value = v * REGION_CACHE_ITEM_MULTIPLIER;
cache->set (region_index, v);
return v;
}
@ -2732,29 +2811,24 @@ struct SparseVariationRegion : Array16Of<SparseVarRegionAxis>
struct SparseVarRegionList
{
using cache_t = hb_atomic_t<int>;
HB_ALWAYS_INLINE
float evaluate (unsigned int region_index,
const int *coords, unsigned int coord_len,
cache_t *cache = nullptr) const
hb_scalar_cache_t *cache = nullptr) const
{
if (unlikely (region_index >= regions.len))
return 0.;
cache_t *cached_value = nullptr;
if (cache)
{
cached_value = &(cache[region_index]);
if (*cached_value != REGION_CACHE_ITEM_CACHE_INVALID)
return *cached_value * REGION_CACHE_ITEM_DIVISOR;
}
float v;
if (cache && cache->get (region_index, &v))
return v;
const SparseVariationRegion &region = this+regions[region_index];
float v = region.evaluate (coords, coord_len);
v = region.evaluate (coords, coord_len);
if (cache)
*cached_value = v * REGION_CACHE_ITEM_MULTIPLIER;
cache->set (region_index, v);
return v;
}
@ -2792,46 +2866,62 @@ struct VarData
+ itemCount * get_row_size ();
}
float get_delta (unsigned int inner,
const int *coords, unsigned int coord_count,
const VarRegionList &regions,
VarRegionList::cache_t *cache = nullptr) const
float _get_delta (unsigned int inner,
const int *coords, unsigned int coord_count,
const VarRegionList &regions,
hb_scalar_cache_t *cache = nullptr) const
{
if (unlikely (inner >= itemCount))
return 0.;
bool is_long = longWords ();
unsigned int count = regionIndices.len;
unsigned word_count = wordCount ();
unsigned int scount = is_long ? count : word_count;
unsigned int lcount = is_long ? word_count : 0;
unsigned int count = regionIndices.len;
bool is_long = longWords ();
unsigned word_count = wordCount ();
unsigned int scount = is_long ? count : word_count;
unsigned int lcount = is_long ? word_count : 0;
const HBUINT8 *bytes = get_delta_bytes ();
const HBUINT8 *row = bytes + inner * get_row_size ();
const HBUINT8 *bytes = get_delta_bytes ();
const HBUINT8 *row = bytes + inner * get_row_size ();
float delta = 0.;
unsigned int i = 0;
float delta = 0.;
unsigned int i = 0;
const HBINT32 *lcursor = reinterpret_cast<const HBINT32 *> (row);
for (; i < lcount; i++)
{
float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
if (scalar)
delta += scalar * *lcursor;
lcursor++;
}
const HBINT16 *scursor = reinterpret_cast<const HBINT16 *> (lcursor);
for (; i < scount; i++)
{
float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
if (scalar)
delta += scalar * *scursor;
scursor++;
}
const HBINT8 *bcursor = reinterpret_cast<const HBINT8 *> (scursor);
for (; i < count; i++)
{
float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
if (scalar)
delta += scalar * *bcursor;
bcursor++;
}
const HBINT32 *lcursor = reinterpret_cast<const HBINT32 *> (row);
for (; i < lcount; i++)
{
float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
delta += scalar * *lcursor++;
}
const HBINT16 *scursor = reinterpret_cast<const HBINT16 *> (lcursor);
for (; i < scount; i++)
{
float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
delta += scalar * *scursor++;
}
const HBINT8 *bcursor = reinterpret_cast<const HBINT8 *> (scursor);
for (; i < count; i++)
{
float scalar = regions.evaluate (regionIndices.arrayZ[i], coords, coord_count, cache);
delta += scalar * *bcursor++;
}
return delta;
}
return delta;
HB_ALWAYS_INLINE
float get_delta (unsigned int inner,
const int *coords, unsigned int coord_count,
const VarRegionList &regions,
hb_scalar_cache_t *cache = nullptr) const
{
unsigned int count = regionIndices.len;
if (!count) return 0.f; // This is quite common, so optimize it.
return _get_delta (inner, coords, coord_count, regions, cache);
}
void get_region_scalars (const int *coords, unsigned int coord_count,
@ -2869,7 +2959,7 @@ struct VarData
return false;
}
if (unlikely (!c->extend_min (this))) return_trace (false);
if (unlikely (!c->extend_min (this))) return_trace (false);
itemCount = row_count;
int min_threshold = has_long ? -65536 : -128;
@ -3150,7 +3240,7 @@ struct MultiVarData
const int *coords, unsigned int coord_count,
const SparseVarRegionList &regions,
hb_array_t<float> out,
SparseVarRegionList::cache_t *cache = nullptr) const
hb_scalar_cache_t *cache = nullptr) const
{
auto &deltaSets = StructAfter<decltype (deltaSetsX)> (regionIndices);
@ -3187,31 +3277,24 @@ struct MultiVarData
struct ItemVariationStore
{
friend struct item_variations_t;
using cache_t = VarRegionList::cache_t;
cache_t *create_cache () const
hb_scalar_cache_t *create_cache () const
{
#ifdef HB_NO_VAR
return nullptr;
return hb_scalar_cache_t::create (0);
#endif
unsigned count = (this+regions).regionCount;
if (!count) return nullptr;
cache_t *cache = (cache_t *) hb_malloc (sizeof (float) * count);
if (unlikely (!cache)) return nullptr;
for (unsigned i = 0; i < count; i++)
cache[i] = REGION_CACHE_ITEM_CACHE_INVALID;
return cache;
return hb_scalar_cache_t::create ((this+regions).regionCount);
}
static void destroy_cache (cache_t *cache) { hb_free (cache); }
static void destroy_cache (hb_scalar_cache_t *cache)
{
hb_scalar_cache_t::destroy (cache);
}
private:
float get_delta (unsigned int outer, unsigned int inner,
const int *coords, unsigned int coord_count,
VarRegionList::cache_t *cache = nullptr) const
hb_scalar_cache_t *cache = nullptr) const
{
#ifdef HB_NO_VAR
return 0.f;
@ -3229,7 +3312,7 @@ struct ItemVariationStore
public:
float get_delta (unsigned int index,
const int *coords, unsigned int coord_count,
VarRegionList::cache_t *cache = nullptr) const
hb_scalar_cache_t *cache = nullptr) const
{
unsigned int outer = index >> 16;
unsigned int inner = index & 0xFFFF;
@ -3237,7 +3320,7 @@ struct ItemVariationStore
}
float get_delta (unsigned int index,
hb_array_t<const int> coords,
VarRegionList::cache_t *cache = nullptr) const
hb_scalar_cache_t *cache = nullptr) const
{
return get_delta (index,
coords.arrayZ, coords.length,
@ -3445,43 +3528,28 @@ struct ItemVariationStore
struct MultiItemVariationStore
{
using cache_t = SparseVarRegionList::cache_t;
cache_t *create_cache (hb_array_t<cache_t> static_cache = hb_array_t<cache_t> ()) const
hb_scalar_cache_t *create_cache (hb_scalar_cache_t *static_cache = nullptr) const
{
#ifdef HB_NO_VAR
return nullptr;
return hb_scalar_cache_t::create (0);
#endif
auto &r = this+regions;
unsigned count = r.regions.len;
cache_t *cache;
if (count <= static_cache.length)
cache = static_cache.arrayZ;
else
{
cache = (cache_t *) hb_malloc (sizeof (float) * count);
if (unlikely (!cache)) return nullptr;
}
for (unsigned i = 0; i < count; i++)
cache[i] = REGION_CACHE_ITEM_CACHE_INVALID;
return cache;
return hb_scalar_cache_t::create (count, static_cache);
}
static void destroy_cache (cache_t *cache,
hb_array_t<cache_t> static_cache = hb_array_t<cache_t> ())
static void destroy_cache (hb_scalar_cache_t *cache,
hb_scalar_cache_t *static_cache = nullptr)
{
if (cache != static_cache.arrayZ)
hb_free (cache);
hb_scalar_cache_t::destroy (cache, static_cache);
}
private:
void get_delta (unsigned int outer, unsigned int inner,
const int *coords, unsigned int coord_count,
hb_array_t<float> out,
VarRegionList::cache_t *cache = nullptr) const
hb_scalar_cache_t *cache = nullptr) const
{
#ifdef HB_NO_VAR
return;
@ -3501,7 +3569,7 @@ struct MultiItemVariationStore
void get_delta (unsigned int index,
const int *coords, unsigned int coord_count,
hb_array_t<float> out,
VarRegionList::cache_t *cache = nullptr) const
hb_scalar_cache_t *cache = nullptr) const
{
unsigned int outer = index >> 16;
unsigned int inner = index & 0xFFFF;
@ -3510,7 +3578,7 @@ struct MultiItemVariationStore
void get_delta (unsigned int index,
hb_array_t<const int> coords,
hb_array_t<float> out,
VarRegionList::cache_t *cache = nullptr) const
hb_scalar_cache_t *cache = nullptr) const
{
return get_delta (index,
coords.arrayZ, coords.length,
@ -3540,8 +3608,6 @@ struct MultiItemVariationStore
DEFINE_SIZE_ARRAY_SIZED (8, dataSets);
};
#undef REGION_CACHE_ITEM_CACHE_INVALID
template <typename MapCountT>
struct DeltaSetIndexMapFormat01
{
@ -3592,13 +3658,19 @@ struct DeltaSetIndexMapFormat01
return_trace (true);
}
HB_ALWAYS_INLINE
uint32_t map (unsigned int v) const /* Returns 16.16 outer.inner. */
{
/* If count is zero, pass value unchanged. This takes
* care of direct mapping for advance map. */
if (!mapCount)
return v;
return _map (v);
}
HB_HOT
uint32_t _map (unsigned int v) const /* Returns 16.16 outer.inner. */
{
if (v >= mapCount)
v = mapCount - 1;
@ -3736,7 +3808,7 @@ struct ItemVarStoreInstancer
ItemVarStoreInstancer (const ItemVariationStore *varStore_,
const DeltaSetIndexMap *varIdxMap,
hb_array_t<const int> coords,
VarRegionList::cache_t *cache = nullptr) :
hb_scalar_cache_t *cache = nullptr) :
varStore (varStore_), varIdxMap (varIdxMap), coords (coords), cache (cache)
{
if (!varStore)
@ -3762,7 +3834,7 @@ struct ItemVarStoreInstancer
const ItemVariationStore *varStore;
const DeltaSetIndexMap *varIdxMap;
hb_array_t<const int> coords;
VarRegionList::cache_t *cache;
hb_scalar_cache_t *cache;
};
struct MultiItemVarStoreInstancer
@ -3770,7 +3842,7 @@ struct MultiItemVarStoreInstancer
MultiItemVarStoreInstancer (const MultiItemVariationStore *varStore,
const DeltaSetIndexMap *varIdxMap,
hb_array_t<const int> coords,
SparseVarRegionList::cache_t *cache = nullptr) :
hb_scalar_cache_t *cache = nullptr) :
varStore (varStore), varIdxMap (varIdxMap), coords (coords), cache (cache)
{
if (!varStore)
@ -3803,7 +3875,7 @@ struct MultiItemVarStoreInstancer
const MultiItemVariationStore *varStore;
const DeltaSetIndexMap *varIdxMap;
hb_array_t<const int> coords;
SparseVarRegionList::cache_t *cache;
hb_scalar_cache_t *cache;
};
@ -4783,13 +4855,13 @@ struct VariationDevice
hb_position_t get_x_delta (hb_font_t *font,
const ItemVariationStore &store,
ItemVariationStore::cache_t *store_cache = nullptr) const
{ return !font->num_coords ? 0 : font->em_scalef_x (get_delta (font, store, store_cache)); }
hb_scalar_cache_t *store_cache = nullptr) const
{ return !font->has_nonzero_coords ? 0 : font->em_scalef_x (get_delta (font, store, store_cache)); }
hb_position_t get_y_delta (hb_font_t *font,
const ItemVariationStore &store,
ItemVariationStore::cache_t *store_cache = nullptr) const
{ return !font->num_coords ? 0 : font->em_scalef_y (get_delta (font, store, store_cache)); }
hb_scalar_cache_t *store_cache = nullptr) const
{ return !font->has_nonzero_coords ? 0 : font->em_scalef_y (get_delta (font, store, store_cache)); }
VariationDevice* copy (hb_serialize_context_t *c,
const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map) const
@ -4823,9 +4895,9 @@ struct VariationDevice
float get_delta (hb_font_t *font,
const ItemVariationStore &store,
ItemVariationStore::cache_t *store_cache = nullptr) const
hb_scalar_cache_t *store_cache = nullptr) const
{
return store.get_delta (varIdx, font->coords, font->num_coords, (ItemVariationStore::cache_t *) store_cache);
return store.get_delta (varIdx, font->coords, font->num_coords, store_cache);
}
protected:
@ -4850,7 +4922,7 @@ struct Device
{
hb_position_t get_x_delta (hb_font_t *font,
const ItemVariationStore &store=Null (ItemVariationStore),
ItemVariationStore::cache_t *store_cache = nullptr) const
hb_scalar_cache_t *store_cache = nullptr) const
{
switch (u.b.format)
{
@ -4868,7 +4940,7 @@ struct Device
}
hb_position_t get_y_delta (hb_font_t *font,
const ItemVariationStore &store=Null (ItemVariationStore),
ItemVariationStore::cache_t *store_cache = nullptr) const
hb_scalar_cache_t *store_cache = nullptr) const
{
switch (u.b.format)
{
@ -4954,6 +5026,18 @@ struct Device
}
}
bool is_variation_device () const
{
switch (u.b.format) {
#ifndef HB_NO_VAR
case 0x8000:
return true;
#endif
default:
return false;
}
}
protected:
union {
DeviceHeader b;

View file

@ -700,7 +700,7 @@ struct hb_ot_apply_context_t :
const GDEF::accelerator_t &gdef_accel;
const hb_ot_layout_lookup_accelerator_t *lookup_accel = nullptr;
const ItemVariationStore &var_store;
ItemVariationStore::cache_t *var_store_cache;
hb_scalar_cache_t *var_store_cache;
hb_set_digest_t digest;
hb_direction_t direction;
@ -723,7 +723,7 @@ struct hb_ot_apply_context_t :
hb_font_t *font_,
hb_buffer_t *buffer_,
hb_blob_t *table_blob_,
ItemVariationStore::cache_t *var_store_cache_ = nullptr) :
hb_scalar_cache_t *var_store_cache_ = nullptr) :
table_index (table_index_),
font (font_), face (font->face), buffer (buffer_),
sanitizer (table_blob_),

View file

@ -343,7 +343,7 @@ hb_ot_layout_get_glyphs_in_class (hb_face_t *face,
* @face: The #hb_face_t to work on
* @glyph: The #hb_codepoint_t code point to query
* @start_offset: offset of the first attachment point to retrieve
* @point_count: (inout) (optional): Input = the maximum number of attachment points to return;
* @point_count: (inout) (nullable): Input = the maximum number of attachment points to return;
* Output = the actual number of attachment points returned (may be zero)
* @point_array: (out) (array length=point_count): The array of attachment points found for the query
*
@ -373,7 +373,7 @@ hb_ot_layout_get_attach_points (hb_face_t *face,
* @direction: The #hb_direction_t text direction to use
* @glyph: The #hb_codepoint_t code point to query
* @start_offset: offset of the first caret position to retrieve
* @caret_count: (inout) (optional): Input = the maximum number of caret positions to return;
* @caret_count: (inout) (nullable): Input = the maximum number of caret positions to return;
* Output = the actual number of caret positions returned (may be zero)
* @caret_array: (out) (array length=caret_count): The array of caret positions found for the query
*
@ -444,7 +444,7 @@ get_gsubgpos_table (hb_face_t *face,
* @face: #hb_face_t to work upon
* @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
* @start_offset: offset of the first script tag to retrieve
* @script_count: (inout) (optional): Input = the maximum number of script tags to return;
* @script_count: (inout) (nullable): Input = the maximum number of script tags to return;
* Output = the actual number of script tags returned (may be zero)
* @script_tags: (out) (array length=script_count): The array of #hb_tag_t script tags found for the query
*
@ -541,8 +541,8 @@ hb_ot_layout_table_choose_script (hb_face_t *face,
* @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
* @script_count: Number of script tags in the array
* @script_tags: Array of #hb_tag_t script tags
* @script_index: (out) (optional): The index of the requested script
* @chosen_script: (out) (optional): #hb_tag_t of the requested script
* @script_index: (out) (nullable): The index of the requested script
* @chosen_script: (out) (nullable): #hb_tag_t of the requested script
*
* Selects an OpenType script for @table_tag from the @script_tags array.
*
@ -613,7 +613,7 @@ hb_ot_layout_table_select_script (hb_face_t *face,
* @face: #hb_face_t to work upon
* @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
* @start_offset: offset of the first feature tag to retrieve
* @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
* @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return;
* Output = the actual number of feature tags returned (may be zero)
* @feature_tags: (out) (array length=feature_count): Array of feature tags found in the table
*
@ -683,7 +683,7 @@ hb_ot_layout_table_find_feature (hb_face_t *face,
* @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
* @script_index: The index of the requested script tag
* @start_offset: offset of the first language tag to retrieve
* @language_count: (inout) (optional): Input = the maximum number of language tags to return;
* @language_count: (inout) (nullable): Input = the maximum number of language tags to return;
* Output = the actual number of language tags returned (may be zero)
* @language_tags: (out) (array length=language_count): Array of language tags found in the table
*
@ -911,7 +911,7 @@ hb_ot_layout_language_get_required_feature (hb_face_t *face,
* @script_index: The index of the requested script tag
* @language_index: The index of the requested language tag
* @start_offset: offset of the first feature tag to retrieve
* @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
* @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return;
* Output: the actual number of feature tags returned (may be zero)
* @feature_indexes: (out) (array length=feature_count): The array of feature indexes found for the query
*
@ -947,7 +947,7 @@ hb_ot_layout_language_get_feature_indexes (hb_face_t *face,
* @script_index: The index of the requested script tag
* @language_index: The index of the requested language tag
* @start_offset: offset of the first feature tag to retrieve
* @feature_count: (inout) (optional): Input = the maximum number of feature tags to return;
* @feature_count: (inout) (nullable): Input = the maximum number of feature tags to return;
* Output = the actual number of feature tags returned (may be zero)
* @feature_tags: (out) (array length=feature_count): The array of #hb_tag_t feature tags found for the query
*
@ -1035,7 +1035,7 @@ hb_ot_layout_language_find_feature (hb_face_t *face,
* @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
* @feature_index: The index of the requested feature
* @start_offset: offset of the first lookup to retrieve
* @lookup_count: (inout) (optional): Input = the maximum number of lookups to return;
* @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return;
* Output = the actual number of lookups returned (may be zero)
* @lookup_indexes: (out) (array length=lookup_count): The array of lookup indexes found for the query
*
@ -1386,10 +1386,10 @@ hb_ot_layout_collect_lookups (hb_face_t *face,
* @face: #hb_face_t to work upon
* @table_tag: #HB_OT_TAG_GSUB or #HB_OT_TAG_GPOS
* @lookup_index: The index of the feature lookup to query
* @glyphs_before: (out): Array of glyphs preceding the substitution range
* @glyphs_input: (out): Array of input glyphs that would be substituted by the lookup
* @glyphs_after: (out): Array of glyphs following the substitution range
* @glyphs_output: (out): Array of glyphs that would be the substituted output of the lookup
* @glyphs_before: (out) (nullable): Array of glyphs preceding the substitution range
* @glyphs_input: (out) (nullable): Array of input glyphs that would be substituted by the lookup
* @glyphs_after: (out) (nullable): Array of glyphs following the substitution range
* @glyphs_output: (out) (nullable): Array of glyphs that would be the substituted output of the lookup
*
* Fetches a list of all glyphs affected by the specified lookup in the
* specified face's GSUB table or GPOS table.
@ -1473,7 +1473,7 @@ hb_ot_layout_table_find_feature_variations (hb_face_t *face,
* @feature_index: The index of the feature to query
* @variations_index: The index of the feature variation to query
* @start_offset: offset of the first lookup to retrieve
* @lookup_count: (inout) (optional): Input = the maximum number of lookups to return;
* @lookup_count: (inout) (nullable): Input = the maximum number of lookups to return;
* Output = the actual number of lookups returned (may be zero)
* @lookup_indexes: (out) (array length=lookup_count): The array of lookups found for the query
*
@ -1777,15 +1777,15 @@ hb_ot_layout_get_size_params (hb_face_t *face,
* @face: #hb_face_t to work upon
* @table_tag: table tag to query, "GSUB" or "GPOS".
* @feature_index: index of feature to query.
* @label_id: (out) (optional): The name table name ID that specifies a string
* for a user-interface label for this feature. (May be NULL.)
* @tooltip_id: (out) (optional): The name table name ID that specifies a string
* @label_id: (out) (nullable): The name table name ID that specifies a string
* for a user-interface label for this feature.
* @tooltip_id: (out) (nullable): The name table name ID that specifies a string
* that an application can use for tooltip text for this
* feature. (May be NULL.)
* @sample_id: (out) (optional): The name table name ID that specifies sample text
* that illustrates the effect of this feature. (May be NULL.)
* @num_named_parameters: (out) (optional): Number of named parameters. (May be zero.)
* @first_param_id: (out) (optional): The first name table name ID used to specify
* feature.
* @sample_id: (out) (nullable): The name table name ID that specifies sample text
* that illustrates the effect of this feature.
* @num_named_parameters: (out) (nullable): Number of named parameters.
* @first_param_id: (out) (nullable): The first name table name ID used to specify
* strings for user-interface labels for the feature
* parameters. (Must be zero if numParameters is zero.)
*
@ -1852,7 +1852,7 @@ hb_ot_layout_feature_get_name_ids (hb_face_t *face,
* @table_tag: table tag to query, "GSUB" or "GPOS".
* @feature_index: index of feature to query.
* @start_offset: offset of the first character to retrieve
* @char_count: (inout) (optional): Input = the maximum number of characters to return;
* @char_count: (inout) (nullable): Input = the maximum number of characters to return;
* Output = the actual number of characters returned (may be zero)
* @characters: (out caller-allocates) (array length=char_count): A buffer pointer.
* The Unicode codepoints of the characters for which this feature provides
@ -2017,7 +2017,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
unsigned int i = 0;
auto *font_data = font->data.ot.get ();
auto *var_store_cache = font_data == HB_SHAPER_DATA_SUCCEEDED ? nullptr : (OT::ItemVariationStore::cache_t *) font_data;
auto *var_store_cache = (OT::hb_scalar_cache_t *) font_data;
OT::hb_ot_apply_context_t c (table_index, font, buffer, proxy.accel.get_blob (), var_store_cache);
c.set_recurse_func (Proxy::Lookup::template dispatch_recurse_func<OT::hb_ot_apply_context_t>);
@ -2627,7 +2627,7 @@ struct hb_get_glyph_alternates_dispatch_t :
* @lookup_index: index of the feature lookup to query.
* @glyph: a glyph id.
* @start_offset: starting offset.
* @alternate_count: (inout) (optional): Input = the maximum number of alternate glyphs to return;
* @alternate_count: (inout) (nullable): Input = the maximum number of alternate glyphs to return;
* Output = the actual number of alternate glyphs returned (may be zero).
* @alternate_glyphs: (out caller-allocates) (array length=alternate_count): A glyphs buffer.
* Alternate glyphs associated with the glyph id.

View file

@ -44,6 +44,7 @@
#include "hb-ot-face.hh"
#include "hb-set.hh"
#include "hb-unicode.hh"
#include "hb-aat-layout.hh"
#include "hb-ot-layout-gdef-table.hh"
@ -425,25 +426,20 @@ _hb_ot_shaper_face_data_destroy (hb_ot_face_data_t *data)
*/
struct hb_ot_font_data_t {
OT::ItemVariationStore::cache_t unused; // Just for alignment
OT::hb_scalar_cache_t unused; // Just for alignment
};
hb_ot_font_data_t *
_hb_ot_shaper_font_data_create (hb_font_t *font)
{
if (!font->num_coords)
return (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
const OT::ItemVariationStore &var_store = font->face->table.GDEF->table->get_var_store ();
auto *cache = (hb_ot_font_data_t *) var_store.create_cache ();
return cache ? cache : (hb_ot_font_data_t *) HB_SHAPER_DATA_SUCCEEDED;
return (hb_ot_font_data_t *) var_store.create_cache ();
}
void
_hb_ot_shaper_font_data_destroy (hb_ot_font_data_t *data)
{
if (data == HB_SHAPER_DATA_SUCCEEDED) return;
OT::ItemVariationStore::destroy_cache ((OT::ItemVariationStore::cache_t *) data);
OT::ItemVariationStore::destroy_cache ((OT::hb_scalar_cache_t *) data);
}
@ -651,59 +647,6 @@ hb_ensure_native_direction (hb_buffer_t *buffer)
* Substitute
*/
#ifndef HB_NO_VERTICAL
static hb_codepoint_t
hb_vert_char_for (hb_codepoint_t u)
{
switch (u >> 8)
{
case 0x20: switch (u) {
case 0x2013u: return 0xfe32u; // EN DASH
case 0x2014u: return 0xfe31u; // EM DASH
case 0x2025u: return 0xfe30u; // TWO DOT LEADER
case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS
} break;
case 0x30: switch (u) {
case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA
case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP
case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET
case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET
case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET
case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET
case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET
case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET
case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET
case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET
case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET
case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET
case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET
case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET
case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET
case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET
} break;
case 0xfe: switch (u) {
case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE
} break;
case 0xff: switch (u) {
case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK
case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS
case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS
case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA
case 0xff1au: return 0xfe13u; // FULLWIDTH COLON
case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON
case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK
case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET
case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET
case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE
case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET
case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET
} break;
}
return u;
}
#endif
static inline void
hb_ot_rotate_chars (const hb_ot_shape_context_t *c)
{
@ -729,7 +672,7 @@ hb_ot_rotate_chars (const hb_ot_shape_context_t *c)
if (HB_DIRECTION_IS_VERTICAL (c->target_direction) && !c->plan->has_vert)
{
for (unsigned int i = 0; i < count; i++) {
hb_codepoint_t codepoint = hb_vert_char_for (info[i].codepoint);
hb_codepoint_t codepoint = hb_unicode_funcs_t::vertical_char_for (info[i].codepoint);
if (unlikely (codepoint != info[i].codepoint && c->font->has_glyph (codepoint)))
info[i].codepoint = codepoint;
}
@ -1054,23 +997,16 @@ hb_ot_position_default (const hb_ot_shape_context_t *c)
{
c->font->get_glyph_h_advances (count, &info[0].codepoint, sizeof(info[0]),
&pos[0].x_advance, sizeof(pos[0]));
/* The nil glyph_h_origin() func returns 0, so no need to apply it. */
if (c->font->has_glyph_h_origin_func ())
for (unsigned int i = 0; i < count; i++)
c->font->subtract_glyph_h_origin (info[i].codepoint,
&pos[i].x_offset,
&pos[i].y_offset);
// h_origin defaults to zero; only apply it if the font has it.
if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ())
c->font->subtract_glyph_h_origins (c->buffer);
}
else
{
c->font->get_glyph_v_advances (count, &info[0].codepoint, sizeof(info[0]),
&pos[0].y_advance, sizeof(pos[0]));
for (unsigned int i = 0; i < count; i++)
{
c->font->subtract_glyph_v_origin (info[i].codepoint,
&pos[i].x_offset,
&pos[i].y_offset);
}
// v_origin defaults to non-zero; apply even if only fallback is there.
c->font->subtract_glyph_v_origins (c->buffer);
}
if (c->buffer->scratch_flags & HB_BUFFER_SCRATCH_FLAG_HAS_SPACE_FALLBACK)
_hb_ot_shape_fallback_spaces (c->plan, c->font, c->buffer);
@ -1079,10 +1015,6 @@ hb_ot_position_default (const hb_ot_shape_context_t *c)
static inline void
hb_ot_position_plan (const hb_ot_shape_context_t *c)
{
unsigned int count = c->buffer->len;
hb_glyph_info_t *info = c->buffer->info;
hb_glyph_position_t *pos = c->buffer->pos;
/* If the font has no GPOS and direction is forward, then when
* zeroing mark widths, we shift the mark with it, such that the
* mark is positioned hanging over the previous glyph. When
@ -1097,12 +1029,9 @@ hb_ot_position_plan (const hb_ot_shape_context_t *c)
/* We change glyph origin to what GPOS expects (horizontal), apply GPOS, change it back. */
/* The nil glyph_h_origin() func returns 0, so no need to apply it. */
if (c->font->has_glyph_h_origin_func ())
for (unsigned int i = 0; i < count; i++)
c->font->add_glyph_h_origin (info[i].codepoint,
&pos[i].x_offset,
&pos[i].y_offset);
// h_origin defaults to zero; only apply it if the font has it.
if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ())
c->font->add_glyph_h_origins (c->buffer);
hb_ot_layout_position_start (c->font, c->buffer);
@ -1139,12 +1068,9 @@ hb_ot_position_plan (const hb_ot_shape_context_t *c)
hb_ot_zero_width_default_ignorables (c->buffer);
hb_ot_layout_position_finish_offsets (c->font, c->buffer);
/* The nil glyph_h_origin() func returns 0, so no need to apply it. */
if (c->font->has_glyph_h_origin_func ())
for (unsigned int i = 0; i < count; i++)
c->font->subtract_glyph_h_origin (info[i].codepoint,
&pos[i].x_offset,
&pos[i].y_offset);
// h_origin defaults to zero; only apply it if the font has it.
if (c->font->has_glyph_h_origin_func () || c->font->has_glyph_h_origins_func ())
c->font->subtract_glyph_h_origins (c->buffer);
if (c->plan->fallback_mark_positioning)
_hb_ot_shape_fallback_mark_position (c->plan, c->font, c->buffer,

View file

@ -654,7 +654,7 @@ postprocess_glyphs_arabic (const hb_ot_shape_plan_t *plan,
/* https://www.unicode.org/reports/tr53/ */
static hb_codepoint_t
static const hb_codepoint_t
modifier_combining_marks[] =
{
0x0654u, /* ARABIC HAMZA ABOVE */

View file

@ -163,7 +163,7 @@ thai_pua_shape (hb_codepoint_t u, thai_action_t action, hb_font_t *font)
}
static enum thai_above_state_t
static const enum thai_above_state_t
{ /* Cluster above looks like: */
T0, /* ⣤ */
T1, /* ⣼ */

View file

@ -101,8 +101,9 @@
#ifndef HB_OPTIMIZE_SIZE
static const uint8_t
hb_use_u8[3345] =
#include <stdint.h>
static const uint8_t hb_use_u8[3345]=
{
16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 57, 58, 59, 195, 211, 62,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
@ -315,8 +316,7 @@ hb_use_u8[3345] =
J, HR, G, G, HM, HM, HM, G, O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O,
VBlw,
};
static const uint16_t
hb_use_u16[856] =
static const uint16_t hb_use_u16[856]=
{
0, 0, 1, 2, 0, 3, 0, 3, 0, 0, 4, 5, 0, 6, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0,
@ -374,22 +374,21 @@ hb_use_u16[856] =
163,163,163,163,163,163,163,130,
};
static inline unsigned
hb_use_b4 (const uint8_t* a, unsigned i)
static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i)
{
return (a[i>>1]>>((i&1u)<<2))&15u;
return (a[i>>1]>>((i&1)<<2))&15;
}
static inline uint_fast8_t
hb_use_get_category (unsigned u)
static inline uint8_t hb_use_get_category (unsigned u)
{
return u<921600u?hb_use_u8[2953+(((hb_use_u8[625+(((hb_use_u16[((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31u))])<<3)+((u>>1>>3)&7u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O;
return u<921600 ? hb_use_u8[2953u+(((hb_use_u8[625u+(((hb_use_u16[((hb_use_u8[113u+(((hb_use_b4(hb_use_u8,u>>1>>3>>3>>5))<<5)+((u>>1>>3>>3)&31))])<<3)+((u>>1>>3)&7)])<<3)+((u>>1)&7))])<<1)+((u)&1))] : O;
}
#else
static const uint8_t
hb_use_u8[3657] =
#include <stdint.h>
static const uint8_t hb_use_u8[3657]=
{
16, 50, 51, 51, 51, 52, 51, 83, 118, 131, 57, 58, 59, 195, 211, 62,
51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
@ -621,8 +620,7 @@ hb_use_u8[3657] =
VMPst, G, G, J, J, J, SB, SE, J, HR, G, G, HM, HM, HM, G,
O, MPre, MPre, MPst,VMAbv, MBlw, VBlw, O, VBlw,
};
static const uint16_t
hb_use_u16[486] =
static const uint16_t hb_use_u16[486]=
{
0, 0, 1, 2, 0, 3, 4, 5, 0, 6, 7, 0, 8, 0, 9, 10,
11, 12, 10, 13, 14, 10, 10, 15, 16, 17, 18, 19, 20, 21, 22, 23,
@ -657,15 +655,13 @@ hb_use_u16[486] =
130,130,163,163,163,130,
};
static inline unsigned
hb_use_b4 (const uint8_t* a, unsigned i)
static inline uint8_t hb_use_b4 (const uint8_t* a, unsigned i)
{
return (a[i>>1]>>((i&1u)<<2))&15u;
return (a[i>>1]>>((i&1)<<2))&15;
}
static inline uint_fast8_t
hb_use_get_category (unsigned u)
static inline uint8_t hb_use_get_category (unsigned u)
{
return u<921600u?hb_use_u8[3265+(((hb_use_u8[937+(((hb_use_u16[((hb_use_u8[369+(((hb_use_u8[113+(((hb_use_b4(hb_use_u8,u>>1>>3>>1>>3>>4))<<4)+((u>>1>>3>>1>>3)&15u))])<<3)+((u>>1>>3>>1)&7u))])<<1)+((u>>1>>3)&1u)])<<3)+((u>>1)&7u))])<<1)+((u)&1u))]:O;
return u<921600 ? hb_use_u8[3265u+(((hb_use_u8[937u+(((hb_use_u16[((hb_use_u8[369u+(((hb_use_u8[113u+(((hb_use_b4(hb_use_u8,u>>1>>3>>1>>3>>4))<<4)+((u>>1>>3>>1>>3)&15))])<<3)+((u>>1>>3>>1)&7))])<<1)+((u>>1>>3)&1)])<<3)+((u>>1)&7))])<<1)+((u)&1))] : O;
}
#endif

View file

@ -6,8 +6,8 @@
*
* on files with these headers:
*
* <meta name="updated_at" content="2024-12-06 06:35 AM" />
* File-Date: 2025-01-21
* <meta name="updated_at" content="2024-12-06T06:35:00Z" />
* File-Date: 2025-03-10
*/
#ifndef HB_OT_TAG_TABLE_HH

View file

@ -143,10 +143,13 @@ struct AxisValueMap
struct SegmentMaps : Array16Of<AxisValueMap>
{
int map (int value, unsigned int from_offset = 0, unsigned int to_offset = 1) const
float map_float (float value, unsigned int from_offset = 0, unsigned int to_offset = 1) const
{
#define fromCoord coords[from_offset].to_int ()
#define toCoord coords[to_offset].to_int ()
#define fromCoord coords[from_offset].to_float ()
#define toCoord coords[to_offset].to_float ()
const auto *map = arrayZ;
/* The following special-cases are not part of OpenType, which requires
* that at least -1, 0, and +1 must be mapped. But we include these as
* part of a better error recovery scheme. */
@ -155,47 +158,98 @@ struct SegmentMaps : Array16Of<AxisValueMap>
if (!len)
return value;
else /* len == 1*/
return value - arrayZ[0].fromCoord + arrayZ[0].toCoord;
return value - map[0].fromCoord + map[0].toCoord;
}
if (value <= arrayZ[0].fromCoord)
return value - arrayZ[0].fromCoord + arrayZ[0].toCoord;
// At least two mappings now.
unsigned int i;
unsigned int count = len - 1;
for (i = 1; i < count && value > arrayZ[i].fromCoord; i++)
;
/* CoreText is wild...
* PingFangUI avar needs all this special-casing...
* So we implement an extended version of the spec here,
* which is more robust and more likely to be compatible with
* the wild. */
if (value >= arrayZ[i].fromCoord)
return value - arrayZ[i].fromCoord + arrayZ[i].toCoord;
unsigned start = 0;
unsigned end = len;
if (map[start].fromCoord == -1 && map[start].toCoord == -1 && map[start+1].fromCoord == -1)
start++;
if (map[end-1].fromCoord == +1 && map[end-1].toCoord == +1 && map[end-2].fromCoord == +1)
end--;
if (unlikely (arrayZ[i-1].fromCoord == arrayZ[i].fromCoord))
return arrayZ[i-1].toCoord;
/* Look for exact match first, and do lots of special-casing. */
unsigned i;
for (i = start; i < end; i++)
if (value == map[i].fromCoord)
break;
if (i < end)
{
// There's at least one exact match. See if there are more.
unsigned j = i;
for (; j + 1 < end; j++)
if (value != map[j + 1].fromCoord)
break;
// [i,j] inclusive are all exact matches:
// If there's only one, return it. This is the only spec-compliant case.
if (i == j)
return map[i].toCoord;
// If there's exactly three, return the middle one.
if (i + 2 == j)
return map[i + 1].toCoord;
// Ignore the middle ones. Return the one mapping closer to 0.
if (value < 0) return map[j].toCoord;
if (value > 0) return map[i].toCoord;
// Mapping 0? CoreText seems confused. It seems to prefer 0 here...
// So we'll just return the smallest one. lol
return fabsf (map[i].toCoord) < fabsf (map[j].toCoord) ? map[i].toCoord : map[j].toCoord;
// Mapping 0? Return one not mapping to 0.
if (map[i].toCoord == 0)
return map[j].toCoord;
else
return map[i].toCoord;
}
/* There's at least two and we're not an exact match. Prepare to lerp. */
// Find the segment we're in.
for (i = start; i < end; i++)
if (value < map[i].fromCoord)
break;
if (i == 0)
{
// Value before all segments; Shift.
return value - map[0].fromCoord + map[0].toCoord;
}
if (i == end)
{
// Value after all segments; Shift.
return value - map[end - 1].fromCoord + map[end - 1].toCoord;
}
// Actually interpolate.
auto &before = map[i-1];
auto &after = map[i];
float denom = after.fromCoord - before.fromCoord; // Can't be zero by now.
return before.toCoord + ((after.toCoord - before.toCoord) * (value - before.fromCoord)) / denom;
int denom = arrayZ[i].fromCoord - arrayZ[i-1].fromCoord;
return roundf (arrayZ[i-1].toCoord + ((float) (arrayZ[i].toCoord - arrayZ[i-1].toCoord) *
(value - arrayZ[i-1].fromCoord)) / denom);
#undef toCoord
#undef fromCoord
}
int unmap (int value) const { return map (value, 1, 0); }
float unmap_float (float value) const { return map_float (value, 1, 0); }
// TODO Kill this.
Triple unmap_axis_range (const Triple& axis_range) const
{
F2DOT14 val, unmapped_val;
val.set_float (axis_range.minimum);
unmapped_val.set_int (unmap (val.to_int ()));
float unmapped_min = unmapped_val.to_float ();
val.set_float (axis_range.middle);
unmapped_val.set_int (unmap (val.to_int ()));
float unmapped_middle = unmapped_val.to_float ();
val.set_float (axis_range.maximum);
unmapped_val.set_int (unmap (val.to_int ()));
float unmapped_max = unmapped_val.to_float ();
float unmapped_min = unmap_float (axis_range.minimum);
float unmapped_middle = unmap_float (axis_range.middle);
float unmapped_max = unmap_float (axis_range.maximum);
return Triple{(double) unmapped_min, (double) unmapped_middle, (double) unmapped_max};
}
@ -203,6 +257,11 @@ struct SegmentMaps : Array16Of<AxisValueMap>
bool subset (hb_subset_context_t *c, hb_tag_t axis_tag) const
{
TRACE_SUBSET (this);
/* This function cannot work on avar2 table (and currently doesn't).
* We should instead keep the design coords in the shape plan and use
* those. unmap_axis_range needs to be killed. */
/* avar mapped normalized axis range*/
Triple *axis_range;
if (!c->plan->axes_location.has (axis_tag, &axis_range))
@ -304,14 +363,14 @@ struct avar
return_trace (true);
}
void map_coords (int *coords, unsigned int coords_length) const
void map_coords_16_16 (int *coords, unsigned int coords_length) const
{
unsigned int count = hb_min (coords_length, axisCount);
const SegmentMaps *map = &firstAxisSegmentMaps;
for (unsigned int i = 0; i < count; i++)
{
coords[i] = map->map (coords[i]);
coords[i] = roundf (map->map_float (coords[i] / 65536.f) * 65536.f);
map = &StructAfter<SegmentMaps> (*map);
}
@ -336,8 +395,8 @@ struct avar
int v = coords[i];
uint32_t varidx = varidx_map.map (i);
float delta = var_store.get_delta (varidx, coords, coords_length, var_store_cache);
v += roundf (delta);
v = hb_clamp (v, -(1<<14), +(1<<14));
v += roundf (delta * 4); // 2.14 -> 16.16
v = hb_clamp (v, -(1<<16), +(1<<16));
out.push (v);
}
for (unsigned i = 0; i < coords_length; i++)
@ -347,18 +406,6 @@ struct avar
#endif
}
void unmap_coords (int *coords, unsigned int coords_length) const
{
unsigned int count = hb_min (coords_length, axisCount);
const SegmentMaps *map = &firstAxisSegmentMaps;
for (unsigned int i = 0; i < count; i++)
{
coords[i] = map->unmap (coords[i]);
map = &StructAfter<SegmentMaps> (*map);
}
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);

View file

@ -33,7 +33,6 @@
namespace OT {
/* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */
struct TupleVariationHeader
{
@ -53,7 +52,7 @@ struct TupleVariationHeader
{
const F2DOT14 *peak_tuple = nullptr;
if (has_peak ())
peak_tuple = get_peak_tuple (axis_count).arrayZ;
peak_tuple = get_peak_tuple (axis_count);
else
{
unsigned int index = get_index ();
@ -68,8 +67,8 @@ struct TupleVariationHeader
if (has_interm)
{
start_tuple = get_start_tuple (axis_count).arrayZ;
end_tuple = get_end_tuple (axis_count).arrayZ;
start_tuple = get_start_tuple (axis_count);
end_tuple = get_end_tuple (axis_count);
}
for (unsigned i = 0; i < axis_count; i++)
@ -98,60 +97,56 @@ struct TupleVariationHeader
return true;
}
HB_ALWAYS_INLINE
double calculate_scalar (hb_array_t<const int> coords, unsigned int coord_count,
const hb_array_t<const F2DOT14> shared_tuples,
const hb_vector_t<hb_pair_t<int,int>> *shared_tuple_active_idx = nullptr) const
hb_scalar_cache_t *shared_tuple_scalar_cache = nullptr) const
{
unsigned tuple_index = tupleIndex;
const F2DOT14 *peak_tuple;
unsigned start_idx = 0;
unsigned end_idx = coord_count;
unsigned step = 1;
bool has_interm = tuple_index & TuppleIndex::IntermediateRegion; // Inlined for performance
if (unlikely (has_interm))
shared_tuple_scalar_cache = nullptr;
if (has_peak ())
peak_tuple = get_peak_tuple (coord_count).arrayZ;
if (unlikely (tuple_index & TuppleIndex::EmbeddedPeakTuple)) // Inlined for performance
{
peak_tuple = get_peak_tuple (coord_count);
shared_tuple_scalar_cache = nullptr;
}
else
{
unsigned int index = get_index ();
unsigned int index = tuple_index & TuppleIndex::TupleIndexMask; // Inlined for performance
float scalar;
if (shared_tuple_scalar_cache &&
shared_tuple_scalar_cache->get (index, &scalar))
return (double) scalar;
if (unlikely ((index + 1) * coord_count > shared_tuples.length))
return 0.0;
peak_tuple = shared_tuples.sub_array (coord_count * index, coord_count).arrayZ;
peak_tuple = shared_tuples.arrayZ + (coord_count * index);
if (shared_tuple_active_idx)
{
if (unlikely (index >= shared_tuple_active_idx->length))
return 0.0;
auto _ = (*shared_tuple_active_idx).arrayZ[index];
if (_.second != -1)
{
start_idx = _.first;
end_idx = _.second + 1;
step = _.second - _.first;
}
else if (_.first != -1)
{
start_idx = _.first;
end_idx = start_idx + 1;
}
}
}
const F2DOT14 *start_tuple = nullptr;
const F2DOT14 *end_tuple = nullptr;
bool has_interm = has_intermediate ();
if (has_interm)
{
start_tuple = get_start_tuple (coord_count).arrayZ;
end_tuple = get_end_tuple (coord_count).arrayZ;
start_tuple = get_start_tuple (coord_count);
end_tuple = get_end_tuple (coord_count);
}
double scalar = 1.0;
for (unsigned int i = start_idx; i < end_idx; i += step)
for (unsigned int i = 0; i < coord_count; i++)
{
int peak = peak_tuple[i].to_int ();
if (!peak) continue;
int v = coords[i];
if (!v) { scalar = 0.0; break; }
if (v == peak) continue;
if (has_interm)
@ -160,16 +155,18 @@ struct TupleVariationHeader
int end = end_tuple[i].to_int ();
if (unlikely (start > peak || peak > end ||
(start < 0 && end > 0 && peak))) continue;
if (v < start || v > end) return 0.0;
if (v < start || v > end) { scalar = 0.0; break; }
if (v < peak)
{ if (peak != start) scalar *= (double) (v - start) / (peak - start); }
else
{ if (peak != end) scalar *= (double) (end - v) / (end - peak); }
}
else if (!v || v < hb_min (0, peak) || v > hb_max (0, peak)) return 0.0;
else if (v < hb_min (0, peak) || v > hb_max (0, peak)) { scalar = 0.0; break; }
else
scalar *= (double) v / peak;
}
if (shared_tuple_scalar_cache)
shared_tuple_scalar_cache->set (get_index (), scalar);
return scalar;
}
@ -194,12 +191,14 @@ struct TupleVariationHeader
hb_array_t<const F2DOT14> get_all_tuples (unsigned axis_count) const
{ return StructAfter<UnsizedArrayOf<F2DOT14>> (tupleIndex).as_array ((has_peak () + has_intermediate () * 2) * axis_count); }
hb_array_t<const F2DOT14> get_peak_tuple (unsigned axis_count) const
{ return get_all_tuples (axis_count).sub_array (0, axis_count); }
hb_array_t<const F2DOT14> get_start_tuple (unsigned axis_count) const
{ return get_all_tuples (axis_count).sub_array (has_peak () * axis_count, axis_count); }
hb_array_t<const F2DOT14> get_end_tuple (unsigned axis_count) const
{ return get_all_tuples (axis_count).sub_array (has_peak () * axis_count + axis_count, axis_count); }
const F2DOT14* get_all_tuples_base (unsigned axis_count) const
{ return StructAfter<UnsizedArrayOf<F2DOT14>> (tupleIndex).arrayZ; }
const F2DOT14* get_peak_tuple (unsigned axis_count) const
{ return get_all_tuples_base (axis_count); }
const F2DOT14* get_start_tuple (unsigned axis_count) const
{ return get_all_tuples_base (axis_count) + has_peak () * axis_count; }
const F2DOT14* get_end_tuple (unsigned axis_count) const
{ return get_all_tuples_base (axis_count) + has_peak () * axis_count + axis_count; }
HBUINT16 varDataSize; /* The size in bytes of the serialized
* data for this tuple variation table. */
@ -1330,7 +1329,7 @@ struct TupleVariationData
{
var_data_bytes = var_data_bytes_;
var_data = var_data_bytes_.as<TupleVariationData> ();
index = 0;
tuples_left = var_data->tupleVarCount.get_count ();
axis_count = axis_count_;
current_tuple = &var_data->get_tuple_var_header ();
data_offset = 0;
@ -1349,30 +1348,41 @@ struct TupleVariationData
return true;
}
bool is_valid () const
bool is_valid ()
{
return (index < var_data->tupleVarCount.get_count ()) &&
var_data_bytes.check_range (current_tuple, TupleVariationHeader::min_size) &&
var_data_bytes.check_range (current_tuple, hb_max (current_tuple->get_data_size (),
current_tuple->get_size (axis_count)));
if (unlikely (tuples_left <= 0))
return false;
current_tuple_size = TupleVariationHeader::min_size;
if (unlikely (!var_data_bytes.check_range (current_tuple, current_tuple_size)))
return false;
current_tuple_size = current_tuple->get_size (axis_count);
if (unlikely (!var_data_bytes.check_range (current_tuple, current_tuple_size)))
return false;
return true;
}
HB_ALWAYS_INLINE
bool move_to_next ()
{
data_offset += current_tuple->get_data_size ();
current_tuple = &current_tuple->get_next (axis_count);
index++;
current_tuple = &StructAtOffset<TupleVariationHeader> (current_tuple, current_tuple_size);
tuples_left--;
return is_valid ();
}
// TODO: Make it return (sanitized) hb_bytes_t
const HBUINT8 *get_serialized_data () const
{ return &(table_base+var_data->data) + data_offset; }
private:
signed tuples_left;
const TupleVariationData *var_data;
unsigned int index;
unsigned int axis_count;
unsigned int data_offset;
unsigned int current_tuple_size;
const void *table_base;
public:
@ -1449,9 +1459,10 @@ struct TupleVariationData
static bool decompile_deltas (const HBUINT8 *&p /* IN/OUT */,
hb_vector_t<T> &deltas /* IN/OUT */,
const HBUINT8 *end,
bool consume_all = false)
bool consume_all = false,
unsigned start = 0)
{
return TupleValues::decompile (p, deltas, end, consume_all);
return TupleValues::decompile (p, deltas, end, consume_all, start);
}
bool has_data () const { return tupleVarCount; }

View file

@ -78,7 +78,7 @@ struct InstanceRecord
return false;
if (!axes_location->has (*axis_tag))
continue;
Triple axis_limit = axes_location->get (*axis_tag);
if (!axis_coord_pinned_or_within_axis_range (coords, i, axis_limit))
return false;
@ -106,7 +106,7 @@ struct InstanceRecord
{
if (!axis_coord_pinned_or_within_axis_range (coords, i, *axis_limit))
return_trace (false);
//skip pinned axis
if (axis_limit->is_point ())
continue;
@ -179,7 +179,7 @@ struct AxisRecord
hb_tag_t get_axis_tag () const { return axisTag; }
int normalize_axis_value (float v) const
float normalize_axis_value (float v) const
{
float min_value, default_value, max_value;
get_coordinates (min_value, default_value, max_value);
@ -189,23 +189,9 @@ struct AxisRecord
if (v == default_value)
return 0;
else if (v < default_value)
v = (v - default_value) / (default_value - min_value);
return (v - default_value) / (default_value - min_value);
else
v = (v - default_value) / (max_value - default_value);
return roundf (v * 16384.f);
}
float unnormalize_axis_value (int v) const
{
float min_value, default_value, max_value;
get_coordinates (min_value, default_value, max_value);
if (v == 0)
return default_value;
else if (v < 0)
return v * (default_value - min_value) / 16384.f + default_value;
else
return v * (max_value - default_value) / 16384.f + default_value;
return (v - default_value) / (max_value - default_value);
}
hb_ot_name_id_t get_name_id () const { return axisNameID; }
@ -341,12 +327,9 @@ struct fvar
return axes.lfind (tag, &i) && ((void) axes[i].get_axis_info (i, info), true);
}
int normalize_axis_value (unsigned int axis_index, float v) const
float normalize_axis_value (unsigned int axis_index, float v) const
{ return get_axes ()[axis_index].normalize_axis_value (v); }
float unnormalize_axis_value (unsigned int axis_index, int v) const
{ return get_axes ()[axis_index].unnormalize_axis_value (v); }
unsigned int get_instance_count () const { return instanceCount; }
hb_ot_name_id_t get_instance_subfamily_name_id (unsigned int instance_index) const

View file

@ -582,6 +582,17 @@ struct gvar_GVAR
public:
struct accelerator_t
{
hb_scalar_cache_t *create_cache () const
{
return hb_scalar_cache_t::create (table->sharedTupleCount);
}
static void destroy_cache (hb_scalar_cache_t *cache)
{
hb_scalar_cache_t::destroy (cache);
}
bool has_data () const { return table->has_data (); }
accelerator_t (hb_face_t *face)
@ -589,36 +600,6 @@ struct gvar_GVAR
table = hb_sanitize_context_t ().reference_table<gvar_GVAR> (face);
/* If sanitize failed, set glyphCount to 0. */
glyphCount = table->version.to_int () ? face->get_num_glyphs () : 0;
/* For shared tuples that only have one or two axes active, shared the index
* of that axis as a cache. This will speed up caclulate_scalar() a lot
* for fonts with lots of axes and many "monovar" or "duovar" tuples. */
hb_array_t<const F2DOT14> shared_tuples = (table+table->sharedTuples).as_array (table->sharedTupleCount * table->axisCount);
unsigned count = table->sharedTupleCount;
if (unlikely (!shared_tuple_active_idx.resize (count, false))) return;
unsigned axis_count = table->axisCount;
for (unsigned i = 0; i < count; i++)
{
hb_array_t<const F2DOT14> tuple = shared_tuples.sub_array (axis_count * i, axis_count);
int idx1 = -1, idx2 = -1;
for (unsigned j = 0; j < axis_count; j++)
{
const F2DOT14 &peak = tuple.arrayZ[j];
if (peak.to_int () != 0)
{
if (idx1 == -1)
idx1 = j;
else if (idx2 == -1)
idx2 = j;
else
{
idx1 = idx2 = -1;
break;
}
}
}
shared_tuple_active_idx.arrayZ[i] = {idx1, idx2};
}
}
~accelerator_t () { table.destroy (); }
@ -655,6 +636,7 @@ struct gvar_GVAR
hb_array_t<const int> coords,
const hb_array_t<contour_point_t> points,
hb_glyf_scratch_t &scratch,
hb_scalar_cache_t *gvar_cache = nullptr,
bool phantom_only = false) const
{
if (unlikely (glyph >= glyphCount)) return true;
@ -690,10 +672,12 @@ struct gvar_GVAR
unsigned count = points.length;
bool flush = false;
do
{
float scalar = iterator.current_tuple->calculate_scalar (coords, num_coords, shared_tuples,
&shared_tuple_active_idx);
gvar_cache);
if (scalar == 0.f) continue;
const HBUINT8 *p = iterator.get_serialized_data ();
unsigned int length = iterator.current_tuple->get_data_size ();
@ -717,11 +701,12 @@ struct gvar_GVAR
const hb_array_t<unsigned int> &indices = has_private_points ? private_indices : shared_indices;
bool apply_to_all = (indices.length == 0);
unsigned int num_deltas = apply_to_all ? points.length : indices.length;
unsigned num_deltas = apply_to_all ? points.length : indices.length;
unsigned start_deltas = (phantom_only && num_deltas >= 4 ? num_deltas - 4 : 0);
if (unlikely (!x_deltas.resize (num_deltas, false))) return false;
if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end))) return false;
if (unlikely (!GlyphVariationData::decompile_deltas (p, x_deltas, end, false, start_deltas))) return false;
if (unlikely (!y_deltas.resize (num_deltas, false))) return false;
if (unlikely (!GlyphVariationData::decompile_deltas (p, y_deltas, end))) return false;
if (unlikely (!GlyphVariationData::decompile_deltas (p, y_deltas, end, false, start_deltas))) return false;
if (!apply_to_all)
{
@ -884,7 +869,6 @@ struct gvar_GVAR
private:
hb_blob_ptr_t<gvar_GVAR> table;
unsigned glyphCount;
hb_vector_t<hb_pair_t<int, int>> shared_tuple_active_idx;
};
protected:

View file

@ -156,7 +156,7 @@ struct index_map_subset_plan_t
unsigned outer = (*new_varidx) >> 16;
unsigned bit_count = (outer == 0) ? 1 : hb_bit_storage (outer);
outer_bit_count = hb_max (bit_count, outer_bit_count);
unsigned inner = (*new_varidx) & 0xFFFF;
bit_count = (inner == 0) ? 1 : hb_bit_storage (inner);
inner_bit_count = hb_max (bit_count, inner_bit_count);
@ -284,6 +284,8 @@ struct HVARVVAR
static constexpr hb_tag_t HVARTag = HB_OT_TAG_HVAR;
static constexpr hb_tag_t VVARTag = HB_OT_TAG_VVAR;
bool has_data () const { return version.major != 0; }
bool sanitize (hb_sanitize_context_t *c) const
{
TRACE_SANITIZE (this);
@ -382,9 +384,10 @@ struct HVARVVAR
hvar_plan.index_map_plans.as_array ()));
}
HB_ALWAYS_INLINE
float get_advance_delta_unscaled (hb_codepoint_t glyph,
const int *coords, unsigned int coord_count,
ItemVariationStore::cache_t *store_cache = nullptr) const
hb_scalar_cache_t *store_cache = nullptr) const
{
uint32_t varidx = (this+advMap).map (glyph);
return (this+varStore).get_delta (varidx,
@ -392,16 +395,6 @@ struct HVARVVAR
store_cache);
}
bool get_lsb_delta_unscaled (hb_codepoint_t glyph,
const int *coords, unsigned int coord_count,
float *lsb) const
{
if (!lsbMap) return false;
uint32_t varidx = (this+lsbMap).map (glyph);
*lsb = (this+varStore).get_delta (varidx, coords, coord_count);
return true;
}
public:
FixedVersion<>version; /* Version of the metrics variation table
* initially set to 0x00010000u */
@ -454,14 +447,16 @@ struct VVAR : HVARVVAR {
bool subset (hb_subset_context_t *c) const { return HVARVVAR::_subset<VVAR> (c); }
bool get_vorg_delta_unscaled (hb_codepoint_t glyph,
const int *coords, unsigned int coord_count,
float *delta) const
HB_ALWAYS_INLINE
float get_vorg_delta_unscaled (hb_codepoint_t glyph,
const int *coords, unsigned int coord_count,
hb_scalar_cache_t *store_cache = nullptr) const
{
if (!vorgMap) return false;
if (!vorgMap) return 0.f;
uint32_t varidx = (this+vorgMap).map (glyph);
*delta = (this+varStore).get_delta (varidx, coords, coord_count);
return true;
return (this+varStore).get_delta (varidx,
coords, coord_count,
store_cache);
}
protected:

View file

@ -70,7 +70,7 @@ hb_ot_var_has_data (hb_face_t *face)
* hb_ot_var_get_axis_count:
* @face: The #hb_face_t to work on
*
* Fetches the number of OpenType variation axes included in the face.
* Fetches the number of OpenType variation axes included in the face.
*
* Return value: the number of variation axes defined
*
@ -183,7 +183,7 @@ hb_ot_var_find_axis_info (hb_face_t *face,
* hb_ot_var_get_named_instance_count:
* @face: The #hb_face_t to work on
*
* Fetches the number of named instances included in the face.
* Fetches the number of named instances included in the face.
*
* Return value: the number of named instances defined
*
@ -263,7 +263,7 @@ hb_ot_var_named_instance_get_design_coords (hb_face_t *face,
* @face: The #hb_face_t to work on
* @variations: The array of variations to normalize
* @variations_length: The number of variations to normalize
* @coords: (out) (array length=coords_length): The array of normalized coordinates
* @coords: (out) (array length=coords_length): The array of normalized coordinates
* @coords_length: The length of the coordinate array
*
* Normalizes all of the coordinates in the given list of variation axes.
@ -286,10 +286,14 @@ hb_ot_var_normalize_variations (hb_face_t *face,
hb_ot_var_axis_info_t info;
if (hb_ot_var_find_axis_info (face, variations[i].tag, &info) &&
info.axis_index < coords_length)
coords[info.axis_index] = fvar.normalize_axis_value (info.axis_index, variations[i].value);
coords[info.axis_index] = roundf (fvar.normalize_axis_value (info.axis_index, variations[i].value) * 65536.0f);
}
face->table.avar->map_coords (coords, coords_length);
face->table.avar->map_coords_16_16 (coords, coords_length);
// Round to 2.14
for (unsigned i = 0; i < coords_length; i++)
coords[i] = (coords[i] + 2) >> 2;
}
/**
@ -309,6 +313,10 @@ hb_ot_var_normalize_variations (hb_face_t *face,
* Any additional scaling defined in the face's `avar` table is also
* applied, as described at https://docs.microsoft.com/en-us/typography/opentype/spec/avar
*
* Note: @coords_length must be the same as the number of axes in the face, as
* for example returned by hb_ot_var_get_axis_count().
* Otherwise, the behavior is undefined.
*
* Since: 1.4.2
**/
void
@ -319,9 +327,13 @@ hb_ot_var_normalize_coords (hb_face_t *face,
{
const OT::fvar &fvar = *face->table.fvar;
for (unsigned int i = 0; i < coords_length; i++)
normalized_coords[i] = fvar.normalize_axis_value (i, design_coords[i]);
normalized_coords[i] = roundf (fvar.normalize_axis_value (i, design_coords[i]) * 65536.0f);
face->table.avar->map_coords (normalized_coords, coords_length);
face->table.avar->map_coords_16_16 (normalized_coords, coords_length);
// Round to 2.14
for (unsigned i = 0; i < coords_length; i++)
normalized_coords[i] = (normalized_coords[i] + 2) >> 2;
}

View file

@ -61,6 +61,7 @@ struct VORG
bool has_data () const { return version.to_int (); }
HB_ALWAYS_INLINE
int get_y_origin (hb_codepoint_t glyph) const
{
unsigned int i;

View file

@ -49,7 +49,7 @@ hb_paint_extents_push_transform (hb_paint_funcs_t *funcs HB_UNUSED,
{
hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data;
c->push_transform (hb_transform_t {xx, yx, xy, yy, dx, dy});
c->push_transform (hb_transform_t<> {xx, yx, xy, yy, dx, dy});
}
static void
@ -71,7 +71,7 @@ hb_paint_extents_push_clip_glyph (hb_paint_funcs_t *funcs HB_UNUSED,
{
hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data;
hb_extents_t extents;
hb_extents_t<> extents;
hb_draw_funcs_t *draw_extent_funcs = hb_draw_extents_get_funcs ();
hb_font_draw_glyph (font, glyph, draw_extent_funcs, &extents);
c->push_clip (extents);
@ -85,7 +85,7 @@ hb_paint_extents_push_clip_rectangle (hb_paint_funcs_t *funcs HB_UNUSED,
{
hb_paint_extents_context_t *c = (hb_paint_extents_context_t *) paint_data;
hb_extents_t extents = {xmin, ymin, xmax, ymax};
hb_extents_t<> extents = {xmin, ymin, xmax, ymax};
c->push_clip (extents);
}
@ -136,10 +136,10 @@ hb_paint_extents_paint_image (hb_paint_funcs_t *funcs HB_UNUSED,
if (!glyph_extents)
return false; // Happens with SVG images.
hb_extents_t extents = {(float) glyph_extents->x_bearing,
(float) glyph_extents->y_bearing + glyph_extents->height,
(float) glyph_extents->x_bearing + glyph_extents->width,
(float) glyph_extents->y_bearing};
hb_extents_t<> extents = {(float) glyph_extents->x_bearing,
(float) glyph_extents->y_bearing + glyph_extents->height,
(float) glyph_extents->x_bearing + glyph_extents->width,
(float) glyph_extents->y_bearing};
c->push_clip (extents);
c->paint ();
c->pop_clip ();

View file

@ -41,9 +41,9 @@ struct hb_paint_extents_context_t
clips.clear ();
groups.clear ();
transforms.push (hb_transform_t{});
clips.push (hb_bounds_t{hb_bounds_t::UNBOUNDED});
groups.push (hb_bounds_t{hb_bounds_t::EMPTY});
transforms.push (hb_transform_t<>{});
clips.push (hb_bounds_t<>{hb_bounds_t<>::UNBOUNDED});
groups.push (hb_bounds_t<>{hb_bounds_t<>::EMPTY});
}
hb_paint_extents_context_t ()
@ -51,19 +51,19 @@ struct hb_paint_extents_context_t
clear ();
}
hb_extents_t get_extents ()
hb_extents_t<> get_extents ()
{
return groups.tail().extents;
}
bool is_bounded ()
{
return groups.tail().status != hb_bounds_t::UNBOUNDED;
return groups.tail().status != hb_bounds_t<>::UNBOUNDED;
}
void push_transform (const hb_transform_t &trans)
void push_transform (const hb_transform_t<> &trans)
{
hb_transform_t t = transforms.tail ();
hb_transform_t<> t = transforms.tail ();
t.multiply (trans);
transforms.push (t);
}
@ -73,13 +73,13 @@ struct hb_paint_extents_context_t
transforms.pop ();
}
void push_clip (hb_extents_t extents)
void push_clip (hb_extents_t<> extents)
{
/* Transform extents and push a new clip. */
const hb_transform_t &t = transforms.tail ();
const hb_transform_t<> &t = transforms.tail ();
t.transform_extents (extents);
auto bounds = hb_bounds_t {extents};
auto bounds = hb_bounds_t<> {extents};
bounds.intersect (clips.tail ());
clips.push (bounds);
@ -92,19 +92,19 @@ struct hb_paint_extents_context_t
void push_group ()
{
groups.push (hb_bounds_t {hb_bounds_t::EMPTY});
groups.push (hb_bounds_t<> {hb_bounds_t<>::EMPTY});
}
void pop_group (hb_paint_composite_mode_t mode)
{
const hb_bounds_t src_bounds = groups.pop ();
hb_bounds_t &backdrop_bounds = groups.tail ();
const hb_bounds_t<> src_bounds = groups.pop ();
hb_bounds_t<> &backdrop_bounds = groups.tail ();
// https://learn.microsoft.com/en-us/typography/opentype/spec/colr#format-32-paintcomposite
switch ((int) mode)
{
case HB_PAINT_COMPOSITE_MODE_CLEAR:
backdrop_bounds.status = hb_bounds_t::EMPTY;
backdrop_bounds.status = hb_bounds_t<>::EMPTY;
break;
case HB_PAINT_COMPOSITE_MODE_SRC:
case HB_PAINT_COMPOSITE_MODE_SRC_OUT:
@ -125,16 +125,16 @@ struct hb_paint_extents_context_t
void paint ()
{
const hb_bounds_t &clip = clips.tail ();
hb_bounds_t &group = groups.tail ();
const hb_bounds_t<> &clip = clips.tail ();
hb_bounds_t<> &group = groups.tail ();
group.union_ (clip);
}
protected:
hb_vector_t<hb_transform_t> transforms;
hb_vector_t<hb_bounds_t> clips;
hb_vector_t<hb_bounds_t> groups;
hb_vector_t<hb_transform_t<>> transforms;
hb_vector_t<hb_bounds_t<>> clips;
hb_vector_t<hb_bounds_t<>> groups;
};
HB_INTERNAL hb_paint_funcs_t *

View file

@ -28,6 +28,7 @@
#include "hb.hh"
#include "hb-face.hh"
#include "hb-font.hh"
#include "hb-geometry.hh"
#define HB_PAINT_FUNCS_IMPLEMENT_CALLBACKS \
HB_PAINT_FUNC_IMPLEMENT (push_transform) \
@ -72,7 +73,11 @@ struct hb_paint_funcs_t
float xx, float yx,
float xy, float yy,
float dx, float dy)
{ func.push_transform (this, paint_data,
{
// Handle -0.f to avoid -0.f == 0.f in the transform matrix.
if (dx == -0.f) dx = 0.f;
if (dy == -0.f) dy = 0.f;
func.push_transform (this, paint_data,
xx, yx, xy, yy, dx, dy,
!user_data ? nullptr : user_data->push_transform); }
void pop_transform (void *paint_data)
@ -182,54 +187,59 @@ struct hb_paint_funcs_t
0, 0);
}
HB_NODISCARD
bool push_translate (void *paint_data,
void push_transform (void *paint_data, hb_transform_t<float> t)
{
push_transform (paint_data, t.xx, t.yx, t.xy, t.yy, t.x0, t.y0);
}
void push_translate (void *paint_data,
float dx, float dy)
{
if (!dx && !dy)
return false;
push_transform (paint_data,
1.f, 0.f, 0.f, 1.f, dx, dy);
return true;
hb_transform_t<float>::translation (dx, dy));
}
HB_NODISCARD
bool push_scale (void *paint_data,
void push_scale (void *paint_data,
float sx, float sy)
{
if (sx == 1.f && sy == 1.f)
return false;
push_transform (paint_data,
sx, 0.f, 0.f, sy, 0.f, 0.f);
return true;
hb_transform_t<float>::scaling (sx, sy));
}
void push_scale_around_center (void *paint_data,
float sx, float sy,
float cx, float cy)
{
push_transform (paint_data,
hb_transform_t<float>::scaling_around_center (sx, sy, cx, cy));
}
HB_NODISCARD
bool push_rotate (void *paint_data,
void push_rotate (void *paint_data,
float a)
{
if (!a)
return false;
float cc = cosf (a * HB_PI);
float ss = sinf (a * HB_PI);
push_transform (paint_data, cc, ss, -ss, cc, 0.f, 0.f);
return true;
push_transform (paint_data,
hb_transform_t<float>::rotation (a * HB_PI));
}
HB_NODISCARD
bool push_skew (void *paint_data,
void push_rotate_around_center (void *paint_data,
float a,
float cx, float cy)
{
push_transform (paint_data,
hb_transform_t<float>::rotation_around_center (a * HB_PI, cx, cy));
}
void push_skew (void *paint_data,
float sx, float sy)
{
if (!sx && !sy)
return false;
float x = tanf (-sx * HB_PI);
float y = tanf (+sy * HB_PI);
push_transform (paint_data, 1.f, y, x, 1.f, 0.f, 0.f);
return true;
push_transform (paint_data,
hb_transform_t<float>::skewing (-sx * HB_PI, sy * HB_PI));
}
void push_skew_around_center (void *paint_data,
float sx, float sy,
float cx, float cy)
{
push_transform (paint_data,
hb_transform_t<float>::skewing_around_center (-sx * HB_PI, sy * HB_PI, cx, cy));
}
};
DECLARE_NULL_INSTANCE (hb_paint_funcs_t);

View file

@ -266,7 +266,7 @@ bool _resolve_shared_overflow(const hb_vector_t<graph::overflow_record_t>& overf
result = sorted_graph.duplicate(&parents, r.child);
}
if (result == (unsigned) -1) return result;
if (result == (unsigned) -1) return false;
if (parents.get_population() > 1) {
// If the duplicated node has more than one parent pre-emptively raise it's priority to the maximum.
@ -283,7 +283,7 @@ bool _resolve_shared_overflow(const hb_vector_t<graph::overflow_record_t>& overf
sorted_graph.vertices_[result].give_max_priority();
}
return result;
return true;
}
static inline
@ -302,8 +302,11 @@ bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows,
{
// The child object is shared, we may be able to eliminate the overflow
// by duplicating it.
if (!_resolve_shared_overflow(overflows, i, sorted_graph)) continue;
return true;
if (_resolve_shared_overflow(overflows, i, sorted_graph))
return true;
// Sometimes we can't duplicate a node which looks shared because it's not actually shared
// (eg. all links from the same parent) in this case continue on to other resolution options.
}
if (child.is_leaf () && !priority_bumped_parents.has (r.parent))

View file

@ -120,8 +120,8 @@
struct hb_sanitize_context_t :
hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE>
{
hb_sanitize_context_t () :
start (nullptr), end (nullptr),
hb_sanitize_context_t (const char *start_ = nullptr, const char *end_ = nullptr) :
start (start_), end (end_),
length (0),
max_ops (0), max_subtables (0),
recursion_depth (0),
@ -212,14 +212,22 @@ struct hb_sanitize_context_t :
void reset_object ()
{
this->start = this->blob->data;
this->end = this->start + this->blob->length;
if (this->blob)
{
this->start = this->blob->data;
this->end = this->start + this->blob->length;
}
this->length = this->end - this->start;
assert (this->start <= this->end); /* Must not overflow. */
}
void start_processing ()
void start_processing (const char *start_ = nullptr, const char *end_ = nullptr)
{
if (start_)
{
this->start = start_;
this->end = end_;
}
reset_object ();
unsigned m;
if (unlikely (hb_unsigned_mul_overflows (this->end - this->start, HB_SANITIZE_MAX_OPS_FACTOR, &m)))
@ -463,9 +471,11 @@ struct hb_sanitize_context_t :
}
else
{
if (edit_count && !writable) {
start = hb_blob_get_data_writable (blob, nullptr);
end = start + blob->length;
if (edit_count && !writable)
{
unsigned length;
start = hb_blob_get_data_writable (blob, &length);
end = start + length;
if (start)
{

View file

@ -794,7 +794,8 @@ struct hb_serialize_context_t
template <typename T, unsigned Size = sizeof (T)>
void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset)
{
auto &off = * ((BEInt<T, Size> *) (parent->head + link.position));
// XXX We should stop assuming big-endian!
auto &off = * ((HBInt<true, T, Size> *) (parent->head + link.position));
assert (0 == off);
check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW);
}

View file

@ -57,8 +57,8 @@ HB_SHAPER_IMPLEMENT (directwrite)
HB_SHAPER_IMPLEMENT (coretext)
#endif
#ifdef HAVE_HARFRUZZ
HB_SHAPER_IMPLEMENT (harfruzz)
#ifdef HAVE_HARFRUST
HB_SHAPER_IMPLEMENT (harfrust)
#endif
#ifndef HB_NO_FALLBACK_SHAPE

View file

@ -113,27 +113,4 @@ hb_face_t::load_upem () const
return ret;
}
#ifndef HB_NO_VAR
bool
_glyf_get_leading_bearing_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical,
int *lsb)
{
return font->face->table.glyf->get_leading_bearing_with_var_unscaled (font, glyph, is_vertical, lsb);
}
unsigned
_glyf_get_advance_with_var_unscaled (hb_font_t *font, hb_codepoint_t glyph, bool is_vertical)
{
return font->face->table.glyf->get_advance_with_var_unscaled (font, glyph, is_vertical);
}
#endif
bool
_glyf_get_leading_bearing_without_var_unscaled (hb_face_t *face, hb_codepoint_t gid, bool is_vertical, int *lsb)
{
return face->table.glyf->get_leading_bearing_without_var_unscaled (gid, is_vertical, lsb);
}
#endif

View file

@ -42,30 +42,30 @@
hb_vector_t<hb_inc_bimap_t> &inner_maps /* OUT */)
{
if (varidx_set.is_empty () || subtable_count == 0) return;
if (unlikely (!inner_maps.resize (subtable_count))) return;
for (unsigned idx : varidx_set)
{
uint16_t major = idx >> 16;
uint16_t minor = idx & 0xFFFF;
if (major >= subtable_count)
continue;
inner_maps[major].add (minor);
}
}
static inline hb_font_t*
_get_hb_font_with_variations (const hb_subset_plan_t *plan)
{
hb_font_t *font = hb_font_create (plan->source);
hb_vector_t<hb_variation_t> vars;
if (!vars.alloc (plan->user_axes_location.get_population ())) {
hb_font_destroy (font);
return nullptr;
}
for (auto _ : plan->user_axes_location)
{
hb_variation_t var;
@ -73,11 +73,11 @@
var.value = _.second.middle;
vars.push (var);
}
hb_font_set_variations (font, vars.arrayZ, plan->user_axes_location.get_population ());
return font;
}
template<typename ItemVarStore>
void
remap_variation_indices (const ItemVarStore &var_store,
@ -90,7 +90,7 @@
if (&var_store == &Null (OT::ItemVariationStore)) return;
unsigned subtable_count = var_store.get_sub_table_count ();
auto *store_cache = var_store.create_cache ();
unsigned new_major = 0, new_minor = 0;
unsigned last_major = (variation_indices.get_min ()) >> 16;
for (unsigned idx : variation_indices)
@ -99,13 +99,13 @@
if (calculate_delta)
delta = roundf (var_store.get_delta (idx, normalized_coords.arrayZ,
normalized_coords.length, store_cache));
if (no_variations)
{
variation_idx_delta_map.set (idx, hb_pair_t<unsigned, int> (HB_OT_LAYOUT_NO_VARIATIONS_INDEX, delta));
continue;
}
uint16_t major = idx >> 16;
if (major >= subtable_count) break;
if (major != last_major)
@ -113,7 +113,7 @@
new_minor = 0;
++new_major;
}
unsigned new_idx = (new_major << 16) + new_minor;
variation_idx_delta_map.set (idx, hb_pair_t<unsigned, int> (new_idx, delta));
++new_minor;
@ -121,7 +121,7 @@
}
var_store.destroy_cache (store_cache);
}
template
void
remap_variation_indices<OT::ItemVariationStore> (const OT::ItemVariationStore &var_store,
@ -130,7 +130,7 @@
bool calculate_delta, /* not pinned at default */
bool no_variations, /* all axes pinned */
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> &variation_idx_delta_map /* OUT */);
#ifndef HB_NO_BASE
void
collect_base_variation_indices (hb_subset_plan_t* plan)
@ -141,23 +141,23 @@
base.destroy ();
return;
}
hb_set_t varidx_set;
base->collect_variation_indices (plan, varidx_set);
const OT::ItemVariationStore &var_store = base->get_var_store ();
unsigned subtable_count = var_store.get_sub_table_count ();
remap_variation_indices (var_store, varidx_set,
plan->normalized_coords,
!plan->pinned_at_default,
plan->all_axes_pinned,
plan->base_variation_idx_map);
generate_varstore_inner_maps (varidx_set, subtable_count, plan->base_varstore_inner_maps);
base.destroy ();
}
#endif
void
@ -199,24 +199,44 @@ normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan)
{
plan->axes_triple_distances.set (axis_tag, axis.get_triple_distances ());
int normalized_min = axis.normalize_axis_value (axis_range->minimum);
int normalized_default = axis.normalize_axis_value (axis_range->middle);
int normalized_max = axis.normalize_axis_value (axis_range->maximum);
float normalized_min = axis.normalize_axis_value (axis_range->minimum);
float normalized_default = axis.normalize_axis_value (axis_range->middle);
float normalized_max = axis.normalize_axis_value (axis_range->maximum);
// TODO(behdad): Spec says axis normalization should be done in 16.16;
// We used to do it in 2.14, but that's not correct. I fixed this in
// the fvar/avar code, but keeping 2.14 here for now to keep tests
// happy. We might need to adjust fonttools as well.
// I'm only fairly confident in the above statement. Anyway,
// we should look deeper into this, and also update fonttools if
// needed.
// Round to 2.14
normalized_min = roundf (normalized_min * 16384.f) / 16384.f;
normalized_default = roundf (normalized_default * 16384.f) / 16384.f;
normalized_max = roundf (normalized_max * 16384.f) / 16384.f;
if (has_avar && old_axis_idx < avar_axis_count)
{
normalized_min = seg_maps->map (normalized_min);
normalized_default = seg_maps->map (normalized_default);
normalized_max = seg_maps->map (normalized_max);
}
plan->axes_location.set (axis_tag, Triple (static_cast<double> (normalized_min / 16384.0),
static_cast<double> (normalized_default / 16384.0),
static_cast<double> (normalized_max / 16384.0)));
normalized_min = seg_maps->map_float (normalized_min);
normalized_default = seg_maps->map_float (normalized_default);
normalized_max = seg_maps->map_float (normalized_max);
if (normalized_default != 0)
// Round to 2.14
normalized_min = roundf (normalized_min * 16384.f) / 16384.f;
normalized_default = roundf (normalized_default * 16384.f) / 16384.f;
normalized_max = roundf (normalized_max * 16384.f) / 16384.f;
}
plan->axes_location.set (axis_tag, Triple ((double) normalized_min,
(double) normalized_default,
(double) normalized_max));
if (normalized_default == -0.f)
normalized_default = 0.f; // Normalize -0 to 0
if (normalized_default != 0.f)
plan->pinned_at_default = false;
plan->normalized_coords[old_axis_idx] = normalized_default;
plan->normalized_coords[old_axis_idx] = roundf (normalized_default * 16384.f);
}
old_axis_idx++;
@ -243,12 +263,12 @@ update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
hb_glyph_extents_t extents = {0x7FFF, -0x7FFF};
OT::hmtx_accelerator_t _hmtx (plan->source);
OT::ItemVariationStore::cache_t *hvar_store_cache = nullptr;
OT::hb_scalar_cache_t *hvar_store_cache = nullptr;
if (_hmtx.has_data () && _hmtx.var_table.get_length ())
hvar_store_cache = _hmtx.var_table->get_var_store ().create_cache ();
OT::vmtx_accelerator_t _vmtx (plan->source);
OT::ItemVariationStore::cache_t *vvar_store_cache = nullptr;
OT::hb_scalar_cache_t *vvar_store_cache = nullptr;
if (_vmtx.has_data () && _vmtx.var_table.get_length ())
vvar_store_cache = _vmtx.var_table->get_var_store ().create_cache ();
@ -279,8 +299,7 @@ update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
int lsb = extents.x_bearing;
if (!has_bounds_info)
{
if (!_hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb))
continue;
_hmtx.get_leading_bearing_without_var_unscaled (old_gid, &lsb);
}
plan->hmtx_map.set (new_gid, hb_pair ((unsigned) hori_aw, lsb));
plan->bounds_width_vec[new_gid] = extents.width;
@ -292,13 +311,17 @@ update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
if (_vmtx.var_table.get_length ())
vert_aw += (int) roundf (_vmtx.var_table->get_advance_delta_unscaled (old_gid, font->coords, font->num_coords,
vvar_store_cache));
int tsb = extents.y_bearing;
if (!has_bounds_info)
hb_position_t vorg_x = 0;
hb_position_t vorg_y = 0;
int tsb = 0;
if (has_bounds_info &&
hb_font_get_glyph_v_origin (font, old_gid, &vorg_x, &vorg_y))
{
if (!_vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb))
continue;
tsb = vorg_y - extents.y_bearing;
} else {
_vmtx.get_leading_bearing_without_var_unscaled (old_gid, &tsb);
}
plan->vmtx_map.set (new_gid, hb_pair ((unsigned) vert_aw, tsb));
plan->bounds_height_vec[new_gid] = extents.height;
}
@ -385,4 +408,4 @@ remap_colrv1_delta_set_index_indices<OT::DeltaSetIndexMap> (const OT::DeltaSetIn
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> &variation_idx_delta_map, /* IN/OUT */
hb_map_t &new_deltaset_idx_varidx_map /* OUT */);
#endif
#endif

View file

@ -248,7 +248,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes_in,
!(plan->flags & HB_SUBSET_FLAGS_NO_BIDI_CLOSURE));
OT::cmap::accelerator_t cmap (plan->source);
unsigned size_threshold = plan->source->get_num_glyphs ();
unsigned size_threshold = plan->source->get_num_glyphs ();
if (glyphs->is_empty () && unicodes.get_population () < size_threshold)
{
@ -376,7 +376,7 @@ _populate_unicodes_to_retain (const hb_set_t *unicodes_in,
// so record those first.
plan->os2_info.min_cmap_codepoint = plan->unicodes.get_min();
plan->os2_info.max_cmap_codepoint = plan->unicodes.get_max();
hb_set_t variation_selectors_to_retain;
cmap.collect_variation_selectors(&variation_selectors_to_retain);
+ variation_selectors_to_retain.iter()

File diff suppressed because it is too large Load diff

View file

@ -23,8 +23,9 @@
#include "hb-unicode.hh"
static const uint8_t
_hb_emoji_u8[464] =
#include <stdint.h>
static const uint8_t _hb_emoji_u8[464]=
{
16, 17, 17, 17, 50, 20, 21, 17, 17, 17, 17, 17, 17, 17, 17, 17,
17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
@ -57,20 +58,17 @@ _hb_emoji_u8[464] =
0,192,255,255, 0,240,255,255,255,255,255,247,191,255,255,255,
};
static inline unsigned
_hb_emoji_b4 (const uint8_t* a, unsigned i)
static inline uint8_t _hb_emoji_b4 (const uint8_t* a, unsigned i)
{
return (a[i>>1]>>((i&1u)<<2))&15u;
return (a[i>>1]>>((i&1)<<2))&15;
}
static inline unsigned
_hb_emoji_b1 (const uint8_t* a, unsigned i)
static inline uint8_t _hb_emoji_b1 (const uint8_t* a, unsigned i)
{
return (a[i>>3]>>((i&7u)<<0))&1u;
return (a[i>>3]>>((i&7)<<0))&1;
}
static inline uint_fast8_t
_hb_emoji_is_Extended_Pictographic (unsigned u)
static inline uint8_t _hb_emoji_is_Extended_Pictographic (unsigned u)
{
return u<131070u?_hb_emoji_b1(264+_hb_emoji_u8,((_hb_emoji_u8[144+(((_hb_emoji_u8[64+(((_hb_emoji_b4(_hb_emoji_u8,u>>5>>2>>3))<<3)+((u>>5>>2)&7u))])<<2)+((u>>5)&3u))])<<5)+((u)&31u)):0;
return u<131070 ? _hb_emoji_b1(_hb_emoji_u8+264u,((_hb_emoji_u8[144u+(((_hb_emoji_u8[64u+(((_hb_emoji_b4(_hb_emoji_u8,u>>5>>2>>3))<<3)+((u>>5>>2)&7))])<<2)+((u>>5)&3))])<<5)+((u)&31)) : 0;
}

View file

@ -241,6 +241,57 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIMPLE
}
}
static hb_codepoint_t
vertical_char_for (hb_codepoint_t u)
{
switch (u >> 8)
{
case 0x20: switch (u) {
case 0x2013u: return 0xfe32u; // EN DASH
case 0x2014u: return 0xfe31u; // EM DASH
case 0x2025u: return 0xfe30u; // TWO DOT LEADER
case 0x2026u: return 0xfe19u; // HORIZONTAL ELLIPSIS
} break;
case 0x30: switch (u) {
case 0x3001u: return 0xfe11u; // IDEOGRAPHIC COMMA
case 0x3002u: return 0xfe12u; // IDEOGRAPHIC FULL STOP
case 0x3008u: return 0xfe3fu; // LEFT ANGLE BRACKET
case 0x3009u: return 0xfe40u; // RIGHT ANGLE BRACKET
case 0x300au: return 0xfe3du; // LEFT DOUBLE ANGLE BRACKET
case 0x300bu: return 0xfe3eu; // RIGHT DOUBLE ANGLE BRACKET
case 0x300cu: return 0xfe41u; // LEFT CORNER BRACKET
case 0x300du: return 0xfe42u; // RIGHT CORNER BRACKET
case 0x300eu: return 0xfe43u; // LEFT WHITE CORNER BRACKET
case 0x300fu: return 0xfe44u; // RIGHT WHITE CORNER BRACKET
case 0x3010u: return 0xfe3bu; // LEFT BLACK LENTICULAR BRACKET
case 0x3011u: return 0xfe3cu; // RIGHT BLACK LENTICULAR BRACKET
case 0x3014u: return 0xfe39u; // LEFT TORTOISE SHELL BRACKET
case 0x3015u: return 0xfe3au; // RIGHT TORTOISE SHELL BRACKET
case 0x3016u: return 0xfe17u; // LEFT WHITE LENTICULAR BRACKET
case 0x3017u: return 0xfe18u; // RIGHT WHITE LENTICULAR BRACKET
} break;
case 0xfe: switch (u) {
case 0xfe4fu: return 0xfe34u; // WAVY LOW LINE
} break;
case 0xff: switch (u) {
case 0xff01u: return 0xfe15u; // FULLWIDTH EXCLAMATION MARK
case 0xff08u: return 0xfe35u; // FULLWIDTH LEFT PARENTHESIS
case 0xff09u: return 0xfe36u; // FULLWIDTH RIGHT PARENTHESIS
case 0xff0cu: return 0xfe10u; // FULLWIDTH COMMA
case 0xff1au: return 0xfe13u; // FULLWIDTH COLON
case 0xff1bu: return 0xfe14u; // FULLWIDTH SEMICOLON
case 0xff1fu: return 0xfe16u; // FULLWIDTH QUESTION MARK
case 0xff3bu: return 0xfe47u; // FULLWIDTH LEFT SQUARE BRACKET
case 0xff3du: return 0xfe48u; // FULLWIDTH RIGHT SQUARE BRACKET
case 0xff3fu: return 0xfe33u; // FULLWIDTH LOW LINE
case 0xff5bu: return 0xfe37u; // FULLWIDTH LEFT CURLY BRACKET
case 0xff5du: return 0xfe38u; // FULLWIDTH RIGHT CURLY BRACKET
} break;
}
return u;
}
struct {
#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_unicode_##name##_func_t name;
HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS

View file

@ -47,20 +47,20 @@ HB_BEGIN_DECLS
*
* The minor component of the library version available at compile-time.
*/
#define HB_VERSION_MINOR 2
#define HB_VERSION_MINOR 3
/**
* HB_VERSION_MICRO:
*
* The micro component of the library version available at compile-time.
*/
#define HB_VERSION_MICRO 1
#define HB_VERSION_MICRO 2
/**
* HB_VERSION_STRING:
*
* A string literal containing the library version available at compile-time.
*/
#define HB_VERSION_STRING "11.2.1"
#define HB_VERSION_STRING "11.3.2"
/**
* HB_VERSION_ATLEAST:

View file

@ -314,6 +314,10 @@
#endif
#endif
#ifndef HB_HOT
#define HB_HOT __attribute__((hot))
#endif
/*
* Borrowed from https://bugzilla.mozilla.org/show_bug.cgi?id=1215411
* HB_FALLTHROUGH is an annotation to suppress compiler warnings about switch