# HG changeset patch # User shravanrn@gmail.com # Date 1572421705 0 # Wed Oct 30 07:48:25 2019 +0000 # Node ID 1b90fe16b26aaa175ed963b4cb1610e87cbac410 # Parent f67361bfb377d12c0ee1f26f710833f20641bb77 Bug 1584000 - Migrate glyph to character association code from libThebes to graphite for sandboxed libGraphite performance r=jfkthame,froydnj Differential Revision: https://phabricator.services.mozilla.com/D47388 diff -r f67361bfb377 -r 1b90fe16b26a gfx/graphite2/geckoextra/include/GraphiteExtra.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/graphite2/geckoextra/include/GraphiteExtra.h Wed Oct 30 07:48:25 2019 +0000 @@ -0,0 +1,42 @@ +// -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- +// vim: set ts=2 et sw=2 tw=80: +// This Source Code is subject to the terms of the Mozilla Public License +// version 2.0 (the "License"). You can obtain a copy of the License at +// http://mozilla.org/MPL/2.0/. + +#ifndef GraphiteExtra_h__ +#define GraphiteExtra_h__ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "graphite2/Segment.h" + +typedef struct { + uint32_t baseChar; // in UTF16 code units, not Unicode character indices + uint32_t baseGlyph; + uint32_t nChars; // UTF16 code units + uint32_t nGlyphs; +} gr_glyph_to_char_cluster; + +typedef struct { + gr_glyph_to_char_cluster* clusters; + uint16_t* gids; + float* xLocs; + float* yLocs; + uint32_t cIndex; +} gr_glyph_to_char_association; + +gr_glyph_to_char_association* gr_get_glyph_to_char_association( + gr_segment* aSegment, uint32_t aLength, const char16_t* aText); + +void gr_free_char_association(gr_glyph_to_char_association* aData); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff -r f67361bfb377 -r 1b90fe16b26a gfx/graphite2/geckoextra/src/GraphiteExtra.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gfx/graphite2/geckoextra/src/GraphiteExtra.cpp Wed Oct 30 07:48:25 2019 +0000 @@ -0,0 +1,167 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "graphite2/Font.h" +#include "graphite2/Segment.h" +#include "graphite2/GraphiteExtra.h" + +#include +#include +#include + +#define CHECK(cond, str) \ + do { \ + if (!(cond)) { \ + return false; \ + } \ + } while (false) + +// High surrogates are in the range 0xD800 -- OxDBFF +#define NS_IS_HIGH_SURROGATE(u) ((uint32_t(u) & 0xFFFFFC00) == 0xD800) +// Low surrogates are in the range 0xDC00 -- 0xDFFF +#define NS_IS_LOW_SURROGATE(u) ((uint32_t(u) & 0xFFFFFC00) == 0xDC00) + +#define IS_POWER_OF_2(n) (n & (n - 1)) == 0 + +typedef gr_glyph_to_char_cluster Cluster; + +// returns whether successful +static bool LoopThrough(gr_segment* aSegment, const uint32_t aLength, + const uint32_t aGlyphCount, const char16_t* aText, + uint32_t& aCIndex, Cluster* aClusters, uint16_t* aGids, + float* aXLocs, float* aYLocs) { + // walk through the glyph slots and check which original character + // each is associated with + uint32_t gIndex = 0; // glyph slot index + for (const gr_slot* slot = gr_seg_first_slot(aSegment); slot != nullptr; + slot = gr_slot_next_in_segment(slot), gIndex++) { + CHECK(gIndex < aGlyphCount, "iterating past glyphcount"); + uint32_t before = + gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_before(slot))); + uint32_t after = gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_after(slot))); + aGids[gIndex] = gr_slot_gid(slot); + aXLocs[gIndex] = gr_slot_origin_X(slot); + aYLocs[gIndex] = gr_slot_origin_Y(slot); + + // if this glyph has a "before" character index that precedes the + // current cluster's char index, we need to merge preceding + // aClusters until it gets included + while (before < aClusters[aCIndex].baseChar && aCIndex > 0) { + aClusters[aCIndex - 1].nChars += aClusters[aCIndex].nChars; + aClusters[aCIndex - 1].nGlyphs += aClusters[aCIndex].nGlyphs; + --aCIndex; + } + + // if there's a gap between the current cluster's base character and + // this glyph's, extend the cluster to include the intervening chars + if (gr_slot_can_insert_before(slot) && aClusters[aCIndex].nChars && + before >= aClusters[aCIndex].baseChar + aClusters[aCIndex].nChars) { + CHECK(aCIndex < aLength - 1, "aCIndex at end of word"); + Cluster& c = aClusters[aCIndex + 1]; + c.baseChar = aClusters[aCIndex].baseChar + aClusters[aCIndex].nChars; + c.nChars = before - c.baseChar; + c.baseGlyph = gIndex; + c.nGlyphs = 0; + ++aCIndex; + } + + // increment cluster's glyph count to include current slot + CHECK(aCIndex < aLength, "aCIndex beyond word length"); + ++aClusters[aCIndex].nGlyphs; + + // bump |after| index if it falls in the middle of a surrogate pair + if (NS_IS_HIGH_SURROGATE(aText[after]) && after < aLength - 1 && + NS_IS_LOW_SURROGATE(aText[after + 1])) { + after++; + } + + // extend cluster if necessary to reach the glyph's "after" index + if (aClusters[aCIndex].baseChar + aClusters[aCIndex].nChars < after + 1) { + aClusters[aCIndex].nChars = after + 1 - aClusters[aCIndex].baseChar; + } + } + + // Succeeded + return true; +} + +static gr_glyph_to_char_association* calloc_glyph_to_char_association( + uint32_t aGlyphCount, uint32_t aLength) { + using Type1 = gr_glyph_to_char_association; + using Type2 = gr_glyph_to_char_cluster; + using Type3 = float; + using Type4 = float; + using Type5 = uint16_t; + + // We are allocating memory in a pool. To avoid dealing with thorny alignment + // issues, we allocate from most to least aligned + static_assert( + alignof(Type1) >= alignof(Type2) && alignof(Type2) >= alignof(Type3) && + alignof(Type3) >= alignof(Type4) && alignof(Type4) >= alignof(Type5), + "Unexpected alignments of types"); + + const uint64_t size1 = sizeof(Type1) * static_cast(1); + const uint64_t size2 = sizeof(Type2) * static_cast(aLength); + const uint64_t size3 = sizeof(Type3) * static_cast(aGlyphCount); + const uint64_t size4 = sizeof(Type4) * static_cast(aGlyphCount); + const uint64_t size5 = sizeof(Type5) * static_cast(aGlyphCount); + + uint64_t totalSize = size1 + size2 + size3 + size4 + size5; + + if (totalSize > std::numeric_limits::max()) { + // allocation request got too large + return nullptr; + } + + char* const memoryPool = static_cast(calloc(1, totalSize)); + if (!memoryPool) { + return nullptr; + } + + char* currentPoolFront = memoryPool; + + auto data = reinterpret_cast(currentPoolFront); + currentPoolFront += size1; + data->clusters = reinterpret_cast(currentPoolFront); + currentPoolFront += size2; + data->xLocs = reinterpret_cast(currentPoolFront); + currentPoolFront += size3; + data->yLocs = reinterpret_cast(currentPoolFront); + currentPoolFront += size4; + data->gids = reinterpret_cast(currentPoolFront); + + return data; +} + +// returns nullptr on failure and the glyph to char association on success +gr_glyph_to_char_association* gr_get_glyph_to_char_association( + gr_segment* aSegment, uint32_t aLength, const char16_t* aText) { + uint32_t glyphCount = gr_seg_n_slots(aSegment); + + gr_glyph_to_char_association* data = + calloc_glyph_to_char_association(glyphCount, aLength); + if (!data) { + return nullptr; + } + + bool succeeded = + LoopThrough(aSegment, aLength, glyphCount, aText, data->cIndex, + data->clusters, data->gids, data->xLocs, data->yLocs); + if (!succeeded) { + gr_free_char_association(data); + return nullptr; + } + return data; +} + +void gr_free_char_association(gr_glyph_to_char_association* aData) { + free(aData); +} + +#undef CHECK +#undef NS_IS_HIGH_SURROGATE +#undef NS_IS_LOW_SURROGATE +#undef IS_POWER_OF_2 diff -r f67361bfb377 -r 1b90fe16b26a gfx/graphite2/src/moz.build --- a/gfx/graphite2/src/moz.build Wed Oct 30 11:00:40 2019 +0000 +++ b/gfx/graphite2/src/moz.build Wed Oct 30 07:48:25 2019 +0000 @@ -6,6 +6,7 @@ # This should contain all of the _PUBLIC_HEADERS from files.mk EXPORTS.graphite2 += [ + '../geckoextra/include/GraphiteExtra.h', '../include/graphite2/Font.h', '../include/graphite2/Log.h', '../include/graphite2/Segment.h', @@ -23,6 +24,7 @@ # This should contain all of the _SOURCES from files.mk, except *_machine.cpp UNIFIED_SOURCES += [ + '../geckoextra/src/GraphiteExtra.cpp', 'CmapCache.cpp', 'Code.cpp', 'Collider.cpp', diff -r f67361bfb377 -r 1b90fe16b26a gfx/thebes/gfxGraphiteShaper.cpp --- a/gfx/thebes/gfxGraphiteShaper.cpp Wed Oct 30 11:00:40 2019 +0000 +++ b/gfx/thebes/gfxGraphiteShaper.cpp Wed Oct 30 07:48:25 2019 +0000 @@ -10,6 +10,7 @@ #include "gfxTextRun.h" #include "graphite2/Font.h" +#include "graphite2/GraphiteExtra.h" #include "graphite2/Segment.h" #include "harfbuzz/hb.h" @@ -191,18 +192,6 @@ return NS_SUCCEEDED(rv); } -#define SMALL_GLYPH_RUN \ - 256 // avoid heap allocation of per-glyph data arrays - // for short (typical) runs up to this length - -struct Cluster { - uint32_t baseChar; // in UTF16 code units, not Unicode character indices - uint32_t baseGlyph; - uint32_t nChars; // UTF16 code units - uint32_t nGlyphs; - Cluster() : baseChar(0), baseGlyph(0), nChars(0), nGlyphs(0) {} -}; - nsresult gfxGraphiteShaper::SetGlyphsFromSegment( gfxShapedText* aShapedText, uint32_t aOffset, uint32_t aLength, const char16_t* aText, gr_segment* aSegment, RoundingFlags aRounding) { @@ -211,70 +200,19 @@ int32_t dev2appUnits = aShapedText->GetAppUnitsPerDevUnit(); bool rtl = aShapedText->IsRightToLeft(); - uint32_t glyphCount = gr_seg_n_slots(aSegment); + // identify clusters; graphite may have reordered/expanded/ligated glyphs. + gr_glyph_to_char_association* data = + gr_get_glyph_to_char_association(aSegment, aLength, aText); - // identify clusters; graphite may have reordered/expanded/ligated glyphs. - AutoTArray clusters; - AutoTArray gids; - AutoTArray xLocs; - AutoTArray yLocs; - - if (!clusters.SetLength(aLength, fallible) || - !gids.SetLength(glyphCount, fallible) || - !xLocs.SetLength(glyphCount, fallible) || - !yLocs.SetLength(glyphCount, fallible)) { - return NS_ERROR_OUT_OF_MEMORY; + if (!data) { + return NS_ERROR_FAILURE; } - // walk through the glyph slots and check which original character - // each is associated with - uint32_t gIndex = 0; // glyph slot index - uint32_t cIndex = 0; // current cluster index - for (const gr_slot* slot = gr_seg_first_slot(aSegment); slot != nullptr; - slot = gr_slot_next_in_segment(slot), gIndex++) { - uint32_t before = - gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_before(slot))); - uint32_t after = gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_after(slot))); - gids[gIndex] = gr_slot_gid(slot); - xLocs[gIndex] = gr_slot_origin_X(slot); - yLocs[gIndex] = gr_slot_origin_Y(slot); - - // if this glyph has a "before" character index that precedes the - // current cluster's char index, we need to merge preceding - // clusters until it gets included - while (before < clusters[cIndex].baseChar && cIndex > 0) { - clusters[cIndex - 1].nChars += clusters[cIndex].nChars; - clusters[cIndex - 1].nGlyphs += clusters[cIndex].nGlyphs; - --cIndex; - } - - // if there's a gap between the current cluster's base character and - // this glyph's, extend the cluster to include the intervening chars - if (gr_slot_can_insert_before(slot) && clusters[cIndex].nChars && - before >= clusters[cIndex].baseChar + clusters[cIndex].nChars) { - NS_ASSERTION(cIndex < aLength - 1, "cIndex at end of word"); - Cluster& c = clusters[cIndex + 1]; - c.baseChar = clusters[cIndex].baseChar + clusters[cIndex].nChars; - c.nChars = before - c.baseChar; - c.baseGlyph = gIndex; - c.nGlyphs = 0; - ++cIndex; - } - - // increment cluster's glyph count to include current slot - NS_ASSERTION(cIndex < aLength, "cIndex beyond word length"); - ++clusters[cIndex].nGlyphs; - - // bump |after| index if it falls in the middle of a surrogate pair - if (NS_IS_HIGH_SURROGATE(aText[after]) && after < aLength - 1 && - NS_IS_LOW_SURROGATE(aText[after + 1])) { - after++; - } - // extend cluster if necessary to reach the glyph's "after" index - if (clusters[cIndex].baseChar + clusters[cIndex].nChars < after + 1) { - clusters[cIndex].nChars = after + 1 - clusters[cIndex].baseChar; - } - } + uint32_t cIndex = data->cIndex; + gr_glyph_to_char_cluster* clusters = data->clusters; + uint16_t* gids = data->gids; + float* xLocs = data->xLocs; + float* yLocs = data->yLocs; CompressedGlyph* charGlyphs = aShapedText->GetCharacterGlyphs() + aOffset; @@ -283,7 +221,7 @@ // now put glyphs into the textrun, one cluster at a time for (uint32_t i = 0; i <= cIndex; ++i) { - const Cluster& c = clusters[i]; + const gr_glyph_to_char_cluster& c = clusters[i]; float adv; // total advance of the cluster if (rtl) { @@ -350,11 +288,10 @@ } } + gr_free_char_association(data); return NS_OK; } -#undef SMALL_GLYPH_RUN - // for language tag validation - include list of tags from the IANA registry #include "gfxLanguageTagList.cpp"