#include "VARC.hh" #ifndef HB_NO_VAR_COMPOSITES #include "../../../hb-draw.hh" #include "../../../hb-geometry.hh" #include "../../../hb-ot-layout-common.hh" #include "../../../hb-ot-layout-gdef-table.hh" namespace OT { //namespace Var { struct hb_transforming_pen_context_t { hb_transform_t transform; hb_draw_funcs_t *dfuncs; void *data; hb_draw_state_t *st; }; static void hb_transforming_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, void *data, hb_draw_state_t *st, float to_x, float to_y, void *user_data HB_UNUSED) { hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; c->transform.transform_point (to_x, to_y); c->dfuncs->move_to (c->data, *c->st, to_x, to_y); } static void hb_transforming_pen_line_to (hb_draw_funcs_t *dfuncs HB_UNUSED, void *data, hb_draw_state_t *st, float to_x, float to_y, void *user_data HB_UNUSED) { hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; c->transform.transform_point (to_x, to_y); c->dfuncs->line_to (c->data, *c->st, to_x, to_y); } static void hb_transforming_pen_quadratic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, void *data, hb_draw_state_t *st, float control_x, float control_y, float to_x, float to_y, void *user_data HB_UNUSED) { hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; c->transform.transform_point (control_x, control_y); c->transform.transform_point (to_x, to_y); c->dfuncs->quadratic_to (c->data, *c->st, control_x, control_y, to_x, to_y); } static void hb_transforming_pen_cubic_to (hb_draw_funcs_t *dfuncs HB_UNUSED, void *data, hb_draw_state_t *st, float control1_x, float control1_y, float control2_x, float control2_y, float to_x, float to_y, void *user_data HB_UNUSED) { hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; c->transform.transform_point (control1_x, control1_y); c->transform.transform_point (control2_x, control2_y); c->transform.transform_point (to_x, to_y); c->dfuncs->cubic_to (c->data, *c->st, control1_x, control1_y, control2_x, control2_y, to_x, to_y); } static void hb_transforming_pen_close_path (hb_draw_funcs_t *dfuncs HB_UNUSED, void *data, hb_draw_state_t *st, void *user_data HB_UNUSED) { hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; c->dfuncs->close_path (c->data, *c->st); } static inline void free_static_transforming_pen_funcs (); static struct hb_transforming_pen_funcs_lazy_loader_t : hb_draw_funcs_lazy_loader_t { static hb_draw_funcs_t *create () { hb_draw_funcs_t *funcs = hb_draw_funcs_create (); hb_draw_funcs_set_move_to_func (funcs, hb_transforming_pen_move_to, nullptr, nullptr); hb_draw_funcs_set_line_to_func (funcs, hb_transforming_pen_line_to, nullptr, nullptr); hb_draw_funcs_set_quadratic_to_func (funcs, hb_transforming_pen_quadratic_to, nullptr, nullptr); hb_draw_funcs_set_cubic_to_func (funcs, hb_transforming_pen_cubic_to, nullptr, nullptr); hb_draw_funcs_set_close_path_func (funcs, hb_transforming_pen_close_path, nullptr, nullptr); hb_draw_funcs_make_immutable (funcs); hb_atexit (free_static_transforming_pen_funcs); return funcs; } } static_transforming_pen_funcs; static inline void free_static_transforming_pen_funcs () { static_transforming_pen_funcs.free_instance (); } static hb_draw_funcs_t * hb_transforming_pen_get_funcs () { return static_transforming_pen_funcs.get_unconst (); } hb_ubytes_t VarComponent::get_path_at (hb_font_t *font, hb_codepoint_t parent_gid, hb_draw_session_t &draw_session, hb_array_t coords, hb_ubytes_t total_record, hb_set_t *visited, signed *edges_left, signed depth_left, VarRegionList::cache_t *cache) const { const unsigned char *end = total_record.arrayZ + total_record.length; const unsigned char *record = total_record.arrayZ; auto &VARC = *font->face->table.VARC; auto &varStore = &VARC+VARC.varStore; auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache); #define READ_UINT32VAR(name) \ HB_STMT_START { \ if (unlikely (unsigned (end - record) < HBUINT32VAR::min_size)) return hb_ubytes_t (); \ hb_barrier (); \ auto &varint = * (const HBUINT32VAR *) record; \ unsigned size = varint.get_size (); \ if (unlikely (unsigned (end - record) < size)) return hb_ubytes_t (); \ name = (uint32_t) varint; \ record += size; \ } HB_STMT_END uint32_t flags; READ_UINT32VAR (flags); // gid hb_codepoint_t gid = 0; if (flags & (unsigned) flags_t::GID_IS_24BIT) { if (unlikely (unsigned (end - record) < HBGlyphID24::static_size)) return hb_ubytes_t (); hb_barrier (); gid = * (const HBGlyphID24 *) record; record += HBGlyphID24::static_size; } else { if (unlikely (unsigned (end - record) < HBGlyphID16::static_size)) return hb_ubytes_t (); hb_barrier (); gid = * (const HBGlyphID16 *) record; record += HBGlyphID16::static_size; } // Condition bool show = true; if (flags & (unsigned) flags_t::HAVE_CONDITION) { unsigned conditionIndex; READ_UINT32VAR (conditionIndex); const auto &condition = (&VARC+VARC.conditionList)[conditionIndex]; show = condition.evaluate (coords.arrayZ, coords.length, &instancer); } // Axis values hb_vector_t axisIndices; hb_vector_t axisValues; if (flags & (unsigned) flags_t::HAVE_AXES) { unsigned axisIndicesIndex; READ_UINT32VAR (axisIndicesIndex); axisIndices = (&VARC+VARC.axisIndicesList)[axisIndicesIndex]; axisValues.resize (axisIndices.length); const HBUINT8 *p = (const HBUINT8 *) record; TupleValues::decompile (p, axisValues, (const HBUINT8 *) end); record += (const unsigned char *) p - record; } // Apply variations if any if (flags & (unsigned) flags_t::AXIS_VALUES_HAVE_VARIATION) { uint32_t axisValuesVarIdx; READ_UINT32VAR (axisValuesVarIdx); if (show && coords && !axisValues.in_error ()) varStore.get_delta (axisValuesVarIdx, coords, axisValues.as_array (), cache); } auto component_coords = coords; /* Copying coords is expensive; so we have put an arbitrary * 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 (font->coords, font->num_coords); // Transform uint32_t transformVarIdx = VarIdx::NO_VARIATION; if (flags & (unsigned) flags_t::TRANSFORM_HAS_VARIATION) READ_UINT32VAR (transformVarIdx); #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); \ } HB_STMT_END hb_transform_decomposed_t transform; // Read transform components #define PROCESS_TRANSFORM_COMPONENT(type, 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; \ record += HBINT16::static_size; \ } PROCESS_TRANSFORM_COMPONENTS; #undef PROCESS_TRANSFORM_COMPONENT // Read reserved records unsigned i = flags & (unsigned) flags_t::RESERVED_MASK; while (i) { HB_UNUSED uint32_t discard; READ_UINT32VAR (discard); i &= i - 1; } /* Parsing is over now. */ if (show) { // Only use coord_setter if there's actually any axis overrides. coord_setter_t coord_setter (axisIndices ? component_coords : hb_array ()); // Go backwards, to reduce coord_setter vector reallocations. for (unsigned i = axisIndices.length; i; i--) coord_setter[axisIndices[i - 1]] = axisValues[i - 1]; if (axisIndices) component_coords = coord_setter.get_coords (); // Apply transform variations if any if (transformVarIdx != VarIdx::NO_VARIATION && coords) { float transformValues[9]; unsigned numTransformValues = 0; #define PROCESS_TRANSFORM_COMPONENT(type, flag, name) \ if (flags & (unsigned) flags_t::flag) \ transformValues[numTransformValues++] = transform.name; 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) \ if (flags & (unsigned) flags_t::flag) \ transform.name = transformValues[numTransformValues++]; PROCESS_TRANSFORM_COMPONENTS; #undef PROCESS_TRANSFORM_COMPONENT } // Divide them by their divisors #define PROCESS_TRANSFORM_COMPONENT(type, flag, name) \ if (flags & (unsigned) flags_t::flag) \ { \ HBINT16 int_v; \ int_v = roundf (transform.name); \ type typed_v = * (const type *) &int_v; \ float float_v = (float) typed_v; \ transform.name = float_v; \ } PROCESS_TRANSFORM_COMPONENTS; #undef PROCESS_TRANSFORM_COMPONENT if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y)) transform.scaleY = transform.scaleX; // Scale the transform by the font's scale float x_scale = font->x_multf; float y_scale = font->y_multf; transform.translateX *= x_scale; transform.translateY *= y_scale; transform.tCenterX *= x_scale; transform.tCenterY *= y_scale; // Build a transforming pen to apply the transform. hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs (); hb_transforming_pen_context_t context {transform.to_transform (), draw_session.funcs, draw_session.draw_data, &draw_session.st}; hb_draw_session_t transformer_session {transformer_funcs, &context}; VARC.get_path_at (font, gid, transformer_session, component_coords, parent_gid, visited, edges_left, depth_left - 1); } #undef PROCESS_TRANSFORM_COMPONENTS #undef READ_UINT32VAR return hb_ubytes_t (record, end - record); } //} // namespace Var } // namespace OT #endif