Home | History | Annotate | Download | only in minikin
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "Minikin"
     18 
     19 #include <algorithm>
     20 #include <fstream>
     21 #include <iostream>  // for debugging
     22 #include <math.h>
     23 #include <string>
     24 #include <unicode/ubidi.h>
     25 #include <unicode/utf16.h>
     26 #include <vector>
     27 
     28 #include <log/log.h>
     29 #include <utils/JenkinsHash.h>
     30 #include <utils/LruCache.h>
     31 #include <utils/Singleton.h>
     32 #include <utils/String16.h>
     33 
     34 #include <hb-icu.h>
     35 #include <hb-ot.h>
     36 
     37 #include "FontLanguage.h"
     38 #include "FontLanguageListCache.h"
     39 #include "HbFontCache.h"
     40 #include "LayoutUtils.h"
     41 #include "MinikinInternal.h"
     42 #include <minikin/Emoji.h>
     43 #include <minikin/Layout.h>
     44 
     45 using std::string;
     46 using std::vector;
     47 
     48 namespace minikin {
     49 
     50 const int kDirection_Mask = 0x1;
     51 
     52 struct LayoutContext {
     53     MinikinPaint paint;
     54     FontStyle style;
     55     std::vector<hb_font_t*> hbFonts;  // parallel to mFaces
     56 
     57     void clearHbFonts() {
     58         for (size_t i = 0; i < hbFonts.size(); i++) {
     59             hb_font_set_funcs(hbFonts[i], nullptr, nullptr, nullptr);
     60             hb_font_destroy(hbFonts[i]);
     61         }
     62         hbFonts.clear();
     63     }
     64 };
     65 
     66 // Layout cache datatypes
     67 
     68 class LayoutCacheKey {
     69 public:
     70     LayoutCacheKey(const std::shared_ptr<FontCollection>& collection, const MinikinPaint& paint,
     71             FontStyle style, const uint16_t* chars, size_t start, size_t count, size_t nchars,
     72             bool dir)
     73             : mChars(chars), mNchars(nchars),
     74             mStart(start), mCount(count), mId(collection->getId()), mStyle(style),
     75             mSize(paint.size), mScaleX(paint.scaleX), mSkewX(paint.skewX),
     76             mLetterSpacing(paint.letterSpacing),
     77             mPaintFlags(paint.paintFlags), mHyphenEdit(paint.hyphenEdit), mIsRtl(dir),
     78             mHash(computeHash()) {
     79     }
     80     bool operator==(const LayoutCacheKey &other) const;
     81 
     82     android::hash_t hash() const {
     83         return mHash;
     84     }
     85 
     86     void copyText() {
     87         uint16_t* charsCopy = new uint16_t[mNchars];
     88         memcpy(charsCopy, mChars, mNchars * sizeof(uint16_t));
     89         mChars = charsCopy;
     90     }
     91     void freeText() {
     92         delete[] mChars;
     93         mChars = NULL;
     94     }
     95 
     96     void doLayout(Layout* layout, LayoutContext* ctx,
     97             const std::shared_ptr<FontCollection>& collection) const {
     98         layout->mAdvances.resize(mCount, 0);
     99         ctx->clearHbFonts();
    100         layout->doLayoutRun(mChars, mStart, mCount, mNchars, mIsRtl, ctx, collection);
    101     }
    102 
    103 private:
    104     const uint16_t* mChars;
    105     size_t mNchars;
    106     size_t mStart;
    107     size_t mCount;
    108     uint32_t mId;  // for the font collection
    109     FontStyle mStyle;
    110     float mSize;
    111     float mScaleX;
    112     float mSkewX;
    113     float mLetterSpacing;
    114     int32_t mPaintFlags;
    115     HyphenEdit mHyphenEdit;
    116     bool mIsRtl;
    117     // Note: any fields added to MinikinPaint must also be reflected here.
    118     // TODO: language matching (possibly integrate into style)
    119     android::hash_t mHash;
    120 
    121     android::hash_t computeHash() const;
    122 };
    123 
    124 class LayoutCache : private android::OnEntryRemoved<LayoutCacheKey, Layout*> {
    125 public:
    126     LayoutCache() : mCache(kMaxEntries) {
    127         mCache.setOnEntryRemovedListener(this);
    128     }
    129 
    130     void clear() {
    131         mCache.clear();
    132     }
    133 
    134     Layout* get(LayoutCacheKey& key, LayoutContext* ctx,
    135             const std::shared_ptr<FontCollection>& collection) {
    136         Layout* layout = mCache.get(key);
    137         if (layout == NULL) {
    138             key.copyText();
    139             layout = new Layout();
    140             key.doLayout(layout, ctx, collection);
    141             mCache.put(key, layout);
    142         }
    143         return layout;
    144     }
    145 
    146 private:
    147     // callback for OnEntryRemoved
    148     void operator()(LayoutCacheKey& key, Layout*& value) {
    149         key.freeText();
    150         delete value;
    151     }
    152 
    153     android::LruCache<LayoutCacheKey, Layout*> mCache;
    154 
    155     //static const size_t kMaxEntries = LruCache<LayoutCacheKey, Layout*>::kUnlimitedCapacity;
    156 
    157     // TODO: eviction based on memory footprint; for now, we just use a constant
    158     // number of strings
    159     static const size_t kMaxEntries = 5000;
    160 };
    161 
    162 static unsigned int disabledDecomposeCompatibility(hb_unicode_funcs_t*, hb_codepoint_t,
    163                                                    hb_codepoint_t*, void*) {
    164     return 0;
    165 }
    166 
    167 class LayoutEngine : public ::android::Singleton<LayoutEngine> {
    168 public:
    169     LayoutEngine() {
    170         unicodeFunctions = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
    171         /* Disable the function used for compatibility decomposition */
    172         hb_unicode_funcs_set_decompose_compatibility_func(
    173                 unicodeFunctions, disabledDecomposeCompatibility, NULL, NULL);
    174         hbBuffer = hb_buffer_create();
    175         hb_buffer_set_unicode_funcs(hbBuffer, unicodeFunctions);
    176     }
    177 
    178     hb_buffer_t* hbBuffer;
    179     hb_unicode_funcs_t* unicodeFunctions;
    180     LayoutCache layoutCache;
    181 };
    182 
    183 bool LayoutCacheKey::operator==(const LayoutCacheKey& other) const {
    184     return mId == other.mId
    185             && mStart == other.mStart
    186             && mCount == other.mCount
    187             && mStyle == other.mStyle
    188             && mSize == other.mSize
    189             && mScaleX == other.mScaleX
    190             && mSkewX == other.mSkewX
    191             && mLetterSpacing == other.mLetterSpacing
    192             && mPaintFlags == other.mPaintFlags
    193             && mHyphenEdit == other.mHyphenEdit
    194             && mIsRtl == other.mIsRtl
    195             && mNchars == other.mNchars
    196             && !memcmp(mChars, other.mChars, mNchars * sizeof(uint16_t));
    197 }
    198 
    199 android::hash_t LayoutCacheKey::computeHash() const {
    200     uint32_t hash = android::JenkinsHashMix(0, mId);
    201     hash = android::JenkinsHashMix(hash, mStart);
    202     hash = android::JenkinsHashMix(hash, mCount);
    203     hash = android::JenkinsHashMix(hash, hash_type(mStyle));
    204     hash = android::JenkinsHashMix(hash, hash_type(mSize));
    205     hash = android::JenkinsHashMix(hash, hash_type(mScaleX));
    206     hash = android::JenkinsHashMix(hash, hash_type(mSkewX));
    207     hash = android::JenkinsHashMix(hash, hash_type(mLetterSpacing));
    208     hash = android::JenkinsHashMix(hash, hash_type(mPaintFlags));
    209     hash = android::JenkinsHashMix(hash, hash_type(mHyphenEdit.getHyphen()));
    210     hash = android::JenkinsHashMix(hash, hash_type(mIsRtl));
    211     hash = android::JenkinsHashMixShorts(hash, mChars, mNchars);
    212     return android::JenkinsHashWhiten(hash);
    213 }
    214 
    215 android::hash_t hash_type(const LayoutCacheKey& key) {
    216     return key.hash();
    217 }
    218 
    219 void MinikinRect::join(const MinikinRect& r) {
    220     if (isEmpty()) {
    221         set(r);
    222     } else if (!r.isEmpty()) {
    223         mLeft = std::min(mLeft, r.mLeft);
    224         mTop = std::min(mTop, r.mTop);
    225         mRight = std::max(mRight, r.mRight);
    226         mBottom = std::max(mBottom, r.mBottom);
    227     }
    228 }
    229 
    230 void Layout::reset() {
    231     mGlyphs.clear();
    232     mFaces.clear();
    233     mBounds.setEmpty();
    234     mAdvances.clear();
    235     mAdvance = 0;
    236 }
    237 
    238 static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* /* hbFont */, void* fontData,
    239         hb_codepoint_t glyph, void* /* userData */) {
    240     MinikinPaint* paint = reinterpret_cast<MinikinPaint*>(fontData);
    241     float advance = paint->font->GetHorizontalAdvance(glyph, *paint);
    242     return 256 * advance + 0.5;
    243 }
    244 
    245 static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* /* hbFont */, void* /* fontData */,
    246         hb_codepoint_t /* glyph */, hb_position_t* /* x */, hb_position_t* /* y */,
    247         void* /* userData */) {
    248     // Just return true, following the way that Harfbuzz-FreeType
    249     // implementation does.
    250     return true;
    251 }
    252 
    253 hb_font_funcs_t* getHbFontFuncs(bool forColorBitmapFont) {
    254     assertMinikinLocked();
    255 
    256     static hb_font_funcs_t* hbFuncs = nullptr;
    257     static hb_font_funcs_t* hbFuncsForColorBitmap = nullptr;
    258 
    259     hb_font_funcs_t** funcs = forColorBitmapFont ? &hbFuncs : &hbFuncsForColorBitmap;
    260     if (*funcs == nullptr) {
    261         *funcs = hb_font_funcs_create();
    262         if (forColorBitmapFont) {
    263             // Don't override the h_advance function since we use HarfBuzz's implementation for
    264             // emoji for performance reasons.
    265             // Note that it is technically possible for a TrueType font to have outline and embedded
    266             // bitmap at the same time. We ignore modified advances of hinted outline glyphs in that
    267             // case.
    268         } else {
    269             // Override the h_advance function since we can't use HarfBuzz's implemenation. It may
    270             // return the wrong value if the font uses hinting aggressively.
    271             hb_font_funcs_set_glyph_h_advance_func(*funcs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
    272         }
    273         hb_font_funcs_set_glyph_h_origin_func(*funcs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
    274         hb_font_funcs_make_immutable(*funcs);
    275     }
    276     return *funcs;
    277 }
    278 
    279 static bool isColorBitmapFont(hb_font_t* font) {
    280     hb_face_t* face = hb_font_get_face(font);
    281     HbBlob cbdt(hb_face_reference_table(face, HB_TAG('C', 'B', 'D', 'T')));
    282     return cbdt.size() > 0;
    283 }
    284 
    285 static float HBFixedToFloat(hb_position_t v)
    286 {
    287     return scalbnf (v, -8);
    288 }
    289 
    290 static hb_position_t HBFloatToFixed(float v)
    291 {
    292     return scalbnf (v, +8);
    293 }
    294 
    295 void Layout::dump() const {
    296     for (size_t i = 0; i < mGlyphs.size(); i++) {
    297         const LayoutGlyph& glyph = mGlyphs[i];
    298         std::cout << glyph.glyph_id << ": " << glyph.x << ", " << glyph.y << std::endl;
    299     }
    300 }
    301 
    302 int Layout::findFace(const FakedFont& face, LayoutContext* ctx) {
    303     unsigned int ix;
    304     for (ix = 0; ix < mFaces.size(); ix++) {
    305         if (mFaces[ix].font == face.font) {
    306             return ix;
    307         }
    308     }
    309     mFaces.push_back(face);
    310     // Note: ctx == NULL means we're copying from the cache, no need to create
    311     // corresponding hb_font object.
    312     if (ctx != NULL) {
    313         hb_font_t* font = getHbFontLocked(face.font);
    314         hb_font_set_funcs(font, getHbFontFuncs(isColorBitmapFont(font)), &ctx->paint, 0);
    315         ctx->hbFonts.push_back(font);
    316     }
    317     return ix;
    318 }
    319 
    320 static hb_script_t codePointToScript(hb_codepoint_t codepoint) {
    321     static hb_unicode_funcs_t* u = 0;
    322     if (!u) {
    323         u = LayoutEngine::getInstance().unicodeFunctions;
    324     }
    325     return hb_unicode_script(u, codepoint);
    326 }
    327 
    328 static hb_codepoint_t decodeUtf16(const uint16_t* chars, size_t len, ssize_t* iter) {
    329     UChar32 result;
    330     U16_NEXT(chars, *iter, (ssize_t) len, result);
    331     if (U_IS_SURROGATE(result)) { // isolated surrogate
    332         result = 0xFFFDu; // U+FFFD REPLACEMENT CHARACTER
    333     }
    334     return (hb_codepoint_t) result;
    335 }
    336 
    337 static hb_script_t getScriptRun(const uint16_t* chars, size_t len, ssize_t* iter) {
    338     if (size_t(*iter) == len) {
    339         return HB_SCRIPT_UNKNOWN;
    340     }
    341     uint32_t cp = decodeUtf16(chars, len, iter);
    342     hb_script_t current_script = codePointToScript(cp);
    343     for (;;) {
    344         if (size_t(*iter) == len)
    345             break;
    346         const ssize_t prev_iter = *iter;
    347         cp = decodeUtf16(chars, len, iter);
    348         const hb_script_t script = codePointToScript(cp);
    349         if (script != current_script) {
    350             if (current_script == HB_SCRIPT_INHERITED ||
    351                 current_script == HB_SCRIPT_COMMON) {
    352                 current_script = script;
    353             } else if (script == HB_SCRIPT_INHERITED ||
    354                 script == HB_SCRIPT_COMMON) {
    355                 continue;
    356             } else {
    357                 *iter = prev_iter;
    358                 break;
    359             }
    360         }
    361     }
    362     if (current_script == HB_SCRIPT_INHERITED) {
    363         current_script = HB_SCRIPT_COMMON;
    364     }
    365 
    366     return current_script;
    367 }
    368 
    369 /**
    370  * Disable certain scripts (mostly those with cursive connection) from having letterspacing
    371  * applied. See https://github.com/behdad/harfbuzz/issues/64 for more details.
    372  */
    373 static bool isScriptOkForLetterspacing(hb_script_t script) {
    374     return !(
    375             script == HB_SCRIPT_ARABIC ||
    376             script == HB_SCRIPT_NKO ||
    377             script == HB_SCRIPT_PSALTER_PAHLAVI ||
    378             script == HB_SCRIPT_MANDAIC ||
    379             script == HB_SCRIPT_MONGOLIAN ||
    380             script == HB_SCRIPT_PHAGS_PA ||
    381             script == HB_SCRIPT_DEVANAGARI ||
    382             script == HB_SCRIPT_BENGALI ||
    383             script == HB_SCRIPT_GURMUKHI ||
    384             script == HB_SCRIPT_MODI ||
    385             script == HB_SCRIPT_SHARADA ||
    386             script == HB_SCRIPT_SYLOTI_NAGRI ||
    387             script == HB_SCRIPT_TIRHUTA ||
    388             script == HB_SCRIPT_OGHAM
    389             );
    390 }
    391 
    392 class BidiText {
    393 public:
    394     class Iter {
    395     public:
    396         struct RunInfo {
    397             int32_t mRunStart;
    398             int32_t mRunLength;
    399             bool mIsRtl;
    400         };
    401 
    402         Iter(UBiDi* bidi, size_t start, size_t end, size_t runIndex, size_t runCount, bool isRtl);
    403 
    404         bool operator!= (const Iter& other) const {
    405             return mIsEnd != other.mIsEnd || mNextRunIndex != other.mNextRunIndex
    406                     || mBidi != other.mBidi;
    407         }
    408 
    409         const RunInfo& operator* () const {
    410             return mRunInfo;
    411         }
    412 
    413         const Iter& operator++ () {
    414             updateRunInfo();
    415             return *this;
    416         }
    417 
    418     private:
    419         UBiDi* const mBidi;
    420         bool mIsEnd;
    421         size_t mNextRunIndex;
    422         const size_t mRunCount;
    423         const int32_t mStart;
    424         const int32_t mEnd;
    425         RunInfo mRunInfo;
    426 
    427         void updateRunInfo();
    428     };
    429 
    430     BidiText(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags);
    431 
    432     ~BidiText() {
    433         if (mBidi) {
    434             ubidi_close(mBidi);
    435         }
    436     }
    437 
    438     Iter begin () const {
    439         return Iter(mBidi, mStart, mEnd, 0, mRunCount, mIsRtl);
    440     }
    441 
    442     Iter end() const {
    443         return Iter(mBidi, mStart, mEnd, mRunCount, mRunCount, mIsRtl);
    444     }
    445 
    446 private:
    447     const size_t mStart;
    448     const size_t mEnd;
    449     const size_t mBufSize;
    450     UBiDi* mBidi;
    451     size_t mRunCount;
    452     bool mIsRtl;
    453 
    454     BidiText(const BidiText&) = delete;
    455     void operator=(const BidiText&) = delete;
    456 };
    457 
    458 BidiText::Iter::Iter(UBiDi* bidi, size_t start, size_t end, size_t runIndex, size_t runCount,
    459         bool isRtl)
    460     : mBidi(bidi), mIsEnd(runIndex == runCount), mNextRunIndex(runIndex), mRunCount(runCount),
    461       mStart(start), mEnd(end), mRunInfo() {
    462     if (mRunCount == 1) {
    463         mRunInfo.mRunStart = start;
    464         mRunInfo.mRunLength = end - start;
    465         mRunInfo.mIsRtl = isRtl;
    466         mNextRunIndex = mRunCount;
    467         return;
    468     }
    469     updateRunInfo();
    470 }
    471 
    472 void BidiText::Iter::updateRunInfo() {
    473     if (mNextRunIndex == mRunCount) {
    474         // All runs have been iterated.
    475         mIsEnd = true;
    476         return;
    477     }
    478     int32_t startRun = -1;
    479     int32_t lengthRun = -1;
    480     const UBiDiDirection runDir = ubidi_getVisualRun(mBidi, mNextRunIndex, &startRun, &lengthRun);
    481     mNextRunIndex++;
    482     if (startRun == -1 || lengthRun == -1) {
    483         ALOGE("invalid visual run");
    484         // skip the invalid run.
    485         updateRunInfo();
    486         return;
    487     }
    488     const int32_t runEnd = std::min(startRun + lengthRun, mEnd);
    489     mRunInfo.mRunStart = std::max(startRun, mStart);
    490     mRunInfo.mRunLength = runEnd - mRunInfo.mRunStart;
    491     if (mRunInfo.mRunLength <= 0) {
    492         // skip the empty run.
    493         updateRunInfo();
    494         return;
    495     }
    496     mRunInfo.mIsRtl = (runDir == UBIDI_RTL);
    497 }
    498 
    499 BidiText::BidiText(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags)
    500     : mStart(start), mEnd(start + count), mBufSize(bufSize), mBidi(NULL), mRunCount(1),
    501       mIsRtl((bidiFlags & kDirection_Mask) != 0) {
    502     if (bidiFlags == kBidi_Force_LTR || bidiFlags == kBidi_Force_RTL) {
    503         // force single run.
    504         return;
    505     }
    506     mBidi = ubidi_open();
    507     if (!mBidi) {
    508         ALOGE("error creating bidi object");
    509         return;
    510     }
    511     UErrorCode status = U_ZERO_ERROR;
    512     // Set callbacks to override bidi classes of new emoji
    513     ubidi_setClassCallback(mBidi, emojiBidiOverride, nullptr, nullptr, nullptr, &status);
    514     if (!U_SUCCESS(status)) {
    515         ALOGE("error setting bidi callback function, status = %d", status);
    516         return;
    517     }
    518 
    519     UBiDiLevel bidiReq = bidiFlags;
    520     if (bidiFlags == kBidi_Default_LTR) {
    521         bidiReq = UBIDI_DEFAULT_LTR;
    522     } else if (bidiFlags == kBidi_Default_RTL) {
    523         bidiReq = UBIDI_DEFAULT_RTL;
    524     }
    525     ubidi_setPara(mBidi, reinterpret_cast<const UChar*>(buf), mBufSize, bidiReq, NULL, &status);
    526     if (!U_SUCCESS(status)) {
    527         ALOGE("error calling ubidi_setPara, status = %d", status);
    528         return;
    529     }
    530     const int paraDir = ubidi_getParaLevel(mBidi) & kDirection_Mask;
    531     const ssize_t rc = ubidi_countRuns(mBidi, &status);
    532     if (!U_SUCCESS(status) || rc < 0) {
    533         ALOGW("error counting bidi runs, status = %d", status);
    534     }
    535     if (!U_SUCCESS(status) || rc <= 0) {
    536         mIsRtl = (paraDir == kBidi_RTL);
    537         return;
    538     }
    539     if (rc == 1) {
    540         const UBiDiDirection runDir = ubidi_getVisualRun(mBidi, 0, nullptr, nullptr);
    541         mIsRtl = (runDir == UBIDI_RTL);
    542         return;
    543     }
    544     mRunCount = rc;
    545 }
    546 
    547 void Layout::doLayout(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
    548         int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
    549         const std::shared_ptr<FontCollection>& collection) {
    550     android::AutoMutex _l(gMinikinLock);
    551 
    552     LayoutContext ctx;
    553     ctx.style = style;
    554     ctx.paint = paint;
    555 
    556     reset();
    557     mAdvances.resize(count, 0);
    558 
    559     for (const BidiText::Iter::RunInfo& runInfo : BidiText(buf, start, count, bufSize, bidiFlags)) {
    560         doLayoutRunCached(buf, runInfo.mRunStart, runInfo.mRunLength, bufSize, runInfo.mIsRtl, &ctx,
    561                 start, collection, this, NULL);
    562     }
    563     ctx.clearHbFonts();
    564 }
    565 
    566 float Layout::measureText(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
    567         int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
    568         const std::shared_ptr<FontCollection>& collection, float* advances) {
    569     android::AutoMutex _l(gMinikinLock);
    570 
    571     LayoutContext ctx;
    572     ctx.style = style;
    573     ctx.paint = paint;
    574 
    575     float advance = 0;
    576     for (const BidiText::Iter::RunInfo& runInfo : BidiText(buf, start, count, bufSize, bidiFlags)) {
    577         float* advancesForRun = advances ? advances + (runInfo.mRunStart - start) : advances;
    578         advance += doLayoutRunCached(buf, runInfo.mRunStart, runInfo.mRunLength, bufSize,
    579                 runInfo.mIsRtl, &ctx, 0, collection, NULL, advancesForRun);
    580     }
    581 
    582     ctx.clearHbFonts();
    583     return advance;
    584 }
    585 
    586 float Layout::doLayoutRunCached(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
    587         bool isRtl, LayoutContext* ctx, size_t dstStart,
    588         const std::shared_ptr<FontCollection>& collection, Layout* layout, float* advances) {
    589     const uint32_t originalHyphen = ctx->paint.hyphenEdit.getHyphen();
    590     float advance = 0;
    591     if (!isRtl) {
    592         // left to right
    593         size_t wordstart =
    594                 start == bufSize ? start : getPrevWordBreakForCache(buf, start + 1, bufSize);
    595         size_t wordend;
    596         for (size_t iter = start; iter < start + count; iter = wordend) {
    597             wordend = getNextWordBreakForCache(buf, iter, bufSize);
    598             // Only apply hyphen to the first or last word in the string.
    599             uint32_t hyphen = originalHyphen;
    600             if (iter != start) { // Not the first word
    601                 hyphen &= ~HyphenEdit::MASK_START_OF_LINE;
    602             }
    603             if (wordend < start + count) { // Not the last word
    604                 hyphen &= ~HyphenEdit::MASK_END_OF_LINE;
    605             }
    606             ctx->paint.hyphenEdit = hyphen;
    607             size_t wordcount = std::min(start + count, wordend) - iter;
    608             advance += doLayoutWord(buf + wordstart, iter - wordstart, wordcount,
    609                     wordend - wordstart, isRtl, ctx, iter - dstStart, collection, layout,
    610                     advances ? advances + (iter - start) : advances);
    611             wordstart = wordend;
    612         }
    613     } else {
    614         // right to left
    615         size_t wordstart;
    616         size_t end = start + count;
    617         size_t wordend = end == 0 ? 0 : getNextWordBreakForCache(buf, end - 1, bufSize);
    618         for (size_t iter = end; iter > start; iter = wordstart) {
    619             wordstart = getPrevWordBreakForCache(buf, iter, bufSize);
    620             // Only apply hyphen to the first (rightmost) or last (leftmost) word in the string.
    621             uint32_t hyphen = originalHyphen;
    622             if (wordstart > start) { // Not the first word
    623                 hyphen &= ~HyphenEdit::MASK_START_OF_LINE;
    624             }
    625             if (iter != end) { // Not the last word
    626                 hyphen &= ~HyphenEdit::MASK_END_OF_LINE;
    627             }
    628             ctx->paint.hyphenEdit = hyphen;
    629             size_t bufStart = std::max(start, wordstart);
    630             advance += doLayoutWord(buf + wordstart, bufStart - wordstart, iter - bufStart,
    631                     wordend - wordstart, isRtl, ctx, bufStart - dstStart, collection, layout,
    632                     advances ? advances + (bufStart - start) : advances);
    633             wordend = wordstart;
    634         }
    635     }
    636     return advance;
    637 }
    638 
    639 float Layout::doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
    640         bool isRtl, LayoutContext* ctx, size_t bufStart,
    641         const std::shared_ptr<FontCollection>& collection, Layout* layout, float* advances) {
    642     LayoutCache& cache = LayoutEngine::getInstance().layoutCache;
    643     LayoutCacheKey key(collection, ctx->paint, ctx->style, buf, start, count, bufSize, isRtl);
    644 
    645     float wordSpacing = count == 1 && isWordSpace(buf[start]) ? ctx->paint.wordSpacing : 0;
    646 
    647     float advance;
    648     if (ctx->paint.skipCache()) {
    649         Layout layoutForWord;
    650         key.doLayout(&layoutForWord, ctx, collection);
    651         if (layout) {
    652             layout->appendLayout(&layoutForWord, bufStart, wordSpacing);
    653         }
    654         if (advances) {
    655             layoutForWord.getAdvances(advances);
    656         }
    657         advance = layoutForWord.getAdvance();
    658     } else {
    659         Layout* layoutForWord = cache.get(key, ctx, collection);
    660         if (layout) {
    661             layout->appendLayout(layoutForWord, bufStart, wordSpacing);
    662         }
    663         if (advances) {
    664             layoutForWord->getAdvances(advances);
    665         }
    666         advance = layoutForWord->getAdvance();
    667     }
    668 
    669     if (wordSpacing != 0) {
    670         advance += wordSpacing;
    671         if (advances) {
    672             advances[0] += wordSpacing;
    673         }
    674     }
    675     return advance;
    676 }
    677 
    678 static void addFeatures(const string &str, vector<hb_feature_t>* features) {
    679     if (!str.size())
    680         return;
    681 
    682     const char* start = str.c_str();
    683     const char* end = start + str.size();
    684 
    685     while (start < end) {
    686         static hb_feature_t feature;
    687         const char* p = strchr(start, ',');
    688         if (!p)
    689             p = end;
    690         /* We do not allow setting features on ranges.  As such, reject any
    691          * setting that has non-universal range. */
    692         if (hb_feature_from_string (start, p - start, &feature)
    693                 && feature.start == 0 && feature.end == (unsigned int) -1)
    694             features->push_back(feature);
    695         start = p + 1;
    696     }
    697 }
    698 
    699 static const hb_codepoint_t CHAR_HYPHEN = 0x2010; /* HYPHEN */
    700 
    701 static inline hb_codepoint_t determineHyphenChar(hb_codepoint_t preferredHyphen, hb_font_t* font) {
    702     hb_codepoint_t glyph;
    703     if (preferredHyphen == 0x058A /* ARMENIAN_HYPHEN */
    704                 || preferredHyphen == 0x05BE /* HEBREW PUNCTUATION MAQAF */
    705                 || preferredHyphen == 0x1400 /* CANADIAN SYLLABIC HYPHEN */) {
    706         if (hb_font_get_nominal_glyph(font, preferredHyphen, &glyph)) {
    707             return preferredHyphen;
    708         } else {
    709             // The original hyphen requested was not supported. Let's try and see if the
    710             // Unicode hyphen is supported.
    711             preferredHyphen = CHAR_HYPHEN;
    712         }
    713     }
    714     if (preferredHyphen == CHAR_HYPHEN) { /* HYPHEN */
    715         // Fallback to ASCII HYPHEN-MINUS if the font didn't have a glyph for the preferred hyphen.
    716         // Note that we intentionally don't do anything special if the font doesn't have a
    717         // HYPHEN-MINUS either, so a tofu could be shown, hinting towards something missing.
    718         if (!hb_font_get_nominal_glyph(font, preferredHyphen, &glyph)) {
    719             return 0x002D; // HYPHEN-MINUS
    720         }
    721     }
    722     return preferredHyphen;
    723 }
    724 
    725 static inline void addHyphenToHbBuffer(hb_buffer_t* buffer, hb_font_t* font, uint32_t hyphen,
    726         uint32_t cluster) {
    727     const uint32_t* hyphenStr = HyphenEdit::getHyphenString(hyphen);
    728     while (*hyphenStr != 0) {
    729         hb_codepoint_t hyphenChar = determineHyphenChar(*hyphenStr, font);
    730         hb_buffer_add(buffer, hyphenChar, cluster);
    731         hyphenStr++;
    732     }
    733 }
    734 
    735 // Returns the cluster value assigned to the first codepoint added to the buffer, which can be used
    736 // to translate cluster values returned by HarfBuzz to input indices.
    737 static inline uint32_t addToHbBuffer(hb_buffer_t* buffer,
    738         const uint16_t* buf, size_t start, size_t count, size_t bufSize,
    739         ssize_t scriptRunStart, ssize_t scriptRunEnd,
    740         HyphenEdit hyphenEdit, hb_font_t* hbFont) {
    741 
    742     // Only hyphenate the very first script run for starting hyphens.
    743     const uint32_t startHyphen = (scriptRunStart == 0)
    744             ? hyphenEdit.getStart()
    745             : HyphenEdit::NO_EDIT;
    746     // Only hyphenate the very last script run for ending hyphens.
    747     const uint32_t endHyphen = (static_cast<size_t>(scriptRunEnd) == count)
    748             ? hyphenEdit.getEnd()
    749             : HyphenEdit::NO_EDIT;
    750 
    751     // In the following code, we drop the pre-context and/or post-context if there is a
    752     // hyphen edit at that end. This is not absolutely necessary, since HarfBuzz uses
    753     // contexts only for joining scripts at the moment, e.g. to determine if the first or
    754     // last letter of a text range to shape should take a joining form based on an
    755     // adjacent letter or joiner (that comes from the context).
    756     //
    757     // TODO: Revisit this for:
    758     // 1. Desperate breaks for joining scripts like Arabic (where it may be better to keep
    759     //    the context);
    760     // 2. Special features like start-of-word font features (not implemented in HarfBuzz
    761     //    yet).
    762 
    763     // We don't have any start-of-line replacement edit yet, so we don't need to check for
    764     // those.
    765     if (HyphenEdit::isInsertion(startHyphen)) {
    766         // A cluster value of zero guarantees that the inserted hyphen will be in the same
    767         // cluster with the next codepoint, since there is no pre-context.
    768         addHyphenToHbBuffer(buffer, hbFont, startHyphen, 0 /* cluster value */);
    769     }
    770 
    771     const uint16_t* hbText;
    772     int hbTextLength;
    773     unsigned int hbItemOffset;
    774     unsigned int hbItemLength = scriptRunEnd - scriptRunStart; // This is >= 1.
    775 
    776     const bool hasEndInsertion = HyphenEdit::isInsertion(endHyphen);
    777     const bool hasEndReplacement = HyphenEdit::isReplacement(endHyphen);
    778     if (hasEndReplacement) {
    779         // Skip the last code unit while copying the buffer for HarfBuzz if it's a replacement. We
    780         // don't need to worry about non-BMP characters yet since replacements are only done for
    781         // code units at the moment.
    782         hbItemLength -= 1;
    783     }
    784 
    785     if (startHyphen == HyphenEdit::NO_EDIT) {
    786         // No edit at the beginning. Use the whole pre-context.
    787         hbText = buf;
    788         hbItemOffset = start + scriptRunStart;
    789     } else {
    790         // There's an edit at the beginning. Drop the pre-context and start the buffer at where we
    791         // want to start shaping.
    792         hbText = buf + start + scriptRunStart;
    793         hbItemOffset = 0;
    794     }
    795 
    796     if (endHyphen == HyphenEdit::NO_EDIT) {
    797         // No edit at the end, use the whole post-context.
    798         hbTextLength = (buf + bufSize) - hbText;
    799     } else {
    800         // There is an edit at the end. Drop the post-context.
    801         hbTextLength = hbItemOffset + hbItemLength;
    802     }
    803 
    804     hb_buffer_add_utf16(buffer, hbText, hbTextLength, hbItemOffset, hbItemLength);
    805 
    806     unsigned int numCodepoints;
    807     hb_glyph_info_t* cpInfo = hb_buffer_get_glyph_infos(buffer, &numCodepoints);
    808 
    809     // Add the hyphen at the end, if there's any.
    810     if (hasEndInsertion || hasEndReplacement) {
    811         // When a hyphen is inserted, by assigning the added hyphen and the last
    812         // codepoint added to the HarfBuzz buffer to the same cluster, we can make sure
    813         // that they always remain in the same cluster, even if the last codepoint gets
    814         // merged into another cluster (for example when it's a combining mark).
    815         //
    816         // When a replacement happens instead, we want it to get the cluster value of
    817         // the character it's replacing, which is one "codepoint length" larger than
    818         // the last cluster. But since the character replaced is always just one
    819         // code unit, we can just add 1.
    820         uint32_t hyphenCluster;
    821         if (numCodepoints == 0) {
    822             // Nothing was added to the HarfBuzz buffer. This can only happen if
    823             // we have a replacement that is replacing a one-code unit script run.
    824             hyphenCluster = 0;
    825         } else {
    826             hyphenCluster = cpInfo[numCodepoints - 1].cluster + (uint32_t) hasEndReplacement;
    827         }
    828         addHyphenToHbBuffer(buffer, hbFont, endHyphen, hyphenCluster);
    829         // Since we have just added to the buffer, cpInfo no longer necessarily points to
    830         // the right place. Refresh it.
    831         cpInfo = hb_buffer_get_glyph_infos(buffer, nullptr /* we don't need the size */);
    832     }
    833     return cpInfo[0].cluster;
    834 }
    835 
    836 
    837 void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
    838         bool isRtl, LayoutContext* ctx, const std::shared_ptr<FontCollection>& collection) {
    839     hb_buffer_t* buffer = LayoutEngine::getInstance().hbBuffer;
    840     vector<FontCollection::Run> items;
    841     collection->itemize(buf + start, count, ctx->style, &items);
    842 
    843     vector<hb_feature_t> features;
    844     // Disable default-on non-required ligature features if letter-spacing
    845     // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property
    846     // "When the effective spacing between two characters is not zero (due to
    847     // either justification or a non-zero value of letter-spacing), user agents
    848     // should not apply optional ligatures."
    849     if (fabs(ctx->paint.letterSpacing) > 0.03)
    850     {
    851         static const hb_feature_t no_liga = { HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u };
    852         static const hb_feature_t no_clig = { HB_TAG('c', 'l', 'i', 'g'), 0, 0, ~0u };
    853         features.push_back(no_liga);
    854         features.push_back(no_clig);
    855     }
    856     addFeatures(ctx->paint.fontFeatureSettings, &features);
    857 
    858     double size = ctx->paint.size;
    859     double scaleX = ctx->paint.scaleX;
    860 
    861     float x = mAdvance;
    862     float y = 0;
    863     for (int run_ix = isRtl ? items.size() - 1 : 0;
    864             isRtl ? run_ix >= 0 : run_ix < static_cast<int>(items.size());
    865             isRtl ? --run_ix : ++run_ix) {
    866         FontCollection::Run &run = items[run_ix];
    867         if (run.fakedFont.font == NULL) {
    868             ALOGE("no font for run starting u+%04x length %d", buf[run.start], run.end - run.start);
    869             continue;
    870         }
    871         int font_ix = findFace(run.fakedFont, ctx);
    872         ctx->paint.font = mFaces[font_ix].font;
    873         ctx->paint.fakery = mFaces[font_ix].fakery;
    874         hb_font_t* hbFont = ctx->hbFonts[font_ix];
    875 #ifdef VERBOSE_DEBUG
    876         ALOGD("Run %zu, font %d [%d:%d]", run_ix, font_ix, run.start, run.end);
    877 #endif
    878 
    879         hb_font_set_ppem(hbFont, size * scaleX, size);
    880         hb_font_set_scale(hbFont, HBFloatToFixed(size * scaleX), HBFloatToFixed(size));
    881 
    882         const bool is_color_bitmap_font = isColorBitmapFont(hbFont);
    883 
    884         // TODO: if there are multiple scripts within a font in an RTL run,
    885         // we need to reorder those runs. This is unlikely with our current
    886         // font stack, but should be done for correctness.
    887 
    888         // Note: scriptRunStart and scriptRunEnd, as well as run.start and run.end, run between 0
    889         // and count.
    890         ssize_t scriptRunEnd;
    891         for (ssize_t scriptRunStart = run.start;
    892                 scriptRunStart < run.end;
    893                 scriptRunStart = scriptRunEnd) {
    894             scriptRunEnd = scriptRunStart;
    895             hb_script_t script = getScriptRun(buf + start, run.end, &scriptRunEnd /* iterator */);
    896             // After the last line, scriptRunEnd is guaranteed to have increased, since the only
    897             // time getScriptRun does not increase its iterator is when it has already reached the
    898             // end of the buffer. But that can't happen, since if we have already reached the end
    899             // of the buffer, we should have had (scriptRunEnd == run.end), which means
    900             // (scriptRunStart == run.end) which is impossible due to the exit condition of the for
    901             // loop. So we can be sure that scriptRunEnd > scriptRunStart.
    902 
    903             double letterSpace = 0.0;
    904             double letterSpaceHalfLeft = 0.0;
    905             double letterSpaceHalfRight = 0.0;
    906 
    907             if (ctx->paint.letterSpacing != 0.0 && isScriptOkForLetterspacing(script)) {
    908                 letterSpace = ctx->paint.letterSpacing * size * scaleX;
    909                 if ((ctx->paint.paintFlags & LinearTextFlag) == 0) {
    910                     letterSpace = round(letterSpace);
    911                     letterSpaceHalfLeft = floor(letterSpace * 0.5);
    912                 } else {
    913                     letterSpaceHalfLeft = letterSpace * 0.5;
    914                 }
    915                 letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
    916             }
    917 
    918             hb_buffer_clear_contents(buffer);
    919             hb_buffer_set_script(buffer, script);
    920             hb_buffer_set_direction(buffer, isRtl? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
    921             const FontLanguages& langList =
    922                     FontLanguageListCache::getById(ctx->style.getLanguageListId());
    923             if (langList.size() != 0) {
    924                 const FontLanguage* hbLanguage = &langList[0];
    925                 for (size_t i = 0; i < langList.size(); ++i) {
    926                     if (langList[i].supportsHbScript(script)) {
    927                         hbLanguage = &langList[i];
    928                         break;
    929                     }
    930                 }
    931                 hb_buffer_set_language(buffer, hbLanguage->getHbLanguage());
    932             }
    933 
    934             const uint32_t clusterStart = addToHbBuffer(
    935                 buffer,
    936                 buf, start, count, bufSize,
    937                 scriptRunStart, scriptRunEnd,
    938                 ctx->paint.hyphenEdit, hbFont);
    939 
    940             hb_shape(hbFont, buffer, features.empty() ? NULL : &features[0], features.size());
    941             unsigned int numGlyphs;
    942             hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
    943             hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
    944 
    945             // At this point in the code, the cluster values in the info buffer correspond to the
    946             // input characters with some shift. The cluster value clusterStart corresponds to the
    947             // first character passed to HarfBuzz, which is at buf[start + scriptRunStart] whose
    948             // advance needs to be saved into mAdvances[scriptRunStart]. So cluster values need to
    949             // be reduced by (clusterStart - scriptRunStart) to get converted to indices of
    950             // mAdvances.
    951             const ssize_t clusterOffset = clusterStart - scriptRunStart;
    952 
    953             if (numGlyphs)
    954             {
    955                 mAdvances[info[0].cluster - clusterOffset] += letterSpaceHalfLeft;
    956                 x += letterSpaceHalfLeft;
    957             }
    958             for (unsigned int i = 0; i < numGlyphs; i++) {
    959 #ifdef VERBOSE_DEBUG
    960                 ALOGD("%d %d %d %d",
    961                         positions[i].x_advance, positions[i].y_advance,
    962                         positions[i].x_offset, positions[i].y_offset);
    963                 ALOGD("DoLayout %u: %f; %d, %d",
    964                         info[i].codepoint, HBFixedToFloat(positions[i].x_advance),
    965                         positions[i].x_offset, positions[i].y_offset);
    966 #endif
    967                 if (i > 0 && info[i - 1].cluster != info[i].cluster) {
    968                     mAdvances[info[i - 1].cluster - clusterOffset] += letterSpaceHalfRight;
    969                     mAdvances[info[i].cluster - clusterOffset] += letterSpaceHalfLeft;
    970                     x += letterSpace;
    971                 }
    972 
    973                 hb_codepoint_t glyph_ix = info[i].codepoint;
    974                 float xoff = HBFixedToFloat(positions[i].x_offset);
    975                 float yoff = -HBFixedToFloat(positions[i].y_offset);
    976                 xoff += yoff * ctx->paint.skewX;
    977                 LayoutGlyph glyph = {font_ix, glyph_ix, x + xoff, y + yoff};
    978                 mGlyphs.push_back(glyph);
    979                 float xAdvance = HBFixedToFloat(positions[i].x_advance);
    980                 if ((ctx->paint.paintFlags & LinearTextFlag) == 0) {
    981                     xAdvance = roundf(xAdvance);
    982                 }
    983                 MinikinRect glyphBounds;
    984                 hb_glyph_extents_t extents = {};
    985                 if (is_color_bitmap_font && hb_font_get_glyph_extents(hbFont, glyph_ix, &extents)) {
    986                     // Note that it is technically possible for a TrueType font to have outline and
    987                     // embedded bitmap at the same time. We ignore modified bbox of hinted outline
    988                     // glyphs in that case.
    989                     glyphBounds.mLeft = roundf(HBFixedToFloat(extents.x_bearing));
    990                     glyphBounds.mTop = roundf(HBFixedToFloat(-extents.y_bearing));
    991                     glyphBounds.mRight = roundf(HBFixedToFloat(extents.x_bearing + extents.width));
    992                     glyphBounds.mBottom =
    993                             roundf(HBFixedToFloat(-extents.y_bearing - extents.height));
    994                 } else {
    995                     ctx->paint.font->GetBounds(&glyphBounds, glyph_ix, ctx->paint);
    996                 }
    997                 glyphBounds.offset(x + xoff, y + yoff);
    998                 mBounds.join(glyphBounds);
    999                 if (static_cast<size_t>(info[i].cluster - clusterOffset) < count) {
   1000                     mAdvances[info[i].cluster - clusterOffset] += xAdvance;
   1001                 } else {
   1002                     ALOGE("cluster %zu (start %zu) out of bounds of count %zu",
   1003                         info[i].cluster - clusterOffset, start, count);
   1004                 }
   1005                 x += xAdvance;
   1006             }
   1007             if (numGlyphs)
   1008             {
   1009                 mAdvances[info[numGlyphs - 1].cluster - clusterOffset] += letterSpaceHalfRight;
   1010                 x += letterSpaceHalfRight;
   1011             }
   1012         }
   1013     }
   1014     mAdvance = x;
   1015 }
   1016 
   1017 void Layout::appendLayout(Layout* src, size_t start, float extraAdvance) {
   1018     int fontMapStack[16];
   1019     int* fontMap;
   1020     if (src->mFaces.size() < sizeof(fontMapStack) / sizeof(fontMapStack[0])) {
   1021         fontMap = fontMapStack;
   1022     } else {
   1023         fontMap = new int[src->mFaces.size()];
   1024     }
   1025     for (size_t i = 0; i < src->mFaces.size(); i++) {
   1026         int font_ix = findFace(src->mFaces[i], NULL);
   1027         fontMap[i] = font_ix;
   1028     }
   1029     int x0 = mAdvance;
   1030     for (size_t i = 0; i < src->mGlyphs.size(); i++) {
   1031         LayoutGlyph& srcGlyph = src->mGlyphs[i];
   1032         int font_ix = fontMap[srcGlyph.font_ix];
   1033         unsigned int glyph_id = srcGlyph.glyph_id;
   1034         float x = x0 + srcGlyph.x;
   1035         float y = srcGlyph.y;
   1036         LayoutGlyph glyph = {font_ix, glyph_id, x, y};
   1037         mGlyphs.push_back(glyph);
   1038     }
   1039     for (size_t i = 0; i < src->mAdvances.size(); i++) {
   1040         mAdvances[i + start] = src->mAdvances[i];
   1041         if (i == 0)
   1042           mAdvances[i + start] += extraAdvance;
   1043     }
   1044     MinikinRect srcBounds(src->mBounds);
   1045     srcBounds.offset(x0, 0);
   1046     mBounds.join(srcBounds);
   1047     mAdvance += src->mAdvance + extraAdvance;
   1048 
   1049     if (fontMap != fontMapStack) {
   1050         delete[] fontMap;
   1051     }
   1052 }
   1053 
   1054 size_t Layout::nGlyphs() const {
   1055     return mGlyphs.size();
   1056 }
   1057 
   1058 const MinikinFont* Layout::getFont(int i) const {
   1059     const LayoutGlyph& glyph = mGlyphs[i];
   1060     return mFaces[glyph.font_ix].font;
   1061 }
   1062 
   1063 FontFakery Layout::getFakery(int i) const {
   1064     const LayoutGlyph& glyph = mGlyphs[i];
   1065     return mFaces[glyph.font_ix].fakery;
   1066 }
   1067 
   1068 unsigned int Layout::getGlyphId(int i) const {
   1069     const LayoutGlyph& glyph = mGlyphs[i];
   1070     return glyph.glyph_id;
   1071 }
   1072 
   1073 float Layout::getX(int i) const {
   1074     const LayoutGlyph& glyph = mGlyphs[i];
   1075     return glyph.x;
   1076 }
   1077 
   1078 float Layout::getY(int i) const {
   1079     const LayoutGlyph& glyph = mGlyphs[i];
   1080     return glyph.y;
   1081 }
   1082 
   1083 float Layout::getAdvance() const {
   1084     return mAdvance;
   1085 }
   1086 
   1087 void Layout::getAdvances(float* advances) {
   1088     memcpy(advances, &mAdvances[0], mAdvances.size() * sizeof(float));
   1089 }
   1090 
   1091 void Layout::getBounds(MinikinRect* bounds) const {
   1092     bounds->set(mBounds);
   1093 }
   1094 
   1095 void Layout::purgeCaches() {
   1096     android::AutoMutex _l(gMinikinLock);
   1097     LayoutCache& layoutCache = LayoutEngine::getInstance().layoutCache;
   1098     layoutCache.clear();
   1099     purgeHbFontCacheLocked();
   1100 }
   1101 
   1102 }  // namespace minikin
   1103 
   1104 // Unable to define the static data member outside of android.
   1105 // TODO: introduce our own Singleton to drop android namespace.
   1106 namespace android {
   1107 ANDROID_SINGLETON_STATIC_INSTANCE(minikin::LayoutEngine);
   1108 }  // namespace android
   1109 
   1110