[repacker] begin adding PairPos splitting support.

This commit is contained in:
Garret Rieger 2022-07-27 18:58:41 +00:00
parent f3aff45e04
commit f6a242b605
11 changed files with 268 additions and 36 deletions

View File

@ -411,7 +411,7 @@ test_priority_queue_SOURCES = test-priority-queue.cc hb-static.cc
test_priority_queue_CPPFLAGS = $(HBCFLAGS)
test_priority_queue_LDADD = libharfbuzz.la $(HBLIBS)
test_repacker_SOURCES = test-repacker.cc hb-static.cc graph/gsubgpos-graph.cc
test_repacker_SOURCES = test-repacker.cc hb-static.cc graph/gsubgpos-context.cc
test_repacker_CPPFLAGS = $(HBCFLAGS)
test_repacker_LDADD = libharfbuzz.la libharfbuzz-subset.la $(HBLIBS)

View File

@ -349,7 +349,9 @@ HB_SUBSET_sources = \
hb-repacker.hh \
graph/graph.hh \
graph/gsubgpos-graph.hh \
graph/gsubgpos-graph.cc \
graph/gsubgpos-context.hh \
graph/gsubgpos-context.cc \
graph/pairpos-graph.hh \
graph/serialize.hh \
$(NULL)

View File

