tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

GraphiteExtra.cpp (6152B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "graphite2/Font.h"
      8 #include "graphite2/Segment.h"
      9 #include "graphite2/GraphiteExtra.h"
     10 
     11 #include <stdlib.h>
     12 #include <memory>
     13 #include <limits>
     14 
     15 #define CHECK(cond, str) \
     16  do {                   \
     17    if (!(cond)) {       \
     18      return false;      \
     19    }                    \
     20  } while (false)
     21 
     22 // High surrogates are in the range 0xD800 -- OxDBFF
     23 #define NS_IS_HIGH_SURROGATE(u) ((uint32_t(u) & 0xFFFFFC00) == 0xD800)
     24 // Low surrogates are in the range 0xDC00 -- 0xDFFF
     25 #define NS_IS_LOW_SURROGATE(u) ((uint32_t(u) & 0xFFFFFC00) == 0xDC00)
     26 
     27 #define IS_POWER_OF_2(n) (n & (n - 1)) == 0
     28 
     29 typedef gr_glyph_to_char_cluster Cluster;
     30 
     31 // returns whether successful
     32 static bool LoopThrough(gr_segment* aSegment, const uint32_t aLength,
     33                        const uint32_t aGlyphCount, const char16_t* aText,
     34                        uint32_t& aCIndex, Cluster* aClusters, uint16_t* aGids,
     35                        float* aXLocs, float* aYLocs) {
     36  // walk through the glyph slots and check which original character
     37  // each is associated with
     38  uint32_t gIndex = 0;  // glyph slot index
     39  for (const gr_slot* slot = gr_seg_first_slot(aSegment); slot != nullptr;
     40       slot = gr_slot_next_in_segment(slot), gIndex++) {
     41    CHECK(gIndex < aGlyphCount, "iterating past glyphcount");
     42    uint32_t before =
     43        gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_before(slot)));
     44    uint32_t after = gr_cinfo_base(gr_seg_cinfo(aSegment, gr_slot_after(slot)));
     45    aGids[gIndex] = gr_slot_gid(slot);
     46    aXLocs[gIndex] = gr_slot_origin_X(slot);
     47    aYLocs[gIndex] = gr_slot_origin_Y(slot);
     48 
     49    // if this glyph has a "before" character index that precedes the
     50    // current cluster's char index, we need to merge preceding
     51    // aClusters until it gets included
     52    while (before < aClusters[aCIndex].baseChar && aCIndex > 0) {
     53      aClusters[aCIndex - 1].nChars += aClusters[aCIndex].nChars;
     54      aClusters[aCIndex - 1].nGlyphs += aClusters[aCIndex].nGlyphs;
     55      --aCIndex;
     56    }
     57 
     58    // if there's a gap between the current cluster's base character and
     59    // this glyph's, extend the cluster to include the intervening chars
     60    if (gr_slot_can_insert_before(slot) && aClusters[aCIndex].nChars &&
     61        before >= aClusters[aCIndex].baseChar + aClusters[aCIndex].nChars) {
     62      CHECK(aCIndex < aLength - 1, "aCIndex at end of word");
     63      Cluster& c = aClusters[aCIndex + 1];
     64      c.baseChar = aClusters[aCIndex].baseChar + aClusters[aCIndex].nChars;
     65      c.nChars = before - c.baseChar;
     66      c.baseGlyph = gIndex;
     67      c.nGlyphs = 0;
     68      ++aCIndex;
     69    }
     70 
     71    // increment cluster's glyph count to include current slot
     72    CHECK(aCIndex < aLength, "aCIndex beyond word length");
     73    ++aClusters[aCIndex].nGlyphs;
     74 
     75    // bump |after| index if it falls in the middle of a surrogate pair
     76    if (NS_IS_HIGH_SURROGATE(aText[after]) && after < aLength - 1 &&
     77        NS_IS_LOW_SURROGATE(aText[after + 1])) {
     78      after++;
     79    }
     80 
     81    // extend cluster if necessary to reach the glyph's "after" index
     82    if (aClusters[aCIndex].baseChar + aClusters[aCIndex].nChars < after + 1) {
     83      aClusters[aCIndex].nChars = after + 1 - aClusters[aCIndex].baseChar;
     84    }
     85  }
     86 
     87  // Succeeded
     88  return true;
     89 }
     90 
     91 static gr_glyph_to_char_association* calloc_glyph_to_char_association(
     92    uint32_t aGlyphCount, uint32_t aLength) {
     93  using Type1 = gr_glyph_to_char_association;
     94  using Type2 = gr_glyph_to_char_cluster;
     95  using Type3 = float;
     96  using Type4 = float;
     97  using Type5 = uint16_t;
     98 
     99  // We are allocating memory in a pool. To avoid dealing with thorny alignment
    100  // issues, we allocate from most to least aligned
    101  static_assert(
    102      alignof(Type1) >= alignof(Type2) && alignof(Type2) >= alignof(Type3) &&
    103          alignof(Type3) >= alignof(Type4) && alignof(Type4) >= alignof(Type5),
    104      "Unexpected alignments of types");
    105 
    106  const uint64_t size1 = sizeof(Type1) * static_cast<uint64_t>(1);
    107  const uint64_t size2 = sizeof(Type2) * static_cast<uint64_t>(aLength);
    108  const uint64_t size3 = sizeof(Type3) * static_cast<uint64_t>(aGlyphCount);
    109  const uint64_t size4 = sizeof(Type4) * static_cast<uint64_t>(aGlyphCount);
    110  const uint64_t size5 = sizeof(Type5) * static_cast<uint64_t>(aGlyphCount);
    111 
    112  uint64_t totalSize = size1 + size2 + size3 + size4 + size5;
    113 
    114  if (totalSize > std::numeric_limits<uint32_t>::max()) {
    115    // allocation request got too large
    116    return nullptr;
    117  }
    118 
    119  char* const memoryPool = static_cast<char*>(calloc(1, totalSize));
    120  if (!memoryPool) {
    121    return nullptr;
    122  }
    123 
    124  char* currentPoolFront = memoryPool;
    125 
    126  auto data = reinterpret_cast<Type1*>(currentPoolFront);
    127  currentPoolFront += size1;
    128  data->clusters = reinterpret_cast<Type2*>(currentPoolFront);
    129  currentPoolFront += size2;
    130  data->xLocs = reinterpret_cast<Type3*>(currentPoolFront);
    131  currentPoolFront += size3;
    132  data->yLocs = reinterpret_cast<Type4*>(currentPoolFront);
    133  currentPoolFront += size4;
    134  data->gids = reinterpret_cast<Type5*>(currentPoolFront);
    135 
    136  return data;
    137 }
    138 
    139 // returns nullptr on failure and the glyph to char association on success
    140 gr_glyph_to_char_association* gr_get_glyph_to_char_association(
    141    gr_segment* aSegment, uint32_t aLength, const char16_t* aText) {
    142  uint32_t glyphCount = gr_seg_n_slots(aSegment);
    143 
    144  gr_glyph_to_char_association* data =
    145      calloc_glyph_to_char_association(glyphCount, aLength);
    146  if (!data) {
    147    return nullptr;
    148  }
    149 
    150  bool succeeded =
    151      LoopThrough(aSegment, aLength, glyphCount, aText, data->cIndex,
    152                  data->clusters, data->gids, data->xLocs, data->yLocs);
    153  if (!succeeded) {
    154    gr_free_char_association(data);
    155    return nullptr;
    156  }
    157  return data;
    158 }
    159 
    160 void gr_free_char_association(gr_glyph_to_char_association* aData) {
    161  free(aData);
    162 }
    163 
    164 #undef CHECK
    165 #undef NS_IS_HIGH_SURROGATE
    166 #undef NS_IS_LOW_SURROGATE
    167 #undef IS_POWER_OF_2