From b1d38a6d0bc00094900bbce566975ceae2ecdd40 Mon Sep 17 00:00:00 2001 From: Garret Rieger Date: Tue, 19 Jul 2022 23:33:16 +0000 Subject: [PATCH] [repack] WIP implement extension promotion mechanism. --- src/graph/graph.hh | 38 +++++++- src/graph/gsubgpos-graph.hh | 174 +++++++++++++++++++++++++++++++++++ src/hb-ot-layout-gsubgpos.hh | 2 +- src/hb-repacker.hh | 7 +- 4 files changed, 216 insertions(+), 5 deletions(-) create mode 100644 src/graph/gsubgpos-graph.hh diff --git a/src/graph/graph.hh b/src/graph/graph.hh index ec3ee193e..a56d7f48a 100644 --- a/src/graph/graph.hh +++ b/src/graph/graph.hh @@ -199,7 +199,7 @@ struct graph_t return vertices_.length - 1; } - const hb_serialize_context_t::object_t& object(unsigned i) const + const hb_serialize_context_t::object_t& object (unsigned i) const { return vertices_[i].obj; } @@ -368,6 +368,8 @@ struct graph_t */ bool assign_spaces () { + update_parents (); + hb_set_t visited; hb_set_t roots; find_space_roots (visited, roots); @@ -632,6 +634,40 @@ struct graph_t return true; } + + /* + * Adds a new node to the graph, not connected to anything. + */ + unsigned new_node (char* head, char* tail) + { + positions_invalid = true; + distance_invalid = true; + parents_invalid = true; // TODO: remove + + auto* clone = vertices_.push (); + if (vertices_.in_error ()) { + return -1; + } + + clone->obj.head = head; + clone->obj.tail = tail; + clone->distance = 0; + clone->space = 0; + + unsigned clone_idx = vertices_.length - 2; + + // The last object is the root of the graph, so swap back the root to the end. + // The root's obj idx does change, however since it's root nothing else refers to it. + // all other obj idx's will be unaffected. + hb_swap (vertices_[vertices_.length - 2], *clone); + + // Since the root moved, update the parents arrays of all children on the root. + for (const auto& l : root ().obj.all_links ()) + vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); + + return clone_idx; + } + /* * Raises the sorting priority of all children. */ diff --git a/src/graph/gsubgpos-graph.hh b/src/graph/gsubgpos-graph.hh new file mode 100644 index 000000000..984cb2467 --- /dev/null +++ b/src/graph/gsubgpos-graph.hh @@ -0,0 +1,174 @@ +/* + * 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_GSUBGPOS_GRAPH_HH +#define GRAPH_GSUBGPOS_GRAPH_HH + +#include "graph.hh" +#include "hb-ot-layout-gsubgpos.hh" +#include "OT/Layout/GSUB/ExtensionSubst.hh" + +namespace graph { + +struct GSTAR : public OT::GSUBGPOS +{ + const void* get_lookup_list_field_offset () const + { + switch (u.version.major) { + case 1: return &(u.version1.lookupList); +#ifndef HB_NO_BORING_EXPANSION + case 2: return &(u.version2.lookupList); +#endif + default: return 0; + } + } + +}; + +struct Lookup : public OT::Lookup +{ + unsigned extension_type (hb_tag_t table_tag) + { + switch (table_tag) + { + case HB_OT_TAG_GPOS: return 9; + case HB_OT_TAG_GSUB: return 7; + default: return 0; + } + } + + bool make_extension (hb_tag_t table_tag, + graph_t& graph, + unsigned this_index, + hb_vector_t& buffer) + { + // TODO: use a context_t? + unsigned ext_type = extension_type (table_tag); + unsigned type = lookupType; + if (!ext_type || type == ext_type) + { + // NOOP + printf("Already extension (obj %u).\n", this_index); + return true; + } + + printf("Promoting lookup type %u (obj %u) to extension.\n", + type, + this_index); + + + for (unsigned i = 0; i < subTable.len; i++) + { + unsigned subtable_index = graph.index_for_offset (this_index, &subTable[i]); + if (!make_subtable_extension (graph, + this_index, + subtable_index, + buffer)) + return false; + } + + lookupType = ext_type; + return true; + } + + bool make_subtable_extension (graph_t& graph, + unsigned lookup_index, + unsigned subtable_index, + hb_vector_t& buffer) + { + printf(" Promoting subtable %u in lookup %u to extension.\n", subtable_index, lookup_index); + + unsigned type = lookupType; + unsigned extension_size = OT::ExtensionFormat1::static_size; + unsigned start = buffer.length; + unsigned end = start + extension_size; + if (!buffer.resize (buffer.length + extension_size)) + return false; + + OT::ExtensionFormat1* extension = + (OT::ExtensionFormat1*) &buffer[start]; + extension->format = 1; + extension->extensionLookupType = type; + extension->extensionOffset = 0; + + unsigned ext_index = graph.new_node (&buffer[start], &buffer[end]); + if (ext_index == (unsigned) -1) return false; + + auto& lookup_vertex = graph.vertices_[lookup_index]; + for (auto& l : lookup_vertex.obj.real_links.writer ()) + { + if (l.objidx == subtable_index) + { + // Change lookup to point at the extension. + printf(" Changing %d to %d\n", l.objidx, ext_index); + l.objidx = ext_index; + } + } + + // Make extension point at the subtable. + // TODO: update extension parents array. + auto& ext_vertex = graph.vertices_[ext_index]; + auto& subtable_vertex = graph.vertices_[subtable_index]; + auto* l = ext_vertex.obj.real_links.push (); + + l->width = 4; + l->objidx = subtable_index; + l->is_signed = 0; + l->whence = 0; + l->position = 4; + l->bias = 0; + + subtable_vertex.remap_parent (lookup_index, ext_index); + + return true; + } +}; + +static inline void +find_lookups (graph_t& graph, + hb_hashmap_t& lookups /* OUT */) +{ + // TODO: move this into GSTAR? + // TODO: template on types, based on gstar version. + const GSTAR* gstar = (const GSTAR*) graph.root ().obj.head; + + unsigned lookup_list_idx = graph.index_for_offset (graph.root_idx (), + gstar->get_lookup_list_field_offset()); + + const OT::LookupList* lookupList = + (const OT::LookupList*) graph.object (lookup_list_idx).head; + + for (unsigned i = 0; i < lookupList->len; i++) + { + unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i])); + Lookup* lookup = (Lookup*) graph.object (lookup_idx).head; + lookups.set (lookup_idx, lookup); + } +} + +} + +#endif /* GRAPH_GSUBGPOS_GRAPH_HH */ diff --git a/src/hb-ot-layout-gsubgpos.hh b/src/hb-ot-layout-gsubgpos.hh index 62d5d154c..c3f49c973 100644 --- a/src/hb-ot-layout-gsubgpos.hh +++ b/src/hb-ot-layout-gsubgpos.hh @@ -3868,7 +3868,7 @@ struct ExtensionFormat1 return_trace (dest_offset.serialize_subset (c, src_offset, this, get_type ())); } - protected: + public: // TODO HBUINT16 format; /* Format identifier. Set to 1. */ HBUINT16 extensionLookupType; /* Lookup type of subtable referenced * by ExtensionOffset (i.e. the diff --git a/src/hb-repacker.hh b/src/hb-repacker.hh index acfb686f5..ea2431a94 100644 --- a/src/hb-repacker.hh +++ b/src/hb-repacker.hh @@ -44,7 +44,7 @@ using graph::graph_t; */ static inline -void _make_extensions (graph_t& sorted_graph) +void _make_extensions (hb_tag_t table_tag, graph_t& sorted_graph, hb_vector_t& buffer) { // TODO: Move this into graph or gsubgpos graph? hb_hashmap_t lookups; @@ -52,7 +52,7 @@ void _make_extensions (graph_t& sorted_graph) for (auto p : lookups.iter ()) { - p.second->make_extension (sorted_graph, p.first); + p.second->make_extension (table_tag, sorted_graph, p.first, buffer); } } @@ -182,12 +182,13 @@ hb_resolve_overflows (const T& packed, return graph::serialize (sorted_graph); } + hb_vector_t extension_buffer; if ((table_tag == HB_OT_TAG_GPOS || table_tag == HB_OT_TAG_GSUB) && will_overflow) { + _make_extensions (table_tag, sorted_graph, extension_buffer); DEBUG_MSG (SUBSET_REPACK, nullptr, "Assigning spaces to 32 bit subgraphs."); - _make_extensions (sorted_graph); if (sorted_graph.assign_spaces ()) sorted_graph.sort_shortest_distance (); }