[VARC] Add loads of debug messages and misc changes To be reverted.
diff --git a/src/OT/Var/VARC/VARC.cc b/src/OT/Var/VARC/VARC.cc index 7524686..888968c 100644 --- a/src/OT/Var/VARC/VARC.cc +++ b/src/OT/Var/VARC/VARC.cc
@@ -10,6 +10,28 @@ //namespace Var { +static constexpr uint32_t DEBUG_VARC_TRACE_VAR_IDX = 22478849; + +static void +debug_trace_coords (hb_codepoint_t parent_gid, + hb_codepoint_t gid, + uint32_t var_idx, + hb_array_t<const float> coords_f32) +{ + if (var_idx != DEBUG_VARC_TRACE_VAR_IDX) + return; + + fprintf (stderr, + "VARC_COORDS parent_gid=%u gid=%u var_idx=%u len=%u", + (unsigned) parent_gid, + (unsigned) gid, + (unsigned) var_idx, + (unsigned) coords_f32.length); + for (unsigned i = 0; i < coords_f32.length; i++) + fprintf (stderr, " %u:%.16f/%d", i, (double) coords_f32[i], (int) roundf (coords_f32[i])); + fprintf (stderr, "\n"); +} + #ifndef HB_NO_DRAW @@ -19,9 +41,164 @@ hb_draw_funcs_t *dfuncs; void *data; hb_draw_state_t *st; + float draw_x_scale; + float draw_y_scale; + bool trace_commands; + hb_codepoint_t parent_gid; + hb_codepoint_t gid; + uint32_t var_idx; + unsigned seq; }; static void +debug_trace_cmd_point (const hb_transforming_pen_context_t *c, + const char *phase, + unsigned seq, + const char *op, + float x, + float y) +{ + if (!c->trace_commands) + return; + + fprintf (stderr, + "VARC_CMD phase=%s parent_gid=%u gid=%u var_idx=%u seq=%u op=%s x_raw_26_6=%.16f y_raw_26_6=%.16f x_norm=%.16f y_norm=%.16f x_cmp=%.16f y_cmp=%.16f x_after_draw=%.16f y_after_draw=%.16f x_unscaled=%.16f y_unscaled=%.16f\n", + phase, + (unsigned) c->parent_gid, + (unsigned) c->gid, + (unsigned) c->var_idx, + seq, + op, + (double) (x * 64.0f), + (double) (y * 64.0f), + (double) x, + (double) y, + (double) x, + (double) y, + (double) (x * c->draw_x_scale), + (double) (y * c->draw_y_scale), + (double) (c->draw_x_scale ? (x / c->draw_x_scale) : 0.f), + (double) (c->draw_y_scale ? (y / c->draw_y_scale) : 0.f)); +} + +static void +debug_trace_cmd_quad (const hb_transforming_pen_context_t *c, + const char *phase, + unsigned seq, + const char *op, + float cx0, + float cy0, + float x, + float y) +{ + if (!c->trace_commands) + return; + + fprintf (stderr, + "VARC_CMD phase=%s parent_gid=%u gid=%u var_idx=%u seq=%u op=%s cx0_raw_26_6=%.16f cy0_raw_26_6=%.16f x_raw_26_6=%.16f y_raw_26_6=%.16f cx0_norm=%.16f cy0_norm=%.16f x_norm=%.16f y_norm=%.16f cx0_cmp=%.16f cy0_cmp=%.16f x_cmp=%.16f y_cmp=%.16f cx0_after_draw=%.16f cy0_after_draw=%.16f x_after_draw=%.16f y_after_draw=%.16f cx0_unscaled=%.16f cy0_unscaled=%.16f x_unscaled=%.16f y_unscaled=%.16f\n", + phase, + (unsigned) c->parent_gid, + (unsigned) c->gid, + (unsigned) c->var_idx, + seq, + op, + (double) (cx0 * 64.0f), + (double) (cy0 * 64.0f), + (double) (x * 64.0f), + (double) (y * 64.0f), + (double) cx0, + (double) cy0, + (double) x, + (double) y, + (double) cx0, + (double) cy0, + (double) x, + (double) y, + (double) (cx0 * c->draw_x_scale), + (double) (cy0 * c->draw_y_scale), + (double) (x * c->draw_x_scale), + (double) (y * c->draw_y_scale), + (double) (c->draw_x_scale ? (cx0 / c->draw_x_scale) : 0.f), + (double) (c->draw_y_scale ? (cy0 / c->draw_y_scale) : 0.f), + (double) (c->draw_x_scale ? (x / c->draw_x_scale) : 0.f), + (double) (c->draw_y_scale ? (y / c->draw_y_scale) : 0.f)); +} + +static void +debug_trace_cmd_cubic (const hb_transforming_pen_context_t *c, + const char *phase, + unsigned seq, + const char *op, + float cx0, + float cy0, + float cx1, + float cy1, + float x, + float y) +{ + if (!c->trace_commands) + return; + + fprintf (stderr, + "VARC_CMD phase=%s parent_gid=%u gid=%u var_idx=%u seq=%u op=%s cx0_raw_26_6=%.16f cy0_raw_26_6=%.16f cx1_raw_26_6=%.16f cy1_raw_26_6=%.16f x_raw_26_6=%.16f y_raw_26_6=%.16f cx0_norm=%.16f cy0_norm=%.16f cx1_norm=%.16f cy1_norm=%.16f x_norm=%.16f y_norm=%.16f cx0_cmp=%.16f cy0_cmp=%.16f cx1_cmp=%.16f cy1_cmp=%.16f x_cmp=%.16f y_cmp=%.16f cx0_after_draw=%.16f cy0_after_draw=%.16f cx1_after_draw=%.16f cy1_after_draw=%.16f x_after_draw=%.16f y_after_draw=%.16f cx0_unscaled=%.16f cy0_unscaled=%.16f cx1_unscaled=%.16f cy1_unscaled=%.16f x_unscaled=%.16f y_unscaled=%.16f\n", + phase, + (unsigned) c->parent_gid, + (unsigned) c->gid, + (unsigned) c->var_idx, + seq, + op, + (double) (cx0 * 64.0f), + (double) (cy0 * 64.0f), + (double) (cx1 * 64.0f), + (double) (cy1 * 64.0f), + (double) (x * 64.0f), + (double) (y * 64.0f), + (double) cx0, + (double) cy0, + (double) cx1, + (double) cy1, + (double) x, + (double) y, + (double) cx0, + (double) cy0, + (double) cx1, + (double) cy1, + (double) x, + (double) y, + (double) (cx0 * c->draw_x_scale), + (double) (cy0 * c->draw_y_scale), + (double) (cx1 * c->draw_x_scale), + (double) (cy1 * c->draw_y_scale), + (double) (x * c->draw_x_scale), + (double) (y * c->draw_y_scale), + (double) (c->draw_x_scale ? (cx0 / c->draw_x_scale) : 0.f), + (double) (c->draw_y_scale ? (cy0 / c->draw_y_scale) : 0.f), + (double) (c->draw_x_scale ? (cx1 / c->draw_x_scale) : 0.f), + (double) (c->draw_y_scale ? (cy1 / c->draw_y_scale) : 0.f), + (double) (c->draw_x_scale ? (x / c->draw_x_scale) : 0.f), + (double) (c->draw_y_scale ? (y / c->draw_y_scale) : 0.f)); +} + +static void +debug_trace_cmd_close (const hb_transforming_pen_context_t *c, + const char *phase, + unsigned seq, + const char *op) +{ + if (!c->trace_commands) + return; + + fprintf (stderr, + "VARC_CMD phase=%s parent_gid=%u gid=%u var_idx=%u seq=%u op=%s\n", + phase, + (unsigned) c->parent_gid, + (unsigned) c->gid, + (unsigned) c->var_idx, + seq, + op); +} + +static void hb_transforming_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED, void *data, hb_draw_state_t *st, @@ -29,8 +206,13 @@ void *user_data HB_UNUSED) { hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + unsigned seq = c->seq++; + float pre_x = to_x; + float pre_y = to_y; + debug_trace_cmd_point (c, "pre", seq, "M", pre_x, pre_y); c->transform.transform_point (to_x, to_y); + debug_trace_cmd_point (c, "post", seq, "M", to_x, to_y); c->dfuncs->move_to (c->data, *c->st, to_x, to_y); } @@ -43,8 +225,13 @@ void *user_data HB_UNUSED) { hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + unsigned seq = c->seq++; + float pre_x = to_x; + float pre_y = to_y; + debug_trace_cmd_point (c, "pre", seq, "L", pre_x, pre_y); c->transform.transform_point (to_x, to_y); + debug_trace_cmd_point (c, "post", seq, "L", to_x, to_y); c->dfuncs->line_to (c->data, *c->st, to_x, to_y); } @@ -58,9 +245,16 @@ void *user_data HB_UNUSED) { hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + unsigned seq = c->seq++; + float pre_control_x = control_x; + float pre_control_y = control_y; + float pre_to_x = to_x; + float pre_to_y = to_y; + debug_trace_cmd_quad (c, "pre", seq, "Q", pre_control_x, pre_control_y, pre_to_x, pre_to_y); c->transform.transform_point (control_x, control_y); c->transform.transform_point (to_x, to_y); + debug_trace_cmd_quad (c, "post", seq, "Q", control_x, control_y, to_x, to_y); c->dfuncs->quadratic_to (c->data, *c->st, control_x, control_y, to_x, to_y); } @@ -75,10 +269,25 @@ void *user_data HB_UNUSED) { hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + unsigned seq = c->seq++; + float pre_control1_x = control1_x; + float pre_control1_y = control1_y; + float pre_control2_x = control2_x; + float pre_control2_y = control2_y; + float pre_to_x = to_x; + float pre_to_y = to_y; + debug_trace_cmd_cubic (c, "pre", seq, "C", + pre_control1_x, pre_control1_y, + pre_control2_x, pre_control2_y, + pre_to_x, pre_to_y); c->transform.transform_point (control1_x, control1_y); c->transform.transform_point (control2_x, control2_y); c->transform.transform_point (to_x, to_y); + debug_trace_cmd_cubic (c, "post", seq, "C", + control1_x, control1_y, + control2_x, control2_y, + to_x, to_y); c->dfuncs->cubic_to (c->data, *c->st, control1_x, control1_y, control2_x, control2_y, to_x, to_y); } @@ -90,8 +299,11 @@ void *user_data HB_UNUSED) { hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data; + unsigned seq = c->seq++; + debug_trace_cmd_close (c, "pre", seq, "Z"); c->dfuncs->close_path (c->data, *c->st); + debug_trace_cmd_close (c, "post", seq, "Z"); } static inline void free_static_transforming_pen_funcs (); @@ -131,10 +343,11 @@ 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_array_t<const float> coords_f32, hb_transform_t<> total_transform, hb_ubytes_t total_record, - hb_scalar_cache_t *cache) const + hb_scalar_cache_t *cache, + uint32_t trace_var_idx) const { const unsigned char *end = total_record.arrayZ + total_record.length; const unsigned char *record = total_record.arrayZ; @@ -180,11 +393,18 @@ bool show = true; if (flags & (unsigned) flags_t::HAVE_CONDITION) { + hb_vector_t<int> condition_coords_storage; + if (coords_f32.length && unlikely (!condition_coords_storage.resize (coords_f32.length))) + return hb_ubytes_t (); + for (unsigned i = 0; i < coords_f32.length; i++) + condition_coords_storage[i] = (int) roundf (coords_f32[i]); + hb_array_t<const int> condition_coords = condition_coords_storage.as_array (); + unsigned conditionIndex; READ_UINT32VAR (conditionIndex); const auto &condition = (&VARC+VARC.conditionList)[conditionIndex]; - auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache); - show = condition.evaluate (coords.arrayZ, coords.length, &instancer); + auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, condition_coords, coords_f32, cache); + show = condition.evaluate (condition_coords.arrayZ, condition_coords.length, &instancer); } // Axis values @@ -209,16 +429,24 @@ { uint32_t axisValuesVarIdx; READ_UINT32VAR (axisValuesVarIdx); - if (show && coords && !axisValues.in_error ()) - varStore.get_delta (axisValuesVarIdx, coords, axisValues.as_array (), cache); + if (show && coords_f32 && !axisValues.in_error ()) + varStore.get_delta (axisValuesVarIdx, coords_f32, axisValues.as_array (), cache); } - auto component_coords = coords; + hb_vector_t<float> component_coords_f32_storage; + hb_array_t<const float> component_coords_f32 = coords_f32; + /* 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 (c.font->coords, c.font->num_coords); + component_coords_f32.length > HB_VAR_COMPOSITE_MAX_AXES) + { + if (unlikely (!component_coords_f32_storage.resize (c.font->num_coords))) + return hb_ubytes_t (); + for (unsigned i = 0; i < c.font->num_coords; i++) + component_coords_f32_storage[i] = (float) c.font->coords[i]; + component_coords_f32 = component_coords_f32_storage.as_array (); + } // Transform @@ -268,30 +496,94 @@ if (show) { - // Only use coord_setter if there's actually any axis overrides. - coord_setter_t coord_setter (axisIndices ? component_coords : hb_array<int> ()); - for (unsigned i = 0; i < axisIndices.length; i++) - coord_setter[axisIndices[i]] = roundf (axisValues[i]); + // Apply axis overrides in float coordinate space. if (axisIndices) - component_coords = coord_setter.get_coords (); + { + if (component_coords_f32.arrayZ != component_coords_f32_storage.arrayZ) + { + component_coords_f32_storage.clear (); + component_coords_f32_storage.extend (component_coords_f32); + if (unlikely (component_coords_f32_storage.in_error ())) + return hb_ubytes_t (); + component_coords_f32 = component_coords_f32_storage.as_array (); + } + + for (unsigned i = 0; i < axisIndices.length; i++) + { + unsigned axis = axisIndices[i]; + if (axis >= HB_VAR_COMPOSITE_MAX_AXES) + continue; + if (unlikely (component_coords_f32_storage.length <= axis && + !component_coords_f32_storage.resize (axis + 1))) + return hb_ubytes_t (); + component_coords_f32_storage[axis] = axisValues[i]; + } + component_coords_f32 = component_coords_f32_storage.as_array (); + } // Apply transform variations if any - if (transformVarIdx != VarIdx::NO_VARIATION && coords) + if (transformVarIdx != VarIdx::NO_VARIATION && coords_f32) { + debug_trace_coords (parent_gid, gid, transformVarIdx, coords_f32); + + float rotation_pre_raw = transform.rotation; float transformValues[9]; + unsigned rotationIndex = (unsigned) -1; + if (flags & (unsigned) flags_t::HAVE_ROTATION) + { + rotationIndex = 0; + if (flags & (unsigned) flags_t::HAVE_TRANSLATE_X) rotationIndex++; + if (flags & (unsigned) flags_t::HAVE_TRANSLATE_Y) rotationIndex++; + } unsigned numTransformValues = 0; #define PROCESS_TRANSFORM_COMPONENT(shift, 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); + varStore.get_delta (transformVarIdx, coords_f32, hb_array (transformValues, numTransformValues), cache); + if (rotationIndex < numTransformValues) + varStore.debug_rotation_region_terms (transformVarIdx, + coords_f32.arrayZ, coords_f32.length, + numTransformValues, + rotationIndex, + parent_gid, + gid, + cache); numTransformValues = 0; #define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \ if (flags & (unsigned) flags_t::flag) \ transform.name = transformValues[numTransformValues++]; PROCESS_TRANSFORM_COMPONENTS; #undef PROCESS_TRANSFORM_COMPONENT + if (rotationIndex != (unsigned) -1) + fprintf (stderr, + "VARC_ROT parent_gid=%u gid=%u var_idx=%u pre_raw=%.16f pre=%.16f delta_raw=%.16f delta=%.16f post_raw=%.16f post=%.16f\n", + (unsigned) parent_gid, + (unsigned) gid, + (unsigned) transformVarIdx, + (double) rotation_pre_raw, + (double) (rotation_pre_raw * (1.f / (1 << 12))), + (double) (transform.rotation - rotation_pre_raw), + (double) ((transform.rotation - rotation_pre_raw) * (1.f / (1 << 12))), + (double) transform.rotation, + (double) (transform.rotation * (1.f / (1 << 12)))); + + if (transformVarIdx == DEBUG_VARC_TRACE_VAR_IDX) + fprintf (stderr, + "VARC_XFORM parent_gid=%u gid=%u var_idx=%u tx=%.16f ty=%.16f rot_raw=%.16f sx_raw=%.16f sy_raw=%.16f skx_raw=%.16f sky_raw=%.16f cx=%.16f cy=%.16f\n", + (unsigned) parent_gid, + (unsigned) gid, + (unsigned) transformVarIdx, + (double) transform.translateX, + (double) transform.translateY, + (double) transform.rotation, + (double) transform.scaleX, + (double) transform.scaleY, + (double) transform.skewX, + (double) transform.skewY, + (double) transform.tCenterX, + (double) transform.tCenterY); } // Divide them by their divisors @@ -308,16 +600,61 @@ transform.skewX *= HB_PI; transform.skewY *= HB_PI; - total_transform.transform (transform.to_transform ()); + if (transform.rotation != 0) + { + float s, c; + hb_sincos (transform.rotation, s, c); + fprintf (stderr, + "VARC_TRIG kind=rotate in=%.16f sin=%.16f cos=%.16f\n", + (double) transform.rotation, + (double) s, + (double) c); + } + if (transform.skewX != 0 || transform.skewY != 0) + { + float skew_x = -transform.skewX; + float skew_y = transform.skewY; + float tan_x = skew_x ? tanf (skew_x) : 0; + float tan_y = skew_y ? tanf (skew_y) : 0; + fprintf (stderr, + "VARC_TRIG kind=skew in_x=%.16f in_y=%.16f tan_x=%.16f tan_y=%.16f\n", + (double) skew_x, + (double) skew_y, + (double) tan_x, + (double) tan_y); + } - bool same_coords = component_coords.length == coords.length && - component_coords.arrayZ == coords.arrayZ; + hb_transform_t<> local_transform = transform.to_transform (); + hb_transform_t<> combined_transform = total_transform; + combined_transform.transform (local_transform); + if (transformVarIdx == DEBUG_VARC_TRACE_VAR_IDX) + fprintf (stderr, + "VARC_AFFINE parent_gid=%u gid=%u var_idx=%u xx=%.16f yx=%.16f xy=%.16f yy=%.16f x0=%.16f y0=%.16f\n", + (unsigned) parent_gid, + (unsigned) gid, + (unsigned) transformVarIdx, + (double) combined_transform.xx, + (double) combined_transform.yx, + (double) combined_transform.xy, + (double) combined_transform.yy, + (double) combined_transform.x0, + (double) combined_transform.y0); + total_transform = combined_transform; + + uint32_t child_trace_var_idx = trace_var_idx; + if (transformVarIdx == DEBUG_VARC_TRACE_VAR_IDX) + child_trace_var_idx = transformVarIdx; + + bool same_coords = component_coords_f32.length == coords_f32.length && + component_coords_f32.arrayZ == coords_f32.arrayZ; c.depth_left--; VARC.get_path_at (c, gid, - component_coords, total_transform, + component_coords_f32, + total_transform, parent_gid, - same_coords ? cache : nullptr); + same_coords ? cache : nullptr, + child_trace_var_idx); c.depth_left++; } @@ -330,10 +667,11 @@ bool VARC::get_path_at (const hb_varc_context_t &c, hb_codepoint_t glyph, - hb_array_t<const int> coords, + hb_array_t<const float> coords_f32, hb_transform_t<> transform, hb_codepoint_t parent_glyph, - hb_scalar_cache_t *parent_cache) const + hb_scalar_cache_t *parent_cache, + uint32_t trace_var_idx) const { // Don't recurse on the same glyph. unsigned idx = glyph == parent_glyph ? @@ -341,20 +679,60 @@ (this+coverage).get_coverage (glyph); if (idx == NOT_COVERED) { + hb_vector_t<int> coords_storage; + if (coords_f32.length && unlikely (!coords_storage.resize (coords_f32.length))) + return false; + for (unsigned i = 0; i < coords_f32.length; i++) + coords_storage[i] = (int) roundf (coords_f32[i]); + hb_array_t<const int> coords = coords_storage.as_array (); + if (c.draw_session) { hb_transform_t<> leaf_transform = transform; leaf_transform.x0 *= c.font->x_multf; leaf_transform.y0 *= c.font->y_multf; + bool trace_commands = trace_var_idx == DEBUG_VARC_TRACE_VAR_IDX; + float draw_x_scale = 1.f; + float draw_y_scale = 1.f; + if (c.font && c.font->parent) + { + if (c.font->parent->x_scale) + draw_x_scale = (float) c.font->x_scale / (float) c.font->parent->x_scale; + if (c.font->parent->y_scale) + draw_y_scale = (float) c.font->y_scale / (float) c.font->parent->y_scale; + } + if (trace_commands) + { + fprintf (stderr, + "VARC_CMD_CTX parent_gid=%u gid=%u var_idx=%u draw_x_scale=%.16f draw_y_scale=%.16f font_x_scale=%d font_y_scale=%d parent_x_scale=%d parent_y_scale=%d\n", + (unsigned) parent_glyph, + (unsigned) glyph, + (unsigned) trace_var_idx, + (double) draw_x_scale, + (double) draw_y_scale, + c.font ? c.font->x_scale : 0, + c.font ? c.font->y_scale : 0, + (c.font && c.font->parent) ? c.font->parent->x_scale : 0, + (c.font && c.font->parent) ? c.font->parent->y_scale : 0); + } // 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 {leaf_transform, c.draw_session->funcs, c.draw_session->draw_data, - &c.draw_session->st}; + &c.draw_session->st, + draw_x_scale, + draw_y_scale, + trace_commands, + parent_glyph, + glyph, + trace_var_idx, + 0}; hb_draw_session_t transformer_session {transformer_funcs, &context}; - hb_draw_session_t &shape_draw_session = leaf_transform.is_identity () ? *c.draw_session : transformer_session; + hb_draw_session_t &shape_draw_session = (!trace_commands && leaf_transform.is_identity ()) + ? *c.draw_session + : transformer_session; if (c.font->face->table.glyf->get_path_at (c.font, glyph, shape_draw_session, coords, c.scratch.glyf_scratch)) return true; #ifndef HB_NO_CFF @@ -398,14 +776,16 @@ hb_scalar_cache_t static_cache; hb_scalar_cache_t *cache = parent_cache ? - parent_cache : - (this+varStore).create_cache (&static_cache); + parent_cache : + (this+varStore).create_cache (&static_cache); VarCompositeGlyph::get_path_at (c, - glyph, - coords, transform, - record, - cache); + glyph, + coords_f32, + transform, + record, + cache, + trace_var_idx); if (cache != parent_cache) (this+varStore).destroy_cache (cache, &static_cache);
diff --git a/src/OT/Var/VARC/VARC.hh b/src/OT/Var/VARC/VARC.hh index 719302c..83a2806 100644 --- a/src/OT/Var/VARC/VARC.hh +++ b/src/OT/Var/VARC/VARC.hh
@@ -8,7 +8,6 @@ #include "../../../hb-ot-cff2-table.hh" #include "../../../hb-ot-cff1-table.hh" -#include "coord-setter.hh" namespace OT { @@ -64,10 +63,11 @@ HB_INTERNAL hb_ubytes_t get_path_at (const hb_varc_context_t &c, hb_codepoint_t parent_gid, - hb_array_t<const int> coords, + hb_array_t<const float> coords_f32, hb_transform_t<> transform, hb_ubytes_t record, - hb_scalar_cache_t *cache = nullptr) const; + hb_scalar_cache_t *cache = nullptr, + uint32_t trace_var_idx = VarIdx::NO_VARIATION) const; }; struct VarCompositeGlyph @@ -75,19 +75,21 @@ static void get_path_at (const hb_varc_context_t &c, hb_codepoint_t gid, - hb_array_t<const int> coords, + hb_array_t<const float> coords_f32, hb_transform_t<> transform, hb_ubytes_t record, - hb_scalar_cache_t *cache) + hb_scalar_cache_t *cache, + uint32_t trace_var_idx) { while (record) { const VarComponent &comp = * (const VarComponent *) (record.arrayZ); record = comp.get_path_at (c, gid, - coords, transform, + coords_f32, transform, record, - cache); + cache, + trace_var_idx); } } }; @@ -103,10 +105,11 @@ HB_INTERNAL bool get_path_at (const hb_varc_context_t &c, hb_codepoint_t gid, - hb_array_t<const int> coords, + hb_array_t<const float> coords_f32, hb_transform_t<> transform = HB_TRANSFORM_IDENTITY, hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID, - hb_scalar_cache_t *parent_cache = nullptr) const; + hb_scalar_cache_t *parent_cache = nullptr, + uint32_t trace_var_idx = VarIdx::NO_VARIATION) const; bool get_path (hb_font_t *font, @@ -122,8 +125,14 @@ HB_MAX_NESTING_LEVEL, scratch}; + hb_vector_t<float> coords_f32; + if (font->num_coords && unlikely (!coords_f32.resize (font->num_coords))) + return false; + for (unsigned i = 0; i < font->num_coords; i++) + coords_f32[i] = (float) font->coords[i]; + return get_path_at (c, gid, - hb_array (font->coords, font->num_coords)); + coords_f32.as_array ()); } bool @@ -140,8 +149,14 @@ HB_MAX_NESTING_LEVEL, scratch}; + hb_vector_t<float> coords_f32; + if (font->num_coords && unlikely (!coords_f32.resize (font->num_coords))) + return false; + for (unsigned i = 0; i < font->num_coords; i++) + coords_f32[i] = (float) font->coords[i]; + return get_path_at (c, gid, - hb_array (font->coords, font->num_coords)); + coords_f32.as_array ()); } bool sanitize (hb_sanitize_context_t *c) const
diff --git a/src/hb-open-type.hh b/src/hb-open-type.hh index e7c62cf..71982ce 100644 --- a/src/hb-open-type.hh +++ b/src/hb-open-type.hh
@@ -1963,6 +1963,14 @@ } private: + HB_ALWAYS_INLINE + static float mul_unfused (float a, float b) + { + // Keep mul/add unfused for cross-engine numeric debugging. + volatile float product = a * b; + return product; + } + template <bool scaled> void _add_to (hb_array_t<float> out, float scale = 1.0f) { @@ -1988,14 +1996,14 @@ #ifndef HB_OPTIMIZE_SIZE for (; j + 3 < count; j += 4) { - *arrayZ++ += scaled ? *pp++ * scale : *pp++; - *arrayZ++ += scaled ? *pp++ * scale : *pp++; - *arrayZ++ += scaled ? *pp++ * scale : *pp++; - *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; } #endif for (; j < count; j++) - *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; p = (const unsigned char *) pp; } @@ -2007,14 +2015,14 @@ #ifndef HB_OPTIMIZE_SIZE for (; j + 3 < count; j += 4) { - *arrayZ++ += scaled ? *pp++ * scale : *pp++; - *arrayZ++ += scaled ? *pp++ * scale : *pp++; - *arrayZ++ += scaled ? *pp++ * scale : *pp++; - *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; } #endif for (; j < count; j++) - *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; p = (const unsigned char *) pp; } @@ -2023,7 +2031,7 @@ { const auto *pp = (const HBINT32 *) p; for (unsigned j = 0; j < count; j++) - *arrayZ++ += scaled ? *pp++ * scale : *pp++; + *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++; p = (const unsigned char *) pp; }
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh index 0547301..d22addb 100644 --- a/src/hb-ot-layout-common.hh +++ b/src/hb-ot-layout-common.hh
@@ -2467,30 +2467,36 @@ struct VarRegionAxis { - float evaluate (int coord) const + float evaluate (float coord) const { - int peak = peakCoord.to_int (); - if (peak == 0 || coord == peak) + float peak = (float) peakCoord.to_int (); + if (peak == 0.f || coord == peak) return 1.f; - else if (coord == 0) // Faster + else if (coord == 0.f) // Faster return 0.f; - int start = startCoord.to_int (), end = endCoord.to_int (); + float start = (float) startCoord.to_int (); + float end = (float) endCoord.to_int (); /* TODO Move these to sanitize(). */ if (unlikely (start > peak || peak > end)) return 1.f; - if (unlikely (start < 0 && end > 0 && peak != 0)) + if (unlikely (start < 0.f && end > 0.f && peak != 0.f)) return 1.f; if (coord <= start || end <= coord) return 0.f; - /* Interpolate */ + /* Interpolate in raw 2.14 space. */ if (coord < peak) - return float (coord - start) / (peak - start); + return (coord - start) / (peak - start); else - return float (end - coord) / (end - peak); + return (end - coord) / (end - peak); + } + + float evaluate (int coord) const + { + return evaluate ((float) coord); } bool sanitize (hb_sanitize_context_t *c) const @@ -2521,6 +2527,13 @@ return axis.evaluate (coord); } + float evaluate (const float *coords, unsigned int coord_len) const + { + unsigned i = axisIndex; + float coord = i < coord_len ? coords[i] : 0.f; + return axis.evaluate (coord); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -2813,7 +2826,21 @@ { float factor = arrayZ[i].evaluate (coords, coord_len); if (factor == 0.f) - return 0.; + return 0.f; + v *= factor; + } + return v; + } + + float evaluate (const float *coords, unsigned int coord_len) const + { + float v = 1.f; + unsigned int count = len; + for (unsigned int i = 0; i < count; i++) + { + float factor = arrayZ[i].evaluate (coords, coord_len); + if (factor == 0.f) + return 0.f; v *= factor; } return v; @@ -2824,8 +2851,8 @@ { HB_ALWAYS_INLINE float evaluate (unsigned int region_index, - const int *coords, unsigned int coord_len, - hb_scalar_cache_t *cache = nullptr) const + const int *coords, unsigned int coord_len, + hb_scalar_cache_t *cache = nullptr) const { if (unlikely (region_index >= regions.len)) return 0.; @@ -2843,6 +2870,34 @@ return v; } + HB_ALWAYS_INLINE + float evaluate (unsigned int region_index, + const float *coords, unsigned int coord_len, + hb_scalar_cache_t *cache = nullptr) const + { + if (unlikely (region_index >= regions.len)) + return 0.; + + float v; + if (cache && cache->get (region_index, &v)) + return v; + + const SparseVariationRegion ®ion = this+regions[region_index]; + + v = region.evaluate (coords, coord_len); + if (cache) + cache->set (region_index, v); + + return v; + } + + float evaluate (unsigned int region_index, + hb_array_t<const float> coords, + hb_scalar_cache_t *cache = nullptr) const + { + return evaluate (region_index, coords.arrayZ, coords.length, cache); + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -3247,37 +3302,116 @@ + StructAfter<CFF2Index> (regionIndices).get_size (); } - void get_delta (unsigned int inner, - const int *coords, unsigned int coord_count, - const SparseVarRegionList ®ions, - hb_array_t<float> out, - hb_scalar_cache_t *cache = nullptr) const + template <typename CoordT> + void get_delta_impl (unsigned int inner, + const CoordT *coords, unsigned int coord_count, + const SparseVarRegionList ®ions, + hb_array_t<float> out, + hb_scalar_cache_t *cache = nullptr) const { auto &deltaSets = StructAfter<decltype (deltaSetsX)> (regionIndices); + if (unlikely (!out.length)) + return; + auto values_iter = deltaSets.fetcher (inner); unsigned regionCount = regionIndices.len; unsigned skip = 0; for (unsigned regionIndex = 0; regionIndex < regionCount; regionIndex++) { float scalar = regions.evaluate (regionIndices.arrayZ[regionIndex], - coords, coord_count, - cache); + coords, coord_count, + cache); // We skip lazily. Helps with the tail end. if (scalar == 0.0f) skip += out.length; else { if (skip) - { - values_iter.skip (skip); - skip = 0; - } - values_iter.add_to (out, scalar); + { + values_iter.skip (skip); + skip = 0; + } + values_iter.add_to (out, scalar); } } } + void get_delta (unsigned int inner, + const int *coords, unsigned int coord_count, + const SparseVarRegionList ®ions, + hb_array_t<float> out, + hb_scalar_cache_t *cache = nullptr) const + { + get_delta_impl (inner, coords, coord_count, regions, out, cache); + } + + void get_delta (unsigned int inner, + const float *coords, unsigned int coord_count, + const SparseVarRegionList ®ions, + hb_array_t<float> out, + hb_scalar_cache_t *cache = nullptr) const + { + get_delta_impl (inner, coords, coord_count, regions, out, cache); + } + + void debug_rotation_region_terms (unsigned int inner, + const float *coords, unsigned int coord_count, + const SparseVarRegionList ®ions, + unsigned int tuple_len, + unsigned int rotation_index, + unsigned int parent_gid, + unsigned int gid, + unsigned int var_idx, + hb_scalar_cache_t *cache = nullptr) const + { + if (unlikely (!tuple_len || rotation_index >= tuple_len || tuple_len > 9)) + return; + + auto &deltaSets = StructAfter<decltype (deltaSetsX)> (regionIndices); + auto values_iter = deltaSets.fetcher (inner); + unsigned regionCount = regionIndices.len; + unsigned skip = 0; + float tmp[9] = {}; + float running_f32 = 0.0f; + double running_f64 = 0.0; + + for (unsigned region_order = 0; region_order < regionCount; region_order++) + { + unsigned region_index = regionIndices.arrayZ[region_order]; + float scalar = regions.evaluate (region_index, coords, coord_count, cache); + if (scalar == 0.0f) + { + skip += tuple_len; + fprintf (stderr, + "VARC_ROT_REGION parent_gid=%u gid=%u var_idx=%u region_order=%u region_idx=%u scalar=%.16f raw=%.16f contrib=%.16f running_f32=%.16f running_f64=%.16f\n", + parent_gid, gid, var_idx, region_order, region_index, + 0.0, 0.0, 0.0, (double) running_f32, running_f64); + continue; + } + + if (skip) + { + values_iter.skip (skip); + skip = 0; + } + + for (unsigned i = 0; i < tuple_len; i++) + tmp[i] = 0.0f; + values_iter.add_to (hb_array (tmp, tuple_len), 1.0f); + + float raw = tmp[rotation_index]; + float contrib = raw * scalar; + running_f32 += contrib; + running_f64 += (double) raw * (double) scalar; + fprintf (stderr, + "VARC_ROT_REGION parent_gid=%u gid=%u var_idx=%u region_order=%u region_idx=%u scalar=%.16f raw=%.16f contrib=%.16f running_f32=%.16f running_f64=%.16f\n", + parent_gid, gid, var_idx, region_order, region_index, + (double) scalar, (double) raw, (double) contrib, + (double) running_f32, running_f64); + } + } + bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -3569,44 +3703,118 @@ } private: - void get_delta (unsigned int outer, unsigned int inner, - const int *coords, unsigned int coord_count, - hb_array_t<float> out, - hb_scalar_cache_t *cache = nullptr) const + template <typename CoordT> + void get_delta_impl (unsigned int outer, unsigned int inner, + const CoordT *coords, unsigned int coord_count, + hb_array_t<float> out, + hb_scalar_cache_t *cache = nullptr) const { #ifdef HB_NO_VAR return; #endif + if (unlikely (!out.length)) + return; + + if (unlikely (!coords && coord_count)) + return; + + if (unlikely (!coords)) + coord_count = 0; + if (unlikely (outer >= dataSets.len)) return; return (this+dataSets[outer]).get_delta (inner, - coords, coord_count, - this+regions, - out, - cache); + coords, coord_count, + this+regions, + out, + cache); + } + + void get_delta (unsigned int outer, unsigned int inner, + const int *coords, unsigned int coord_count, + hb_array_t<float> out, + hb_scalar_cache_t *cache = nullptr) const + { + get_delta_impl (outer, inner, coords, coord_count, out, cache); + } + + void get_delta (unsigned int outer, unsigned int inner, + const float *coords, unsigned int coord_count, + hb_array_t<float> out, + hb_scalar_cache_t *cache = nullptr) const + { + get_delta_impl (outer, inner, coords, coord_count, out, cache); } public: void get_delta (unsigned int index, - const int *coords, unsigned int coord_count, - hb_array_t<float> out, - hb_scalar_cache_t *cache = nullptr) const + const int *coords, unsigned int coord_count, + hb_array_t<float> out, + hb_scalar_cache_t *cache = nullptr) const { unsigned int outer = index >> 16; unsigned int inner = index & 0xFFFF; get_delta (outer, inner, coords, coord_count, out, cache); } + void get_delta (unsigned int index, - hb_array_t<const int> coords, - hb_array_t<float> out, - hb_scalar_cache_t *cache = nullptr) const + const float *coords, unsigned int coord_count, + hb_array_t<float> out, + hb_scalar_cache_t *cache = nullptr) const + { + unsigned int outer = index >> 16; + unsigned int inner = index & 0xFFFF; + get_delta (outer, inner, coords, coord_count, out, cache); + } + + void get_delta (unsigned int index, + hb_array_t<const int> coords, + hb_array_t<float> out, + hb_scalar_cache_t *cache = nullptr) const { return get_delta (index, - coords.arrayZ, coords.length, - out, - cache); + coords.arrayZ, coords.length, + out, + cache); + } + + void get_delta (unsigned int index, + hb_array_t<const float> coords, + hb_array_t<float> out, + hb_scalar_cache_t *cache = nullptr) const + { + return get_delta (index, + coords.arrayZ, coords.length, + out, + cache); + } + + void debug_rotation_region_terms (unsigned int index, + const float *coords, unsigned int coord_count, + unsigned int tuple_len, + unsigned int rotation_index, + unsigned int parent_gid, + unsigned int gid, + hb_scalar_cache_t *cache = nullptr) const + { +#ifdef HB_NO_VAR + return; +#endif + unsigned int outer = index >> 16; + unsigned int inner = index & 0xFFFF; + if (unlikely (!coords && coord_count)) + return; + if (unlikely (!coords)) + coord_count = 0; + if (unlikely (outer >= dataSets.len)) + return; + return (this+dataSets[outer]).debug_rotation_region_terms (inner, + coords, coord_count, + this+regions, + tuple_len, rotation_index, + parent_gid, gid, index, cache); } bool sanitize (hb_sanitize_context_t *c) const @@ -3863,16 +4071,21 @@ struct MultiItemVarStoreInstancer { MultiItemVarStoreInstancer (const MultiItemVariationStore *varStore, - const DeltaSetIndexMap *varIdxMap, - hb_array_t<const int> coords, - hb_scalar_cache_t *cache = nullptr) : - varStore (varStore), varIdxMap (varIdxMap), coords (coords), cache (cache) + const DeltaSetIndexMap *varIdxMap, + hb_array_t<const int> coords, + hb_array_t<const float> coords_f32 = hb_array_t<const float> (), + hb_scalar_cache_t *cache = nullptr) : + varStore (varStore), + varIdxMap (varIdxMap), + coords (coords), + coords_f32 (coords_f32), + cache (cache) { if (!varStore) varStore = &Null(MultiItemVariationStore); } - operator bool () const { return varStore && bool (coords); } + operator bool () const { return varStore && (bool (coords_f32) || bool (coords)); } float operator[] (uint32_t varIdx) const { @@ -3883,12 +4096,15 @@ void operator() (hb_array_t<float> out, uint32_t varIdx, unsigned short offset = 0) const { - if (coords && varIdx != VarIdx::NO_VARIATION) + if ((coords_f32 || coords) && varIdx != VarIdx::NO_VARIATION) { varIdx += offset; if (varIdxMap) - varIdx = varIdxMap->map (varIdx); - varStore->get_delta (varIdx, coords, out, cache); + varIdx = varIdxMap->map (varIdx); + if (coords_f32) + varStore->get_delta (varIdx, coords_f32, out, cache); + else + varStore->get_delta (varIdx, coords, out, cache); } else for (unsigned i = 0; i < out.length; i++) @@ -3898,6 +4114,7 @@ const MultiItemVariationStore *varStore; const DeltaSetIndexMap *varIdxMap; hb_array_t<const int> coords; + hb_array_t<const float> coords_f32; hb_scalar_cache_t *cache; };