@ -97,10 +97,11 @@ struct MarkArray : Array16Of<MarkRecord> /* Array of MarkRecords--in Cove
}
};
static void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage,
const MarkArray &mark_array,
const hb_set_t &glyphset,
hb_map_t* klass_mapping /* INOUT */)
HB_INTERNAL inline
void Markclass_closure_and_remap_indexes (const Coverage &mark_coverage,
const MarkArray &mark_array,
const hb_set_t &glyphset,
hb_map_t* klass_mapping /* INOUT */)
{
hb_set_t orig_classes;

View File

@ -1,6 +1,8 @@
#ifndef OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
#define OT_LAYOUT_GPOS_PAIRVALUERECORD_HH
#include "ValueFormat.hh"
namespace OT {
namespace Layout {
namespace GPOS_impl {

View File

@ -28,7 +28,7 @@
namespace graph {
make_extension_context_t::make_extension_context_t (hb_tag_t table_tag_,
gsubgpos_graph_context_t::gsubgpos_graph_context_t (hb_tag_t table_tag_,
graph_t& graph_,
hb_vector_t<char>& buffer_)
: table_tag (table_tag_),
@ -47,7 +47,7 @@ make_extension_context_t::make_extension_context_t (hb_tag_t table_tag_,
buffer.alloc (num_non_ext_subtables () * extension_size);
}
unsigned make_extension_context_t::num_non_ext_subtables () {
unsigned gsubgpos_graph_context_t::num_non_ext_subtables () {
unsigned count = 0;
for (auto l : lookups.values ())
{

View File

@ -0,0 +1,60 @@
/*
* Copyright © 2022 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
*/
#include "graph.hh"
#include "../hb-ot-layout-gsubgpos.hh"
#ifndef GRAPH_GSUBGPOS_CONTEXT_HH
#define GRAPH_GSUBGPOS_CONTEXT_HH
namespace graph {
struct Lookup;
struct gsubgpos_graph_context_t
{
hb_tag_t table_tag;
graph_t& graph;
hb_vector_t<char>& buffer;
unsigned lookup_list_index;
hb_hashmap_t<unsigned, graph::Lookup*> lookups;
HB_INTERNAL gsubgpos_graph_context_t (hb_tag_t table_tag_,
graph_t& graph_,
hb_vector_t<char>& buffer_);
bool in_error () const
{
return buffer.in_error ();
}
private:
HB_INTERNAL unsigned num_non_ext_subtables ();
};
}
#endif // GRAPH_GSUBGPOS_CONTEXT

View File

@ -27,6 +27,8 @@
#include "graph.hh"
#include "../hb-ot-layout-gsubgpos.hh"
#include "../OT/Layout/GSUB/ExtensionSubst.hh"
#include "gsubgpos-context.hh"
#include "pairpos-graph.hh"
#ifndef GRAPH_GSUBGPOS_GRAPH_HH
#define GRAPH_GSUBGPOS_GRAPH_HH
@ -35,27 +37,6 @@ namespace graph {
struct Lookup;
struct make_extension_context_t
{
hb_tag_t table_tag;
graph_t& graph;
hb_vector_t<char>& buffer;
unsigned lookup_list_index;
hb_hashmap_t<unsigned, graph::Lookup*> lookups;
HB_INTERNAL make_extension_context_t (hb_tag_t table_tag_,
graph_t& graph_,
hb_vector_t<char>& buffer_);
bool in_error () const
{
return buffer.in_error ();
}
private:
HB_INTERNAL unsigned num_non_ext_subtables ();
};
template<typename T>
struct ExtensionFormat1 : public OT::ExtensionFormat1<T>
{
@ -65,6 +46,16 @@ struct ExtensionFormat1 : public OT::ExtensionFormat1<T>
this->extensionLookupType = type;
this->extensionOffset = 0;
}
unsigned get_lookup_type () const
{
return this->extensionLookupType;
}
unsigned get_subtable_index (graph_t& graph, unsigned this_index) const
{
return graph.index_for_offset (this_index, &this->extensionOffset);
}
};
struct Lookup : public OT::Lookup
@ -86,7 +77,7 @@ struct Lookup : public OT::Lookup
return lookupType == extension_type (table_tag);
}
bool make_extension (make_extension_context_t& c,
bool make_extension (gsubgpos_graph_context_t& c,
unsigned this_index)
{
unsigned type = lookupType;
@ -115,7 +106,43 @@ struct Lookup : public OT::Lookup
return true;
}
bool make_subtable_extension (make_extension_context_t& c,
bool split_subtables_if_needed (gsubgpos_graph_context_t& c,
unsigned this_index)
{
unsigned type = lookupType;
bool is_ext = is_extension (c.table_tag);
if (c.table_tag != HB_OT_TAG_GPOS)
return true;
if (!is_ext && type != OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair)
return true;
// TODO check subtable type.
for (unsigned i = 0; i < subTable.len; i++)
{
unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]);
if (is_ext) {
unsigned ext_subtable_index = subtable_index;
ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension =
(ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*)
c.graph.object (ext_subtable_index).head;
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)
continue;
}
PairPos* pairPos = (PairPos*) c.graph.object (subtable_index).head;
// TODO sanitize
pairPos->split_subtables (c, subtable_index);
}
return true;
}
bool make_subtable_extension (gsubgpos_graph_context_t& c,
unsigned lookup_index,
unsigned subtable_index)
{
@ -214,6 +241,9 @@ struct GSTAR : public OT::GSUBGPOS
return len >= get_size ();
}
// TODO(garretrieger): add find subtable's method, could be templated locate a specific
// subtable type, maybe take the lookup map as a starting point?
void find_lookups (graph_t& graph,
hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */)
{

110
src/graph/pairpos-graph.hh Normal file
View File

@ -0,0 +1,110 @@
/*
* Copyright © 2022 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_PAIRPOS_GRAPH_HH
#define GRAPH_PAIRPOS_GRAPH_HH
#include "../OT/Layout/GPOS/PairPos.hh"
#include "../OT/Layout/GPOS/PosLookupSubTable.hh"
namespace graph {
struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>
{
unsigned get_size () const
{
return OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size
+ pairSet.get_size () - SmallTypes::size;
}
bool split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
{
printf("Checking if pair pos %u needs splits...\n", this_index);
hb_set_t visited;
const unsigned base_size = get_size ();
printf(" base_size = %u\n", base_size);
unsigned accumulated = base_size;
// TODO: include coverage size
unsigned num_pair_sets = pairSet.len;
printf(" num_pair_sets = %u\n", num_pair_sets);
for (unsigned i = 0; i < pairSet.len; i++)
{
unsigned pair_set_index = pair_set_graph_index (c, this_index, i);
accumulated += c.graph.find_subgraph_size (pair_set_index, visited);
if (accumulated > (1 << 16))
{
// TODO: do the split
printf(" PairPos split needed %u/%u\n", i, num_pair_sets);
accumulated = base_size;
}
}
return true;
}
private:
unsigned pair_set_graph_index (gsubgpos_graph_context_t& c, unsigned this_index, unsigned i) const
{
return c.graph.index_for_offset (this_index, &pairSet[i]);
}
};
struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>
{
bool split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
{
// TODO
return true;
}
};
struct PairPos : public OT::Layout::GPOS_impl::PairPos
{
bool split_subtables (gsubgpos_graph_context_t& c, unsigned this_index)
{
unsigned format = u.format;
printf("PairPos::format = %u\n", format);
switch (u.format) {
case 1:
return ((PairPosFormat1*)(&u.format1))->split_subtables (c, this_index);
case 2:
return ((PairPosFormat2*)(&u.format2))->split_subtables (c, this_index);
#ifndef HB_NO_BORING_EXPANSION
case 3:
case 4:
// Don't split 24bit PairPos's.
#endif
default:
return true;
}
}
};
}
#endif // GRAPH_PAIRPOS_GRAPH_HH

View File

@ -1,4 +1,4 @@
#include "graph/gsubgpos-graph.cc"
#include "gsubgpos-context.cc"
#include "hb-aat-layout.cc"
#include "hb-aat-map.cc"
#include "hb-blob.cc"

View File

@ -66,12 +66,34 @@ inline int compare_sizes (const void* a, const void* b)
return 0;
}
static inline
bool _presplit_subtables_if_needed (graph::gsubgpos_graph_context_t& ext_context)
{
// Algorithm:
// 1. Scan all sub-tables and compute their sizes.
// 2. For any PairPos subtables that are >64kb:
// a. split into two more pieces that are <64kb
// b. De-dup subgraphs (optional) (can this be done during serialization?)
// TODO: Add a split subtables method at the lookup level, it could scan it's subtables and split as
// needed.
// TODO: rename gsubgpos_graph_context_t to be more general.
for (unsigned lookup_index : ext_context.lookups.keys ())
{
graph::Lookup* lookup = ext_context.lookups.get(lookup_index);
lookup->split_subtables_if_needed (ext_context, lookup_index);
}
return true;
}
/*
* Analyze the lookups in a GSUB/GPOS table and decide if any should be promoted
* to extension lookups.
*/
static inline
bool _promote_extensions_if_needed (graph::make_extension_context_t& ext_context)
bool _promote_extensions_if_needed (graph::gsubgpos_graph_context_t& ext_context)
{
// Simple Algorithm (v1, current):
// 1. Calculate how many bytes each non-extension lookup consumes.
@ -293,10 +315,15 @@ hb_resolve_overflows (const T& packed,
{
if (recalculate_extensions)
{
graph::make_extension_context_t ext_context (table_tag, sorted_graph, extension_buffer);
graph::gsubgpos_graph_context_t ext_context (table_tag, sorted_graph, extension_buffer);
if (ext_context.in_error ())
return nullptr;
if (!_presplit_subtables_if_needed (ext_context)) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Subtable splitting failed.");
return nullptr;
}
if (!_promote_extensions_if_needed (ext_context)) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Extensions promotion failed.");
return nullptr;

View File

@ -345,7 +345,7 @@ hb_subset_sources = files(
'hb-subset-plan.cc',
'hb-subset-plan.hh',
'hb-subset-repacker.cc',
'graph/gsubgpos-graph.cc',
'graph/gsubgpos-context.cc',
'hb-subset.cc',
'hb-subset.hh',
)
@ -574,7 +574,7 @@ if get_option('tests').enabled()
'test-number': ['test-number.cc', 'hb-number.cc'],
'test-ot-tag': ['hb-ot-tag.cc'],
'test-priority-queue': ['test-priority-queue.cc', 'hb-static.cc'],
'test-repacker': ['test-repacker.cc', 'hb-static.cc', 'graph/gsubgpos-graph.cc'],
'test-repacker': ['test-repacker.cc', 'hb-static.cc', 'graph/gsubgpos-context.cc'],
'test-set': ['test-set.cc', 'hb-static.cc'],
'test-serialize': ['test-serialize.cc', 'hb-static.cc'],
'test-unicode-ranges': ['test-unicode-ranges.cc'],