| /* | |
| * Copyright © 2020 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. | |
| * | |
| */ | |
| #include "hb-ot-var-common.hh" | |
| #include "hb-ot-var-hvar-table.hh" | |
| // HVAR table data from SourceSerif4Variable-Roman_subset.otf | |
| const char hvar_data[] = "\x0\x1\x0\x0\x0\x0\x0\x14\x0\x0\x0\xc4\x0\x0\x0\x0\x0\x0\x0\x0\x0\x1\x0\x0\x0\x10\x0\x2\x0\x0\x0\x74\x0\x0\x0\x7a\x0\x2\x0\x8\xc0\x0\xc0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x40\x0\x40\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\xc0\x0\xc0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x0\x40\x0\x40\x0\xc0\x0\xc0\x0\x0\x0\xc0\x0\xc0\x0\x0\x0\xc0\x0\xc0\x0\x0\x0\x0\x0\x40\x0\x40\x0\x0\x0\x40\x0\x40\x0\xc0\x0\xc0\x0\x0\x0\x0\x0\x40\x0\x40\x0\x0\x0\x40\x0\x40\x0\x0\x1\x0\x0\x0\x0\x0\x4\x0\x0\x0\x8\x0\x0\x0\x1\x0\x2\x0\x3\x0\x4\x0\x5\x0\x6\x0\x7\xf9\xf\x2f\xbf\xfb\xfb\x35\xf9\x4\x4\xf3\xb4\xf2\xfb\x2e\xf3\x4\x4\xe\xad\xfa\x1\x1a\x1\x15\x22\x59\xd6\xe3\xf6\x6\xf5\x0\x1\x0\x5\x0\x4\x7\x5\x6"; | |
| static void | |
| test_item_variations () | |
| { | |
| const OT::HVAR* hvar_table = reinterpret_cast<const OT::HVAR*> (hvar_data); | |
| hb_tag_t axis_tag = HB_TAG ('w', 'g', 'h', 't'); | |
| hb_map_t axis_idx_tag_map; | |
| axis_idx_tag_map.set (0, axis_tag); | |
| axis_tag = HB_TAG ('o', 'p', 's', 'z'); | |
| axis_idx_tag_map.set (1, axis_tag); | |
| OT::item_variations_t item_vars; | |
| const OT::ItemVariationStore& src_var_store = hvar_table+(hvar_table->varStore); | |
| bool result = item_vars.create_from_item_varstore (src_var_store, axis_idx_tag_map); | |
| hb_always_assert (result); | |
| /* partial instancing wght=300:800 */ | |
| hb_hashmap_t<hb_tag_t, Triple> normalized_axes_location; | |
| normalized_axes_location.set (axis_tag, Triple (-0.512817, 0.0, 0.7000120)); | |
| hb_hashmap_t<hb_tag_t, TripleDistances> axes_triple_distances; | |
| axes_triple_distances.set (axis_tag, TripleDistances (200.0, 500.0)); | |
| result = item_vars.instantiate_tuple_vars (normalized_axes_location, axes_triple_distances); | |
| hb_always_assert (result); | |
| result = item_vars.as_item_varstore (false); | |
| hb_always_assert (result); | |
| hb_always_assert (item_vars.get_region_list().length == 8); | |
| } | |
| static void | |
| test_implicit_advance_mapping () | |
| { | |
| char data[sizeof (hvar_data)]; | |
| hb_memcpy (data, hvar_data, sizeof (data)); | |
| auto *hvar = reinterpret_cast<OT::HVAR *> (data); | |
| const auto &store = hvar + hvar->varStore; | |
| const int locations[] = {-16384, 0, 16384}; | |
| bool exercised_outer_one = false; | |
| for (int weight : locations) | |
| for (int size : locations) | |
| { | |
| int coords[] = {weight, size}; | |
| unsigned mapped = (hvar + hvar->advMap).map (0x10000u); | |
| hb_always_assert (hvar->get_advance_delta_unscaled (0x10000u, coords, 2) == | |
| store.get_delta (mapped, coords, 2)); | |
| } | |
| hvar->advMap = 0; | |
| for (int weight : locations) | |
| for (int size : locations) | |
| { | |
| int coords[] = {weight, size}; | |
| exercised_outer_one |= store.get_delta (0x10000u, coords, 2) != 0; | |
| hb_always_assert (hvar->get_advance_delta_unscaled (0, coords, 2) == | |
| store.get_delta (0, coords, 2)); | |
| hb_always_assert (hvar->get_advance_delta_unscaled (0xFFFFu, coords, 2) == 0); | |
| hb_always_assert (hvar->get_advance_delta_unscaled (0x10000u, coords, 2) == 0); | |
| hb_always_assert (hvar->get_advance_delta_unscaled (0x10001u, coords, 2) == 0); | |
| } | |
| hb_always_assert (exercised_outer_one); | |
| } | |
| static void | |
| test_item_variations_overflow () | |
| { | |
| hb_vector_t<hb_vector_t<int>> row_storage; | |
| hb_hashmap_t<unsigned, const hb_vector_t<int>*> front_mapping; | |
| OT::item_variations_t item_vars; | |
| unsigned old_varidx = 0; | |
| const unsigned total_count = 0xFFFFu * 2u; | |
| const unsigned counts[] = { | |
| 128u, /* 0..127: 1-byte deltas */ | |
| 32768u - 128u, /* 128..32767: 2-byte deltas */ | |
| 0xFFFFu * 2u - 32768u /* 32768..131069: 4-byte deltas */ | |
| }; | |
| const int starts[] = { | |
| 0, | |
| 128, | |
| 32768 | |
| }; | |
| hb_always_assert (row_storage.alloc (total_count)); | |
| for (unsigned bucket = 0; bucket < 3; bucket++) | |
| { | |
| hb_vector_t<const hb_vector_t<int>*> rows; | |
| hb_always_assert (rows.alloc (counts[bucket])); | |
| for (unsigned i = 0; i < counts[bucket]; i++) | |
| { | |
| hb_vector_t<int> row; | |
| hb_always_assert (row.resize (1)); | |
| row[0] = starts[bucket] + i; | |
| row_storage.push (std::move (row)); | |
| } | |
| hb_always_assert (!row_storage.in_error ()); | |
| unsigned base = row_storage.length - counts[bucket]; | |
| for (unsigned i = 0; i < counts[bucket]; i++) | |
| { | |
| const hb_vector_t<int> *row = &row_storage.arrayZ[base + i]; | |
| rows.push (row); | |
| front_mapping.set (old_varidx++, row); | |
| } | |
| hb_always_assert (!rows.in_error ()); | |
| hb_always_assert (item_vars.add_vardata_encoding_for_testing (std::move (rows), 1)); | |
| } | |
| hb_always_assert (!front_mapping.in_error ()); | |
| hb_always_assert (item_vars.compile_varidx_map_for_testing (front_mapping)); | |
| const auto& encodings = item_vars.get_vardata_encodings (); | |
| hb_always_assert (encodings.length == 4); | |
| hb_always_assert (encodings[0].items.length == 128u); | |
| hb_always_assert (encodings[1].items.length == 32768u - 128u); | |
| hb_always_assert (encodings[2].items.length == 0xFFFFu); | |
| hb_always_assert (encodings[3].items.length == 32767u); | |
| const hb_map_t& varidx_map = item_vars.get_varidx_map (); | |
| hb_always_assert (varidx_map.get (0) == 0); | |
| hb_always_assert (varidx_map.get (127) == 127); | |
| hb_always_assert (varidx_map.get (128) == 0x00010000u); | |
| hb_always_assert (varidx_map.get (32767) == 0x00017F7Fu); | |
| hb_always_assert (varidx_map.get (32768) == 0x00020000u); | |
| hb_always_assert (varidx_map.get (98302) == 0x0002FFFEu); | |
| hb_always_assert (varidx_map.get (98303) == 0x00030000u); | |
| hb_always_assert (varidx_map.get (131069) == 0x00037FFEu); | |
| } | |
| static void | |
| test_signed_delta_widths () | |
| { | |
| const int deltas[] = {32767, 32768, 65535, 65536, | |
| -32768, -32769, -65536, -65537}; | |
| hb_tag_t axis = HB_TAG ('T', 'E', 'S', 'T'); | |
| hb_vector_t<hb_tag_t> axes; | |
| axes.push (axis); | |
| hb_map_t axis_map; | |
| axis_map.set (0, axis); | |
| hb_hashmap_t<hb_tag_t, Triple> region; | |
| region.set (axis, Triple (0, 1, 1)); | |
| hb_vector_t<const hb_hashmap_t<hb_tag_t, Triple> *> regions; | |
| regions.push (®ion); | |
| for (int delta : deltas) | |
| { | |
| hb_vector_t<int> row; | |
| row.push (delta); | |
| hb_vector_t<const hb_vector_t<int> *> rows; | |
| rows.push (&row); | |
| hb_vector_t<OT::delta_row_encoding_t> encodings; | |
| encodings.push (OT::delta_row_encoding_t (std::move (rows), 1)); | |
| char buf[128]; | |
| hb_serialize_context_t s (buf, sizeof (buf)); | |
| auto *out = s.start_serialize<OT::ItemVariationStore> (); | |
| hb_always_assert (out->serialize (&s, true, axes, regions, encodings)); | |
| s.end_serialize (); | |
| hb_bytes_t bytes = s.copy_bytes (); | |
| const auto &store = *reinterpret_cast<const OT::ItemVariationStore *> (bytes.arrayZ); | |
| const auto &data = store.get_sub_table (0); | |
| hb_always_assert (data.get_item_delta (0, 0) == delta); | |
| hb_inc_bimap_t inner_map, region_map; | |
| inner_map.add (0); | |
| region_map.add (0); | |
| char subset_buf[32]; | |
| hb_serialize_context_t subset_s (subset_buf, sizeof (subset_buf)); | |
| auto *subset = subset_s.start_serialize<OT::VarData> (); | |
| hb_always_assert (subset->serialize (&subset_s, &data, inner_map, region_map)); | |
| subset_s.end_serialize (); | |
| hb_bytes_t subset_bytes = subset_s.copy_bytes (); | |
| const auto &subset_data = *reinterpret_cast<const OT::VarData *> (subset_bytes.arrayZ); | |
| hb_always_assert (subset_data.get_item_delta (0, 0) == delta); | |
| OT::item_variations_t item_vars; | |
| hb_always_assert (item_vars.create_from_item_varstore (store, axis_map)); | |
| hb_hashmap_t<hb_tag_t, Triple> locations; | |
| hb_hashmap_t<hb_tag_t, TripleDistances> distances; | |
| hb_always_assert (item_vars.instantiate_tuple_vars (locations, distances)); | |
| hb_always_assert (item_vars.as_item_varstore ()); | |
| hb_always_assert (item_vars.has_long_word () == | |
| (delta < -32768 || delta > 32767)); | |
| subset_bytes.fini (); | |
| bytes.fini (); | |
| } | |
| } | |
| static void | |
| test_accumulated_delta_widths () | |
| { | |
| const int deltas[] = {20000, -20000, 1073741824}; | |
| for (int delta : deltas) | |
| { | |
| OT::item_variations_t item_vars; | |
| unsigned outer = item_vars.add_vardata (1); | |
| item_vars.add_tuple (outer, hb_hashmap_t<hb_tag_t, Triple> (), 0, delta, 1); | |
| item_vars.add_tuple (outer, hb_hashmap_t<hb_tag_t, Triple> (), 0, delta, 1); | |
| hb_always_assert (item_vars.build_region_list ()); | |
| if (delta == 1073741824) | |
| hb_always_assert (!item_vars.as_item_varstore ()); | |
| else | |
| { | |
| hb_always_assert (item_vars.as_item_varstore ()); | |
| hb_always_assert (item_vars.has_long_word ()); | |
| hb_always_assert ((*item_vars.get_vardata_encodings ()[0].items[0])[0] == 2 * delta); | |
| } | |
| } | |
| } | |
| static void | |
| test_condition_delta_precision () | |
| { | |
| hb_vector_t<hb_tag_t> axes; | |
| axes.push (HB_TAG ('T','E','S','T')); | |
| hb_hashmap_t<hb_tag_t, Triple> neutral, region; | |
| region.set (axes[0], Triple (0, 1, 1)); | |
| hb_vector_t<const hb_hashmap_t<hb_tag_t, Triple> *> regions; | |
| regions.push (&neutral); | |
| regions.push (®ion); | |
| hb_vector_t<int> row; | |
| row.push (16777217); | |
| row.push (-16777216); | |
| hb_vector_t<const hb_vector_t<int> *> rows; | |
| rows.push (&row); | |
| hb_vector_t<OT::delta_row_encoding_t> encodings; | |
| encodings.push (OT::delta_row_encoding_t (std::move (rows), 2)); | |
| char buf[128]; | |
| hb_serialize_context_t s (buf, sizeof (buf)); | |
| auto *out = s.start_serialize<OT::ItemVariationStore> (); | |
| hb_always_assert (out->serialize (&s, true, axes, regions, encodings)); | |
| s.end_serialize (); | |
| hb_bytes_t bytes = s.copy_bytes (); | |
| const auto &store = *reinterpret_cast<const OT::ItemVariationStore *> (bytes.arrayZ); | |
| int coords[] = {16384}; | |
| OT::ItemVarStoreInstancer instancer (&store, nullptr, hb_array (coords)); | |
| // Positioning keeps float arithmetic, but conditions must retain the unit | |
| // left after cancellation of two deltas exceeding float's integer range. | |
| hb_always_assert (instancer[0] == 0.f); | |
| hb_always_assert (instancer.get_condition_delta (0) == 1.); | |
| const char condition_data[] = "\x00\x02\x00\x00\x00\x00\x00\x00"; | |
| const auto &condition = *reinterpret_cast<const OT::Condition *> (condition_data); | |
| hb_always_assert (condition.evaluate (coords, 1, &instancer)); | |
| bytes.fini (); | |
| } | |
| static void | |
| test_failed_encoding () | |
| { | |
| hb_vector_t<int> row; | |
| row.push (1); | |
| hb_vector_t<const hb_vector_t<int> *> rows; | |
| rows.push (&row); | |
| OT::item_variations_t item_vars; | |
| hb_always_assert (item_vars.add_vardata_encoding_for_testing (std::move (rows), 1)); | |
| auto &encoding = const_cast<OT::delta_row_encoding_t &> (item_vars.get_vardata_encodings ()[0]); | |
| /* Simulate a failed allocation of the encoding's column metadata. */ | |
| encoding.chars.clear (); | |
| encoding.chars.set_error (); | |
| hb_always_assert (encoding.in_error ()); | |
| hb_hashmap_t<unsigned, const hb_vector_t<int> *> front_mapping; | |
| front_mapping.set (0, &row); | |
| hb_always_assert (!item_vars.compile_varidx_map_for_testing (front_mapping)); | |
| } | |
| int | |
| main (int argc, char **argv) | |
| { | |
| test_item_variations (); | |
| test_implicit_advance_mapping (); | |
| test_item_variations_overflow (); | |
| test_signed_delta_widths (); | |
| test_failed_encoding (); | |
| test_accumulated_delta_widths (); | |
| test_condition_delta_precision (); | |
| } |