Skip to content

Commit

Permalink
Bug 1584000 - Migrate glyph to character association code from libThe…
Browse files Browse the repository at this point in the history
…bes to graphite for sandboxed libGraphite performance r=jfkthame,froydnj

Differential Revision: https://phabricator.services.mozilla.com/D47388
  • Loading branch information
shravanrn committed Oct 30, 2019
1 parent c933400 commit 76d8f91
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 76 deletions.
42 changes: 42 additions & 0 deletions gfx/graphite2/geckoextra/include/GraphiteExtra.h
Original file line number Diff line number Diff line change
@@ -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 <stdint.h>

#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
167 changes: 167 additions & 0 deletions gfx/graphite2/geckoextra/src/GraphiteExtra.cpp
Original file line number Diff line number Diff line change
@@ -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 <stdlib.h>
#include <memory>
#include <limits>

#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<uint64_t>(1);
const uint64_t size2 = sizeof(Type2) * static_cast<uint64_t>(aLength);
const uint64_t size3 = sizeof(Type3) * static_cast<uint64_t>(aGlyphCount);
const uint64_t size4 = sizeof(Type4) * static_cast<uint64_t>(aGlyphCount);
const uint64_t size5 = sizeof(Type5) * static_cast<uint64_t>(aGlyphCount);

uint64_t totalSize = size1 + size2 + size3 + size4 + size5;

if (totalSize > std::numeric_limits<uint32_t>::max()) {
// allocation request got too large
return nullptr;
}

char* const memoryPool = static_cast<char*>(calloc(1, totalSize));
if (!memoryPool) {
return nullptr;
}

char* currentPoolFront = memoryPool;

auto data = reinterpret_cast<Type1*>(currentPoolFront);
currentPoolFront += size1;
data->clusters = reinterpret_cast<Type2*>(currentPoolFront);
currentPoolFront += size2;
data->xLocs = reinterpret_cast<Type3*>(currentPoolFront);
currentPoolFront += size3;
data->yLocs = reinterpret_cast<Type4*>(currentPoolFront);
currentPoolFront += size4;
data->gids = reinterpret_cast<Type5*>(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
2 changes: 2 additions & 0 deletions gfx/graphite2/src/moz.build
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -23,6 +24,7 @@ else:

# 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',
Expand Down
89 changes: 13 additions & 76 deletions gfx/thebes/gfxGraphiteShaper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "gfxTextRun.h"

#include "graphite2/Font.h"
#include "graphite2/GraphiteExtra.h"
#include "graphite2/Segment.h"

#include "harfbuzz/hb.h"
Expand Down Expand Up @@ -191,18 +192,6 @@ bool gfxGraphiteShaper::ShapeText(DrawTarget* aDrawTarget,
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) {
Expand All @@ -211,79 +200,28 @@ nsresult gfxGraphiteShaper::SetGlyphsFromSegment(
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.
AutoTArray<Cluster, SMALL_GLYPH_RUN> clusters;
AutoTArray<uint16_t, SMALL_GLYPH_RUN> gids;
AutoTArray<float, SMALL_GLYPH_RUN> xLocs;
AutoTArray<float, SMALL_GLYPH_RUN> yLocs;

if (!clusters.SetLength(aLength, fallible) ||
!gids.SetLength(glyphCount, fallible) ||
!xLocs.SetLength(glyphCount, fallible) ||
!yLocs.SetLength(glyphCount, fallible)) {
return NS_ERROR_OUT_OF_MEMORY;
}

// 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;
}
gr_glyph_to_char_association* data =
gr_get_glyph_to_char_association(aSegment, aLength, aText);

// 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;
}
if (!data) {
return NS_ERROR_FAILURE;
}

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;

bool roundX = bool(aRounding & RoundingFlags::kRoundX);
bool roundY = bool(aRounding & RoundingFlags::kRoundY);

// 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) {
Expand Down Expand Up @@ -350,11 +288,10 @@ nsresult gfxGraphiteShaper::SetGlyphsFromSegment(
}
}

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"

Expand Down

0 comments on commit 76d8f91

Please sign in to comment.