blob: d32553bfe09d13e3d8d3cb08afeffb3f5af68466 [file]
/*
* 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 (&region);
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 (&region);
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 ();
}