19cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien/*
29cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * Copyright (C) 2013 The Android Open Source Project
39cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien *
49cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * Licensed under the Apache License, Version 2.0 (the "License");
59cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * you may not use this file except in compliance with the License.
69cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * You may obtain a copy of the License at
79cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien *
89cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien *      http://www.apache.org/licenses/LICENSE-2.0
99cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien *
109cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * Unless required by applicable law or agreed to in writing, software
119cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * distributed under the License is distributed on an "AS IS" BASIS,
129cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * See the License for the specific language governing permissions and
149cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien * limitations under the License.
159cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien */
169cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
17ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#define LOG_TAG "Minikin"
1869d4fba2f2b7bb2c248cc0e78cf277a6e44665f8Dan Albert
19d231a4b0b1d482c7ae7717b048112e1fe5d0f5a9Raph Levien#include <algorithm>
209cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien#include <fstream>
219cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien#include <iostream>  // for debugging
22555d84c6f98eafcbe677cdcb8e9605760acd8ce5Mark Salyzyn#include <math.h>
2369d4fba2f2b7bb2c248cc0e78cf277a6e44665f8Dan Albert#include <string>
24555d84c6f98eafcbe677cdcb8e9605760acd8ce5Mark Salyzyn#include <unicode/ubidi.h>
25c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader#include <unicode/utf16.h>
2669d4fba2f2b7bb2c248cc0e78cf277a6e44665f8Dan Albert#include <vector>
279cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
2839ab40115fae6d0c948e435233b3dd997ee7d8e5Mark Salyzyn#include <log/log.h>
294d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien#include <utils/JenkinsHash.h>
304d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien#include <utils/LruCache.h>
31fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka#include <utils/Singleton.h>
324d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien#include <utils/String16.h>
334d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
34ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien#include <hb-icu.h>
352a79f59e73294e43f32cc0138e23fcde34eec28aSeigo Nonaka#include <hb-ot.h>
36ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
37198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka#include "FontLanguage.h"
386d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka#include "FontLanguageListCache.h"
3989e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka#include "HbFontCache.h"
40555d84c6f98eafcbe677cdcb8e9605760acd8ce5Mark Salyzyn#include "LayoutUtils.h"
41b80c1f19c58b927820a8a24bf2218e5645724608Raph Levien#include "MinikinInternal.h"
42bab3b98ceb29fa3fc5d8832284312859d7f32cc7Roozbeh Pournader#include <minikin/Emoji.h>
439cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien#include <minikin/Layout.h>
449cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
459cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienusing std::string;
469cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienusing std::vector;
479cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
488920e81717c6e51b92ff8f4479a1f959af260556John Recknamespace minikin {
498920e81717c6e51b92ff8f4479a1f959af260556John Reck
5086fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levienconst int kDirection_Mask = 0x1;
5186fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levien
52288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbodstruct LayoutContext {
53288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    MinikinPaint paint;
54288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    FontStyle style;
55288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    std::vector<hb_font_t*> hbFonts;  // parallel to mFaces
56288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
57288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    void clearHbFonts() {
58288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        for (size_t i = 0; i < hbFonts.size(); i++) {
5989e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka            hb_font_set_funcs(hbFonts[i], nullptr, nullptr, nullptr);
60aaa4e3470270496e6eb80704eadecb2cb7c56bf0Raph Levien            hb_font_destroy(hbFonts[i]);
61288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        }
62288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        hbFonts.clear();
63288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
64288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod};
65288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
664d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien// Layout cache datatypes
674d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
684d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienclass LayoutCacheKey {
694d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienpublic:
70dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka    LayoutCacheKey(const std::shared_ptr<FontCollection>& collection, const MinikinPaint& paint,
71dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka            FontStyle style, const uint16_t* chars, size_t start, size_t count, size_t nchars,
72dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka            bool dir)
73cb20a2f0b366bfc16db3a489a60156dec7a9fe21Andreas Gampe            : mChars(chars), mNchars(nchars),
74cb20a2f0b366bfc16db3a489a60156dec7a9fe21Andreas Gampe            mStart(start), mCount(count), mId(collection->getId()), mStyle(style),
75448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            mSize(paint.size), mScaleX(paint.scaleX), mSkewX(paint.skewX),
768e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod            mLetterSpacing(paint.letterSpacing),
779d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi            mPaintFlags(paint.paintFlags), mHyphenEdit(paint.hyphenEdit), mIsRtl(dir),
789d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi            mHash(computeHash()) {
794d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
804d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    bool operator==(const LayoutCacheKey &other) const;
819d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi
8214e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    android::hash_t hash() const {
839d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi        return mHash;
849d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi    }
854d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
86288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    void copyText() {
87288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        uint16_t* charsCopy = new uint16_t[mNchars];
88288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        memcpy(charsCopy, mChars, mNchars * sizeof(uint16_t));
89288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        mChars = charsCopy;
90288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
91288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    void freeText() {
92288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        delete[] mChars;
93288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        mChars = NULL;
94288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
95288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
9699ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka    void doLayout(Layout* layout, LayoutContext* ctx,
9799ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka            const std::shared_ptr<FontCollection>& collection) const {
98288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        layout->mAdvances.resize(mCount, 0);
99288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        ctx->clearHbFonts();
10099ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka        layout->doLayoutRun(mChars, mStart, mCount, mNchars, mIsRtl, ctx, collection);
101288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
102288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
1034d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienprivate:
104288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    const uint16_t* mChars;
105288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    size_t mNchars;
1064d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    size_t mStart;
1074d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    size_t mCount;
1084d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    uint32_t mId;  // for the font collection
1094d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    FontStyle mStyle;
1104d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    float mSize;
111448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien    float mScaleX;
112448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien    float mSkewX;
1138e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    float mLetterSpacing;
114893399732b64203133c22ad3c1f3535e46bf35a7Raph Levien    int32_t mPaintFlags;
1156c4d167bff33c24c239d77ddb1044b18d180766aRaph Levien    HyphenEdit mHyphenEdit;
1164d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    bool mIsRtl;
1174d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    // Note: any fields added to MinikinPaint must also be reflected here.
1184d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    // TODO: language matching (possibly integrate into style)
11914e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    android::hash_t mHash;
1209d48271c0480c27402cfba359f45964637927a63Keisuke Kuroyanagi
12114e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    android::hash_t computeHash() const;
1224d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien};
1234d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
12414e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonakaclass LayoutCache : private android::OnEntryRemoved<LayoutCacheKey, Layout*> {
1254d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienpublic:
1264d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    LayoutCache() : mCache(kMaxEntries) {
1274d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        mCache.setOnEntryRemovedListener(this);
1284d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
1294d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
130288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    void clear() {
131288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        mCache.clear();
132288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
133288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
134dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka    Layout* get(LayoutCacheKey& key, LayoutContext* ctx,
135dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka            const std::shared_ptr<FontCollection>& collection) {
136288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        Layout* layout = mCache.get(key);
137288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        if (layout == NULL) {
138288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod            key.copyText();
13999ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka            layout = new Layout();
14099ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka            key.doLayout(layout, ctx, collection);
141288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod            mCache.put(key, layout);
142288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        }
143288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        return layout;
144288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    }
145288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
146288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbodprivate:
1474d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    // callback for OnEntryRemoved
1484d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    void operator()(LayoutCacheKey& key, Layout*& value) {
149288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod        key.freeText();
1504d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        delete value;
1514d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
1524d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
15314e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    android::LruCache<LayoutCacheKey, Layout*> mCache;
154288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod
1554d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    //static const size_t kMaxEntries = LruCache<LayoutCacheKey, Layout*>::kUnlimitedCapacity;
1564d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
1574d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    // TODO: eviction based on memory footprint; for now, we just use a constant
1584d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    // number of strings
1594d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    static const size_t kMaxEntries = 5000;
1604d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien};
1614d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
1620bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournaderstatic unsigned int disabledDecomposeCompatibility(hb_unicode_funcs_t*, hb_codepoint_t,
1630bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader                                                   hb_codepoint_t*, void*) {
1640bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader    return 0;
1650bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader}
1660bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader
167fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonakaclass LayoutEngine : public ::android::Singleton<LayoutEngine> {
1684d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienpublic:
1694d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    LayoutEngine() {
1700bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader        unicodeFunctions = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
1710bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader        /* Disable the function used for compatibility decomposition */
1720bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader        hb_unicode_funcs_set_decompose_compatibility_func(
1730bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader                unicodeFunctions, disabledDecomposeCompatibility, NULL, NULL);
1744d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        hbBuffer = hb_buffer_create();
1750bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader        hb_buffer_set_unicode_funcs(hbBuffer, unicodeFunctions);
1764d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
1774d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
1784d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    hb_buffer_t* hbBuffer;
1790bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader    hb_unicode_funcs_t* unicodeFunctions;
1804d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    LayoutCache layoutCache;
1814d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien};
1824d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
1834d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienbool LayoutCacheKey::operator==(const LayoutCacheKey& other) const {
184448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien    return mId == other.mId
185448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            && mStart == other.mStart
186448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            && mCount == other.mCount
187448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            && mStyle == other.mStyle
188448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            && mSize == other.mSize
189448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            && mScaleX == other.mScaleX
190448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            && mSkewX == other.mSkewX
1918e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod            && mLetterSpacing == other.mLetterSpacing
192448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            && mPaintFlags == other.mPaintFlags
1936c4d167bff33c24c239d77ddb1044b18d180766aRaph Levien            && mHyphenEdit == other.mHyphenEdit
194448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien            && mIsRtl == other.mIsRtl
195288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod            && mNchars == other.mNchars
196288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod            && !memcmp(mChars, other.mChars, mNchars * sizeof(uint16_t));
1974d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien}
1984d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
19914e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonakaandroid::hash_t LayoutCacheKey::computeHash() const {
20014e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    uint32_t hash = android::JenkinsHashMix(0, mId);
20114e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    hash = android::JenkinsHashMix(hash, mStart);
20214e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    hash = android::JenkinsHashMix(hash, mCount);
20314e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    hash = android::JenkinsHashMix(hash, hash_type(mStyle));
20414e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    hash = android::JenkinsHashMix(hash, hash_type(mSize));
20514e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    hash = android::JenkinsHashMix(hash, hash_type(mScaleX));
20614e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    hash = android::JenkinsHashMix(hash, hash_type(mSkewX));
20714e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    hash = android::JenkinsHashMix(hash, hash_type(mLetterSpacing));
20814e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    hash = android::JenkinsHashMix(hash, hash_type(mPaintFlags));
209c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    hash = android::JenkinsHashMix(hash, hash_type(mHyphenEdit.getHyphen()));
21014e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    hash = android::JenkinsHashMix(hash, hash_type(mIsRtl));
21114e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    hash = android::JenkinsHashMixShorts(hash, mChars, mNchars);
21214e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka    return android::JenkinsHashWhiten(hash);
2134d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien}
2144d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
21514e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonakaandroid::hash_t hash_type(const LayoutCacheKey& key) {
2164d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    return key.hash();
2174d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien}
2184d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
219ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienvoid MinikinRect::join(const MinikinRect& r) {
220ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    if (isEmpty()) {
221ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        set(r);
222ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    } else if (!r.isEmpty()) {
223ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        mLeft = std::min(mLeft, r.mLeft);
224ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        mTop = std::min(mTop, r.mTop);
225ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        mRight = std::max(mRight, r.mRight);
226ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        mBottom = std::max(mBottom, r.mBottom);
227ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
228ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
229ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
2306da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbodvoid Layout::reset() {
2316da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod    mGlyphs.clear();
2326da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod    mFaces.clear();
2336da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod    mBounds.setEmpty();
2346da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod    mAdvances.clear();
2356da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod    mAdvance = 0;
2366da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod}
2376da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod
238bae347682989d2627081310129a5b60541ed6ad0Seigo Nonakastatic hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* /* hbFont */, void* fontData,
239bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka        hb_codepoint_t glyph, void* /* userData */) {
2404d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    MinikinPaint* paint = reinterpret_cast<MinikinPaint*>(fontData);
241dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka    float advance = paint->font->GetHorizontalAdvance(glyph, *paint);
242bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levien    return 256 * advance + 0.5;
2439cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien}
2449cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
245bae347682989d2627081310129a5b60541ed6ad0Seigo Nonakastatic hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* /* hbFont */, void* /* fontData */,
246bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka        hb_codepoint_t /* glyph */, hb_position_t* /* x */, hb_position_t* /* y */,
247bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka        void* /* userData */) {
2489cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    // Just return true, following the way that Harfbuzz-FreeType
2499cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    // implementation does.
2509cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    return true;
2519cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien}
2529cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
25358ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonakahb_font_funcs_t* getHbFontFuncs(bool forColorBitmapFont) {
254fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka    assertMinikinLocked();
255fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka
25658ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka    static hb_font_funcs_t* hbFuncs = nullptr;
25758ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka    static hb_font_funcs_t* hbFuncsForColorBitmap = nullptr;
25858ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka
25958ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka    hb_font_funcs_t** funcs = forColorBitmapFont ? &hbFuncs : &hbFuncsForColorBitmap;
26058ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka    if (*funcs == nullptr) {
26158ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka        *funcs = hb_font_funcs_create();
26258ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka        if (forColorBitmapFont) {
26358ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka            // Don't override the h_advance function since we use HarfBuzz's implementation for
26458ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka            // emoji for performance reasons.
26558ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka            // Note that it is technically possible for a TrueType font to have outline and embedded
26658ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka            // bitmap at the same time. We ignore modified advances of hinted outline glyphs in that
26758ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka            // case.
2680decbb3686f9fe929bed1664b11366a00520e86aSeigo Nonaka        } else {
2690decbb3686f9fe929bed1664b11366a00520e86aSeigo Nonaka            // Override the h_advance function since we can't use HarfBuzz's implemenation. It may
2700decbb3686f9fe929bed1664b11366a00520e86aSeigo Nonaka            // return the wrong value if the font uses hinting aggressively.
2710decbb3686f9fe929bed1664b11366a00520e86aSeigo Nonaka            hb_font_funcs_set_glyph_h_advance_func(*funcs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
27258ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka        }
27358ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka        hb_font_funcs_set_glyph_h_origin_func(*funcs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
27458ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka        hb_font_funcs_make_immutable(*funcs);
2759cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    }
27658ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka    return *funcs;
27758ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka}
27858ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka
27958ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonakastatic bool isColorBitmapFont(hb_font_t* font) {
28058ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka    hb_face_t* face = hb_font_get_face(font);
28158ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka    HbBlob cbdt(hb_face_reference_table(face, HB_TAG('C', 'B', 'D', 'T')));
28258ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka    return cbdt.size() > 0;
2839cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien}
2849cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
2859cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienstatic float HBFixedToFloat(hb_position_t v)
2869cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien{
2879cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    return scalbnf (v, -8);
2889cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien}
2899cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
2909cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienstatic hb_position_t HBFloatToFixed(float v)
2919cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien{
2929cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    return scalbnf (v, +8);
2939cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien}
2949cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
2959cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levienvoid Layout::dump() const {
2969cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    for (size_t i = 0; i < mGlyphs.size(); i++) {
2979cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        const LayoutGlyph& glyph = mGlyphs[i];
2989cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        std::cout << glyph.glyph_id << ": " << glyph.x << ", " << glyph.y << std::endl;
2999cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    }
3009cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien}
3019cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
302dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonakaint Layout::findFace(const FakedFont& face, LayoutContext* ctx) {
3039cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    unsigned int ix;
3049cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    for (ix = 0; ix < mFaces.size(); ix++) {
3059a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien        if (mFaces[ix].font == face.font) {
3069cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien            return ix;
3079cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        }
3089cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    }
3099cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    mFaces.push_back(face);
3104d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    // Note: ctx == NULL means we're copying from the cache, no need to create
3114d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    // corresponding hb_font object.
3124d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    if (ctx != NULL) {
31389e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka        hb_font_t* font = getHbFontLocked(face.font);
31458ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka        hb_font_set_funcs(font, getHbFontFuncs(isColorBitmapFont(font)), &ctx->paint, 0);
3154d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        ctx->hbFonts.push_back(font);
3164d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
3179cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    return ix;
3189cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien}
3199cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
320ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienstatic hb_script_t codePointToScript(hb_codepoint_t codepoint) {
3214d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    static hb_unicode_funcs_t* u = 0;
322ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    if (!u) {
3230bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader        u = LayoutEngine::getInstance().unicodeFunctions;
324ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
325ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    return hb_unicode_script(u, codepoint);
326ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
327ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
3284d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienstatic hb_codepoint_t decodeUtf16(const uint16_t* chars, size_t len, ssize_t* iter) {
329ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    const uint16_t v = chars[(*iter)++];
330ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    // test whether v in (0xd800..0xdfff), lead or trail surrogate
331ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    if ((v & 0xf800) == 0xd800) {
332ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        // test whether v in (0xd800..0xdbff), lead surrogate
333ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        if (size_t(*iter) < len && (v & 0xfc00) == 0xd800) {
334ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            const uint16_t v2 = chars[(*iter)++];
335ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            // test whether v2 in (0xdc00..0xdfff), trail surrogate
336ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            if ((v2 & 0xfc00) == 0xdc00) {
337ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                // (0xd800 0xdc00) in utf-16 maps to 0x10000 in ucs-32
338ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                const hb_codepoint_t delta = (0xd800 << 10) + 0xdc00 - 0x10000;
339ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                return (((hb_codepoint_t)v) << 10) + v2 - delta;
340ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            }
341b8208bc19c178b73175f76323381a1eeb8059807Behdad Esfahbod            (*iter) -= 1;
342b8208bc19c178b73175f76323381a1eeb8059807Behdad Esfahbod            return 0xFFFDu;
343ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        } else {
344b8208bc19c178b73175f76323381a1eeb8059807Behdad Esfahbod            return 0xFFFDu;
345ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        }
346ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    } else {
347ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        return v;
348ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
349ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
350ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
3514d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienstatic hb_script_t getScriptRun(const uint16_t* chars, size_t len, ssize_t* iter) {
352ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    if (size_t(*iter) == len) {
353ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        return HB_SCRIPT_UNKNOWN;
354ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
355ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    uint32_t cp = decodeUtf16(chars, len, iter);
356ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    hb_script_t current_script = codePointToScript(cp);
357ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    for (;;) {
358ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        if (size_t(*iter) == len)
359ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            break;
360ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        const ssize_t prev_iter = *iter;
361ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        cp = decodeUtf16(chars, len, iter);
362ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        const hb_script_t script = codePointToScript(cp);
363ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        if (script != current_script) {
364ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            if (current_script == HB_SCRIPT_INHERITED ||
365ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                current_script == HB_SCRIPT_COMMON) {
366ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                current_script = script;
367ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            } else if (script == HB_SCRIPT_INHERITED ||
368ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                script == HB_SCRIPT_COMMON) {
369ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                continue;
370ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            } else {
371ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                *iter = prev_iter;
372ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                break;
373ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            }
374ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        }
375ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
376ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    if (current_script == HB_SCRIPT_INHERITED) {
377ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien        current_script = HB_SCRIPT_COMMON;
378ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    }
379ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
380ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    return current_script;
381ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
382ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
3834d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien/**
384e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien * Disable certain scripts (mostly those with cursive connection) from having letterspacing
385e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien * applied. See https://github.com/behdad/harfbuzz/issues/64 for more details.
386e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien */
387e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levienstatic bool isScriptOkForLetterspacing(hb_script_t script) {
388e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien    return !(
389e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_ARABIC ||
390e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_NKO ||
391e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_PSALTER_PAHLAVI ||
392e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_MANDAIC ||
393e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_MONGOLIAN ||
394e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_PHAGS_PA ||
395e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_DEVANAGARI ||
396e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_BENGALI ||
397e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_GURMUKHI ||
398e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_MODI ||
399e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_SHARADA ||
400e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_SYLOTI_NAGRI ||
401e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_TIRHUTA ||
402e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            script == HB_SCRIPT_OGHAM
403e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            );
404e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien}
405e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien
4066292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagiclass BidiText {
4076292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagipublic:
4086292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    class Iter {
4096292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    public:
4106292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        struct RunInfo {
4116292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi            int32_t mRunStart;
4126292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi            int32_t mRunLength;
4136292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi            bool mIsRtl;
4146292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        };
4156292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4166292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        Iter(UBiDi* bidi, size_t start, size_t end, size_t runIndex, size_t runCount, bool isRtl);
4176292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4186292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        bool operator!= (const Iter& other) const {
4196292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi            return mIsEnd != other.mIsEnd || mNextRunIndex != other.mNextRunIndex
4206292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi                    || mBidi != other.mBidi;
4216292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        }
4226292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4236292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        const RunInfo& operator* () const {
4246292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi            return mRunInfo;
4256292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        }
4266292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4276292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        const Iter& operator++ () {
4286292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi            updateRunInfo();
4296292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi            return *this;
4306292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        }
4316292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4326292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    private:
4336292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        UBiDi* const mBidi;
4346292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        bool mIsEnd;
4356292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        size_t mNextRunIndex;
4366292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        const size_t mRunCount;
4376292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        const int32_t mStart;
4386292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        const int32_t mEnd;
4396292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        RunInfo mRunInfo;
4406292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4416292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        void updateRunInfo();
4426292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    };
4436292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4446292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    BidiText(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags);
4456292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4466292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    ~BidiText() {
4476292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        if (mBidi) {
4486292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi            ubidi_close(mBidi);
4496292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        }
4506292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
4516292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4526292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    Iter begin () const {
4536292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        return Iter(mBidi, mStart, mEnd, 0, mRunCount, mIsRtl);
4546292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
4556292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4566292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    Iter end() const {
4576292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        return Iter(mBidi, mStart, mEnd, mRunCount, mRunCount, mIsRtl);
4586292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
4596292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4606292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagiprivate:
4616292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    const size_t mStart;
4626292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    const size_t mEnd;
4636292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    const size_t mBufSize;
4646292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    UBiDi* mBidi;
4656292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    size_t mRunCount;
4666292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    bool mIsRtl;
4676292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4686261d824595d3590b5355d8dea80949769d8e38eElliott Hughes    BidiText(const BidiText&) = delete;
4696261d824595d3590b5355d8dea80949769d8e38eElliott Hughes    void operator=(const BidiText&) = delete;
4706292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi};
4716292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4726292e1a966da86af7045c356fcad6ab8864089b8Keisuke KuroyanagiBidiText::Iter::Iter(UBiDi* bidi, size_t start, size_t end, size_t runIndex, size_t runCount,
4736292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        bool isRtl)
4746292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    : mBidi(bidi), mIsEnd(runIndex == runCount), mNextRunIndex(runIndex), mRunCount(runCount),
4756292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi      mStart(start), mEnd(end), mRunInfo() {
4766292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    if (mRunCount == 1) {
4776292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        mRunInfo.mRunStart = start;
4786292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        mRunInfo.mRunLength = end - start;
4796292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        mRunInfo.mIsRtl = isRtl;
4806292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        mNextRunIndex = mRunCount;
4816292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        return;
4826292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
4836292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    updateRunInfo();
4846292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi}
4856292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
4866292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagivoid BidiText::Iter::updateRunInfo() {
4876292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    if (mNextRunIndex == mRunCount) {
4886292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        // All runs have been iterated.
4896292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        mIsEnd = true;
4906292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        return;
4916292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
4926292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    int32_t startRun = -1;
4936292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    int32_t lengthRun = -1;
4946292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    const UBiDiDirection runDir = ubidi_getVisualRun(mBidi, mNextRunIndex, &startRun, &lengthRun);
4956292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    mNextRunIndex++;
4966292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    if (startRun == -1 || lengthRun == -1) {
4976292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        ALOGE("invalid visual run");
4986292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        // skip the invalid run.
4996292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        updateRunInfo();
5006292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        return;
5016292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
5026292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    const int32_t runEnd = std::min(startRun + lengthRun, mEnd);
5036292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    mRunInfo.mRunStart = std::max(startRun, mStart);
5046292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    mRunInfo.mRunLength = runEnd - mRunInfo.mRunStart;
5056292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    if (mRunInfo.mRunLength <= 0) {
5066292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        // skip the empty run.
5076292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        updateRunInfo();
5086292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        return;
5096292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
5106292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    mRunInfo.mIsRtl = (runDir == UBIDI_RTL);
5116292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi}
5126292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
5136292e1a966da86af7045c356fcad6ab8864089b8Keisuke KuroyanagiBidiText::BidiText(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags)
5146292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    : mStart(start), mEnd(start + count), mBufSize(bufSize), mBidi(NULL), mRunCount(1),
5156292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi      mIsRtl((bidiFlags & kDirection_Mask) != 0) {
5166292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    if (bidiFlags == kBidi_Force_LTR || bidiFlags == kBidi_Force_RTL) {
5176292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        // force single run.
5186292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        return;
5196292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
5206292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    mBidi = ubidi_open();
5216292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    if (!mBidi) {
5226292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        ALOGE("error creating bidi object");
5236292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        return;
5246292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
5256292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    UErrorCode status = U_ZERO_ERROR;
526bab3b98ceb29fa3fc5d8832284312859d7f32cc7Roozbeh Pournader    // Set callbacks to override bidi classes of new emoji
527bab3b98ceb29fa3fc5d8832284312859d7f32cc7Roozbeh Pournader    ubidi_setClassCallback(mBidi, emojiBidiOverride, nullptr, nullptr, nullptr, &status);
528bab3b98ceb29fa3fc5d8832284312859d7f32cc7Roozbeh Pournader    if (!U_SUCCESS(status)) {
529bab3b98ceb29fa3fc5d8832284312859d7f32cc7Roozbeh Pournader        ALOGE("error setting bidi callback function, status = %d", status);
530bab3b98ceb29fa3fc5d8832284312859d7f32cc7Roozbeh Pournader        return;
531bab3b98ceb29fa3fc5d8832284312859d7f32cc7Roozbeh Pournader    }
532bab3b98ceb29fa3fc5d8832284312859d7f32cc7Roozbeh Pournader
5336292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    UBiDiLevel bidiReq = bidiFlags;
5346292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    if (bidiFlags == kBidi_Default_LTR) {
5356292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        bidiReq = UBIDI_DEFAULT_LTR;
5366292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    } else if (bidiFlags == kBidi_Default_RTL) {
5376292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        bidiReq = UBIDI_DEFAULT_RTL;
5386292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
5396292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    ubidi_setPara(mBidi, buf, mBufSize, bidiReq, NULL, &status);
5406292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    if (!U_SUCCESS(status)) {
5416292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        ALOGE("error calling ubidi_setPara, status = %d", status);
5426292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        return;
5436292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
5446292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    const int paraDir = ubidi_getParaLevel(mBidi) & kDirection_Mask;
5456292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    const ssize_t rc = ubidi_countRuns(mBidi, &status);
5466292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    if (!U_SUCCESS(status) || rc < 0) {
5476292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        ALOGW("error counting bidi runs, status = %d", status);
5486292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
5496292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    if (!U_SUCCESS(status) || rc <= 1) {
5506292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        mIsRtl = (paraDir == kBidi_RTL);
5516292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        return;
5526292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    }
5536292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    mRunCount = rc;
5546292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi}
5556292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi
5564d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levienvoid Layout::doLayout(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
55799ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka        int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
55899ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka        const std::shared_ptr<FontCollection>& collection) {
559fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka    android::AutoMutex _l(gMinikinLock);
560fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka
56109f1901d6befcab49ed46cb77151a5d4af14a3b9Behdad Esfahbod    LayoutContext ctx;
56209f1901d6befcab49ed46cb77151a5d4af14a3b9Behdad Esfahbod    ctx.style = style;
56309f1901d6befcab49ed46cb77151a5d4af14a3b9Behdad Esfahbod    ctx.paint = paint;
56409f1901d6befcab49ed46cb77151a5d4af14a3b9Behdad Esfahbod
5656da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod    reset();
5664d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    mAdvances.resize(count, 0);
5676da7796cbe8a17efd61a3302369e69bb222fdb4fBehdad Esfahbod
5686292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi    for (const BidiText::Iter::RunInfo& runInfo : BidiText(buf, start, count, bufSize, bidiFlags)) {
5696292e1a966da86af7045c356fcad6ab8864089b8Keisuke Kuroyanagi        doLayoutRunCached(buf, runInfo.mRunStart, runInfo.mRunLength, bufSize, runInfo.mIsRtl, &ctx,
57099ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka                start, collection, this, NULL);
5714d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
572288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    ctx.clearHbFonts();
5734d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien}
5744d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
5756344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagifloat Layout::measureText(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
5766344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
577dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka        const std::shared_ptr<FontCollection>& collection, float* advances) {
578fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka    android::AutoMutex _l(gMinikinLock);
579fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka
5806344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    LayoutContext ctx;
5816344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    ctx.style = style;
5826344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    ctx.paint = paint;
5836344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi
5846344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    float advance = 0;
5856344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    for (const BidiText::Iter::RunInfo& runInfo : BidiText(buf, start, count, bufSize, bidiFlags)) {
5866344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        float* advancesForRun = advances ? advances + (runInfo.mRunStart - start) : advances;
5876344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        advance += doLayoutRunCached(buf, runInfo.mRunStart, runInfo.mRunLength, bufSize,
5886344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi                runInfo.mIsRtl, &ctx, 0, collection, NULL, advancesForRun);
5896344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    }
5906344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi
5916344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    ctx.clearHbFonts();
5926344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    return advance;
5936344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi}
5946344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi
5956344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagifloat Layout::doLayoutRunCached(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
596dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka        bool isRtl, LayoutContext* ctx, size_t dstStart,
597dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka        const std::shared_ptr<FontCollection>& collection, Layout* layout, float* advances) {
598c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    const uint32_t originalHyphen = ctx->paint.hyphenEdit.getHyphen();
5996344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    float advance = 0;
6004d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    if (!isRtl) {
6014d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        // left to right
6024e3adc6fb2073d5b466b88b8f5329d281038aba1Seigo Nonaka        size_t wordstart =
6034e3adc6fb2073d5b466b88b8f5329d281038aba1Seigo Nonaka                start == bufSize ? start : getPrevWordBreakForCache(buf, start + 1, bufSize);
6044d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        size_t wordend;
6054d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        for (size_t iter = start; iter < start + count; iter = wordend) {
6064e3adc6fb2073d5b466b88b8f5329d281038aba1Seigo Nonaka            wordend = getNextWordBreakForCache(buf, iter, bufSize);
607c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // Only apply hyphen to the first or last word in the string.
608c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            uint32_t hyphen = originalHyphen;
609c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            if (iter != start) { // Not the first word
610c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                hyphen &= ~HyphenEdit::MASK_START_OF_LINE;
611c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            }
612c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            if (wordend < start + count) { // Not the last word
613c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                hyphen &= ~HyphenEdit::MASK_END_OF_LINE;
614c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            }
615c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            ctx->paint.hyphenEdit = hyphen;
6163f1ea5da2ee12b0d95c17c56928c3e553d4eeda0Raph Levien            size_t wordcount = std::min(start + count, wordend) - iter;
6176344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi            advance += doLayoutWord(buf + wordstart, iter - wordstart, wordcount,
6186344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi                    wordend - wordstart, isRtl, ctx, iter - dstStart, collection, layout,
6196344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi                    advances ? advances + (iter - start) : advances);
6204d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            wordstart = wordend;
6214d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        }
6224d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    } else {
6234d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        // right to left
6244d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        size_t wordstart;
6254d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        size_t end = start + count;
6264e3adc6fb2073d5b466b88b8f5329d281038aba1Seigo Nonaka        size_t wordend = end == 0 ? 0 : getNextWordBreakForCache(buf, end - 1, bufSize);
6274d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        for (size_t iter = end; iter > start; iter = wordstart) {
6284e3adc6fb2073d5b466b88b8f5329d281038aba1Seigo Nonaka            wordstart = getPrevWordBreakForCache(buf, iter, bufSize);
629c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // Only apply hyphen to the first (rightmost) or last (leftmost) word in the string.
630c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            uint32_t hyphen = originalHyphen;
631c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            if (wordstart > start) { // Not the first word
632c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                hyphen &= ~HyphenEdit::MASK_START_OF_LINE;
633c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            }
634c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            if (iter != end) { // Not the last word
635c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                hyphen &= ~HyphenEdit::MASK_END_OF_LINE;
636c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            }
637c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            ctx->paint.hyphenEdit = hyphen;
6383f1ea5da2ee12b0d95c17c56928c3e553d4eeda0Raph Levien            size_t bufStart = std::max(start, wordstart);
6396344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi            advance += doLayoutWord(buf + wordstart, bufStart - wordstart, iter - bufStart,
6406344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi                    wordend - wordstart, isRtl, ctx, bufStart - dstStart, collection, layout,
6416344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi                    advances ? advances + (bufStart - start) : advances);
6424d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            wordend = wordstart;
6434d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        }
64486fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levien    }
6456344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    return advance;
64686fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levien}
64786fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levien
6486344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagifloat Layout::doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
649dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka        bool isRtl, LayoutContext* ctx, size_t bufStart,
650dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka        const std::shared_ptr<FontCollection>& collection, Layout* layout, float* advances) {
651fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka    LayoutCache& cache = LayoutEngine::getInstance().layoutCache;
6526344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi    LayoutCacheKey key(collection, ctx->paint, ctx->style, buf, start, count, bufSize, isRtl);
653acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka
654acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka    float wordSpacing = count == 1 && isWordSpace(buf[start]) ? ctx->paint.wordSpacing : 0;
655acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka
656acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka    float advance;
657acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka    if (ctx->paint.skipCache()) {
65899ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka        Layout layoutForWord;
65999ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka        key.doLayout(&layoutForWord, ctx, collection);
6606344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        if (layout) {
661acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka            layout->appendLayout(&layoutForWord, bufStart, wordSpacing);
6626344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        }
6636344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        if (advances) {
6646344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi            layoutForWord.getAdvances(advances);
6656344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        }
666acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka        advance = layoutForWord.getAdvance();
667288c915963b3500c7efb958ba613650e2ecdfdfaBehdad Esfahbod    } else {
668fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka        Layout* layoutForWord = cache.get(key, ctx, collection);
6696344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        if (layout) {
670acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka            layout->appendLayout(layoutForWord, bufStart, wordSpacing);
6716344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        }
6726344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        if (advances) {
6736344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi            layoutForWord->getAdvances(advances);
6746344de60fa37c4e4246cb37d82215564826c2b8bKeisuke Kuroyanagi        }
675acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka        advance = layoutForWord->getAdvance();
6764d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
677acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka
678acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka    if (wordSpacing != 0) {
679acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka        advance += wordSpacing;
680acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka        if (advances) {
681acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka            advances[0] += wordSpacing;
682acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka        }
683acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka    }
684acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka    return advance;
6854d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien}
6864d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
6875986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbodstatic void addFeatures(const string &str, vector<hb_feature_t>* features) {
6885986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod    if (!str.size())
6895986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod        return;
69067ea671fe421d0e4642caef619ec39ec86bdcaefRaph Levien
6915986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod    const char* start = str.c_str();
6925986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod    const char* end = start + str.size();
6935986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod
6945986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod    while (start < end) {
6955986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod        static hb_feature_t feature;
6965986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod        const char* p = strchr(start, ',');
6975986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod        if (!p)
6985986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod            p = end;
6995986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod        /* We do not allow setting features on ranges.  As such, reject any
7005986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod         * setting that has non-universal range. */
7015986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod        if (hb_feature_from_string (start, p - start, &feature)
7025986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod                && feature.start == 0 && feature.end == (unsigned int) -1)
7035986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod            features->push_back(feature);
7045986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod        start = p + 1;
7055986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod    }
7064043f6f6d9c584bc61bc3d81d1680bf1b558330eRaph Levien}
7074043f6f6d9c584bc61bc3d81d1680bf1b558330eRaph Levien
708829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournaderstatic const hb_codepoint_t CHAR_HYPHEN = 0x2010; /* HYPHEN */
709829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader
710c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournaderstatic inline hb_codepoint_t determineHyphenChar(hb_codepoint_t preferredHyphen, hb_font_t* font) {
711829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader    hb_codepoint_t glyph;
712829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader    if (preferredHyphen == 0x058A /* ARMENIAN_HYPHEN */
713c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                || preferredHyphen == 0x05BE /* HEBREW PUNCTUATION MAQAF */
714c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                || preferredHyphen == 0x1400 /* CANADIAN SYLLABIC HYPHEN */) {
715829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader        if (hb_font_get_nominal_glyph(font, preferredHyphen, &glyph)) {
716829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader            return preferredHyphen;
717829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader        } else {
718829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader            // The original hyphen requested was not supported. Let's try and see if the
719829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader            // Unicode hyphen is supported.
720829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader            preferredHyphen = CHAR_HYPHEN;
721829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader        }
722829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader    }
723829f28371c4c4926965a5dbaec256f7e76e415f8Roozbeh Pournader    if (preferredHyphen == CHAR_HYPHEN) { /* HYPHEN */
724c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // Fallback to ASCII HYPHEN-MINUS if the font didn't have a glyph for the preferred hyphen.
725c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // Note that we intentionally don't do anything special if the font doesn't have a
726c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // HYPHEN-MINUS either, so a tofu could be shown, hinting towards something missing.
727c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        if (!hb_font_get_nominal_glyph(font, preferredHyphen, &glyph)) {
728c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            return 0x002D; // HYPHEN-MINUS
729c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        }
730c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    }
731c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    return preferredHyphen;
732c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader}
733c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
734c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournaderstatic inline void addHyphenToHbBuffer(hb_buffer_t* buffer, hb_font_t* font, uint32_t hyphen,
735c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        uint32_t cluster) {
736c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    const uint32_t* hyphenStr = HyphenEdit::getHyphenString(hyphen);
737c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    while (*hyphenStr != 0) {
738c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        hb_codepoint_t hyphenChar = determineHyphenChar(*hyphenStr, font);
739c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        hb_buffer_add(buffer, hyphenChar, cluster);
740c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        hyphenStr++;
741c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    }
742c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader}
743c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
744c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader// Returns the cluster value assigned to the first codepoint added to the buffer, which can be used
745c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader// to translate cluster values returned by HarfBuzz to input indices.
746c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournaderstatic inline uint32_t addToHbBuffer(hb_buffer_t* buffer,
747c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        const uint16_t* buf, size_t start, size_t count, size_t bufSize,
748c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        ssize_t scriptRunStart, ssize_t scriptRunEnd,
749c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        HyphenEdit hyphenEdit, hb_font_t* hbFont) {
750c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
751c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // Only hyphenate the very first script run for starting hyphens.
752c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    const uint32_t startHyphen = (scriptRunStart == 0)
753c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            ? hyphenEdit.getStart()
754c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            : HyphenEdit::NO_EDIT;
755c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // Only hyphenate the very last script run for ending hyphens.
756c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    const uint32_t endHyphen = (static_cast<size_t>(scriptRunEnd) == count)
757c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            ? hyphenEdit.getEnd()
758c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            : HyphenEdit::NO_EDIT;
759c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
760c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // In the following code, we drop the pre-context and/or post-context if there is a
761c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // hyphen edit at that end. This is not absolutely necessary, since HarfBuzz uses
762c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // contexts only for joining scripts at the moment, e.g. to determine if the first or
763c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // last letter of a text range to shape should take a joining form based on an
764c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // adjacent letter or joiner (that comes from the context).
765c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    //
766c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // TODO: Revisit this for:
767c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // 1. Desperate breaks for joining scripts like Arabic (where it may be better to keep
768c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    //    the context);
769c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // 2. Special features like start-of-word font features (not implemented in HarfBuzz
770c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    //    yet).
771c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
772c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // We don't have any start-of-line replacement edit yet, so we don't need to check for
773c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // those.
774c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    if (HyphenEdit::isInsertion(startHyphen)) {
775c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // A cluster value of zero guarantees that the inserted hyphen will be in the same
776c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // cluster with the next codepoint, since there is no pre-context.
777c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        addHyphenToHbBuffer(buffer, hbFont, startHyphen, 0 /* cluster value */);
778c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    }
779c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
780c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    const uint16_t* hbText;
781c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    int hbTextLength;
782c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    unsigned int hbItemOffset;
783c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    unsigned int hbItemLength = scriptRunEnd - scriptRunStart; // This is >= 1.
784c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
785c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    const bool hasEndInsertion = HyphenEdit::isInsertion(endHyphen);
786c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    const bool hasEndReplacement = HyphenEdit::isReplacement(endHyphen);
787c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    if (hasEndReplacement) {
788c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // Skip the last code unit while copying the buffer for HarfBuzz if it's a replacement. We
789c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // don't need to worry about non-BMP characters yet since replacements are only done for
790c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // code units at the moment.
791c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        hbItemLength -= 1;
792c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    }
793c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
794c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    if (startHyphen == HyphenEdit::NO_EDIT) {
795c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // No edit at the beginning. Use the whole pre-context.
796c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        hbText = buf;
797c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        hbItemOffset = start + scriptRunStart;
798c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    } else {
799c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // There's an edit at the beginning. Drop the pre-context and start the buffer at where we
800c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // want to start shaping.
801c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        hbText = buf + start + scriptRunStart;
802c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        hbItemOffset = 0;
803c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    }
804c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
805c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    if (endHyphen == HyphenEdit::NO_EDIT) {
806c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // No edit at the end, use the whole post-context.
807c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        hbTextLength = (buf + bufSize) - hbText;
808c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    } else {
809c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // There is an edit at the end. Drop the post-context.
810c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        hbTextLength = hbItemOffset + hbItemLength;
811c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    }
812c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
813c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    hb_buffer_add_utf16(buffer, hbText, hbTextLength, hbItemOffset, hbItemLength);
814c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
815c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    unsigned int numCodepoints;
816c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    hb_glyph_info_t* cpInfo = hb_buffer_get_glyph_infos(buffer, &numCodepoints);
817c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
818c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    // Add the hyphen at the end, if there's any.
819c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    if (hasEndInsertion || hasEndReplacement) {
820c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // When a hyphen is inserted, by assigning the added hyphen and the last
821c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // codepoint added to the HarfBuzz buffer to the same cluster, we can make sure
822c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // that they always remain in the same cluster, even if the last codepoint gets
823c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // merged into another cluster (for example when it's a combining mark).
824c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        //
825c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // When a replacement happens instead, we want it to get the cluster value of
826c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // the character it's replacing, which is one "codepoint length" larger than
827c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // the last cluster. But since the character replaced is always just one
828c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // code unit, we can just add 1.
829c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        uint32_t hyphenCluster;
830c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        if (numCodepoints == 0) {
831c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // Nothing was added to the HarfBuzz buffer. This can only happen if
832c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // we have a replacement that is replacing a one-code unit script run.
833c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            hyphenCluster = 0;
834c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        } else {
835c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            hyphenCluster = cpInfo[numCodepoints - 1].cluster + (uint32_t) hasEndReplacement;
836c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        }
837c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        addHyphenToHbBuffer(buffer, hbFont, endHyphen, hyphenCluster);
838c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // Since we have just added to the buffer, cpInfo no longer necessarily points to
839c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // the right place. Refresh it.
840c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        cpInfo = hb_buffer_get_glyph_infos(buffer, nullptr /* we don't need the size */);
841c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    }
842c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader    return cpInfo[0].cluster;
843c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader}
844c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
845c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
84686fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levienvoid Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
84799ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka        bool isRtl, LayoutContext* ctx, const std::shared_ptr<FontCollection>& collection) {
8484d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    hb_buffer_t* buffer = LayoutEngine::getInstance().hbBuffer;
84986fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levien    vector<FontCollection::Run> items;
85099ef32cef014231c0d544e8d4f97449695d429c0Seigo Nonaka    collection->itemize(buf + start, count, ctx->style, &items);
85186fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levien
8524043f6f6d9c584bc61bc3d81d1680bf1b558330eRaph Levien    vector<hb_feature_t> features;
8538e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    // Disable default-on non-required ligature features if letter-spacing
8548e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property
8558e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    // "When the effective spacing between two characters is not zero (due to
8568e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    // either justification or a non-zero value of letter-spacing), user agents
8578e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    // should not apply optional ligatures."
8588e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    if (fabs(ctx->paint.letterSpacing) > 0.03)
8598e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    {
8608e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod        static const hb_feature_t no_liga = { HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u };
8618e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod        static const hb_feature_t no_clig = { HB_TAG('c', 'l', 'i', 'g'), 0, 0, ~0u };
8628e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod        features.push_back(no_liga);
8638e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod        features.push_back(no_clig);
8648e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    }
8655986f6048ae21e0ec094c1f2ca0169d0ca6ec6b5Behdad Esfahbod    addFeatures(ctx->paint.fontFeatureSettings, &features);
8664043f6f6d9c584bc61bc3d81d1680bf1b558330eRaph Levien
8678e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    double size = ctx->paint.size;
8688e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod    double scaleX = ctx->paint.scaleX;
8698e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod
87086fa46c5ebb0d2c3319e08f4fbf487d8c2abbbfcRaph Levien    float x = mAdvance;
8719cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    float y = 0;
872dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka    for (int run_ix = isRtl ? items.size() - 1 : 0;
873dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka            isRtl ? run_ix >= 0 : run_ix < static_cast<int>(items.size());
874dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonaka            isRtl ? --run_ix : ++run_ix) {
8759cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        FontCollection::Run &run = items[run_ix];
8769a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien        if (run.fakedFont.font == NULL) {
8777c382381191b2280b53c375fe83dfc6217bbdfa9Raph Levien            ALOGE("no font for run starting u+%04x length %d", buf[run.start], run.end - run.start);
8787c382381191b2280b53c375fe83dfc6217bbdfa9Raph Levien            continue;
8797c382381191b2280b53c375fe83dfc6217bbdfa9Raph Levien        }
8809a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien        int font_ix = findFace(run.fakedFont, ctx);
8819a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien        ctx->paint.font = mFaces[font_ix].font;
8829a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien        ctx->paint.fakery = mFaces[font_ix].fakery;
8834d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        hb_font_t* hbFont = ctx->hbFonts[font_ix];
8842967a13420ffd1d426e6edd1648414910593d179Roozbeh Pournader#ifdef VERBOSE_DEBUG
885bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka        ALOGD("Run %zu, font %d [%d:%d]", run_ix, font_ix, run.start, run.end);
8869cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien#endif
8878e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod
888448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien        hb_font_set_ppem(hbFont, size * scaleX, size);
889448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien        hb_font_set_scale(hbFont, HBFloatToFixed(size * scaleX), HBFloatToFixed(size));
8909cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
89158ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka        const bool is_color_bitmap_font = isColorBitmapFont(hbFont);
89258ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka
893d231a4b0b1d482c7ae7717b048112e1fe5d0f5a9Raph Levien        // TODO: if there are multiple scripts within a font in an RTL run,
894d231a4b0b1d482c7ae7717b048112e1fe5d0f5a9Raph Levien        // we need to reorder those runs. This is unlikely with our current
895d231a4b0b1d482c7ae7717b048112e1fe5d0f5a9Raph Levien        // font stack, but should be done for correctness.
896c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
897c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // Note: scriptRunStart and scriptRunEnd, as well as run.start and run.end, run between 0
898c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        // and count.
899c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        ssize_t scriptRunEnd;
900c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader        for (ssize_t scriptRunStart = run.start;
901c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                scriptRunStart < run.end;
902c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                scriptRunStart = scriptRunEnd) {
903c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            scriptRunEnd = scriptRunStart;
904c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            hb_script_t script = getScriptRun(buf + start, run.end, &scriptRunEnd /* iterator */);
905c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // After the last line, scriptRunEnd is guaranteed to have increased, since the only
906c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // time getScriptRun does not increase its iterator is when it has already reached the
907c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // end of the buffer. But that can't happen, since if we have already reached the end
908c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // of the buffer, we should have had (scriptRunEnd == run.end), which means
909c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // (scriptRunStart == run.end) which is impossible due to the exit condition of the for
910c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // loop. So we can be sure that scriptRunEnd > scriptRunStart.
911ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
912e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            double letterSpace = 0.0;
913e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            double letterSpaceHalfLeft = 0.0;
914e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            double letterSpaceHalfRight = 0.0;
915e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien
916e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            if (ctx->paint.letterSpacing != 0.0 && isScriptOkForLetterspacing(script)) {
917e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien                letterSpace = ctx->paint.letterSpacing * size * scaleX;
918e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien                if ((ctx->paint.paintFlags & LinearTextFlag) == 0) {
919e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien                    letterSpace = round(letterSpace);
920e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien                    letterSpaceHalfLeft = floor(letterSpace * 0.5);
921e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien                } else {
922e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien                    letterSpaceHalfLeft = letterSpace * 0.5;
923e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien                }
924e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien                letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
925e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien            }
926e8721b2d293ba1a0e5d92a066aa9aa15807357ccRaph Levien
9270bbff3a96d3836079371cdd4398c21afad3c5234Roozbeh Pournader            hb_buffer_clear_contents(buffer);
928ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            hb_buffer_set_script(buffer, script);
929d231a4b0b1d482c7ae7717b048112e1fe5d0f5a9Raph Levien            hb_buffer_set_direction(buffer, isRtl? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
9306d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka            const FontLanguages& langList =
931fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka                    FontLanguageListCache::getById(ctx->style.getLanguageListId());
9326d9dcd2cf3d3ed26a886e02d94c907311e7b1f83Seigo Nonaka            if (langList.size() != 0) {
933198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka                const FontLanguage* hbLanguage = &langList[0];
934198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka                for (size_t i = 0; i < langList.size(); ++i) {
935198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka                    if (langList[i].supportsHbScript(script)) {
936198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka                        hbLanguage = &langList[i];
937198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka                        break;
938198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka                    }
939198b46f1fea3f47ef8eb6317799c0d77aaec52f6Seigo Nonaka                }
94022462be7358f1facbe0c0074f8e58a41c2314b6eSeigo Nonaka                hb_buffer_set_language(buffer, hbLanguage->getHbLanguage());
9417b221d97b7b64dc5ce457e19666d55d042e22e62Raph Levien            }
942c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
943c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            const uint32_t clusterStart = addToHbBuffer(
944c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                buffer,
945c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                buf, start, count, bufSize,
946c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                scriptRunStart, scriptRunEnd,
947c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                ctx->paint.hyphenEdit, hbFont);
948c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
9494043f6f6d9c584bc61bc3d81d1680bf1b558330eRaph Levien            hb_shape(hbFont, buffer, features.empty() ? NULL : &features[0], features.size());
950ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            unsigned int numGlyphs;
9514d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
9524d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien            hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
953c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
954c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // At this point in the code, the cluster values in the info buffer correspond to the
955c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // input characters with some shift. The cluster value clusterStart corresponds to the
956c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // first character passed to HarfBuzz, which is at buf[start + scriptRunStart] whose
957c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // advance needs to be saved into mAdvances[scriptRunStart]. So cluster values need to
958c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // be reduced by (clusterStart - scriptRunStart) to get converted to indices of
959c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            // mAdvances.
960c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader            const ssize_t clusterOffset = clusterStart - scriptRunStart;
961c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader
9628e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod            if (numGlyphs)
9638e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod            {
964c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                mAdvances[info[0].cluster - clusterOffset] += letterSpaceHalfLeft;
9656740536e3927d25bf5c2567e5f6e8c175973cbb7Raph Levien                x += letterSpaceHalfLeft;
9668e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod            }
967ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            for (unsigned int i = 0; i < numGlyphs; i++) {
9682967a13420ffd1d426e6edd1648414910593d179Roozbeh Pournader#ifdef VERBOSE_DEBUG
9692967a13420ffd1d426e6edd1648414910593d179Roozbeh Pournader                ALOGD("%d %d %d %d",
9702967a13420ffd1d426e6edd1648414910593d179Roozbeh Pournader                        positions[i].x_advance, positions[i].y_advance,
9712967a13420ffd1d426e6edd1648414910593d179Roozbeh Pournader                        positions[i].x_offset, positions[i].y_offset);
9722967a13420ffd1d426e6edd1648414910593d179Roozbeh Pournader                ALOGD("DoLayout %u: %f; %d, %d",
9732967a13420ffd1d426e6edd1648414910593d179Roozbeh Pournader                        info[i].codepoint, HBFixedToFloat(positions[i].x_advance),
9742967a13420ffd1d426e6edd1648414910593d179Roozbeh Pournader                        positions[i].x_offset, positions[i].y_offset);
9752967a13420ffd1d426e6edd1648414910593d179Roozbeh Pournader#endif
9768e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod                if (i > 0 && info[i - 1].cluster != info[i].cluster) {
977c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                    mAdvances[info[i - 1].cluster - clusterOffset] += letterSpaceHalfRight;
978c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                    mAdvances[info[i].cluster - clusterOffset] += letterSpaceHalfLeft;
9796740536e3927d25bf5c2567e5f6e8c175973cbb7Raph Levien                    x += letterSpace;
9808e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod                }
9818e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod
982ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                hb_codepoint_t glyph_ix = info[i].codepoint;
983ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                float xoff = HBFixedToFloat(positions[i].x_offset);
984448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien                float yoff = -HBFixedToFloat(positions[i].y_offset);
985448b0fd720d7ba902b9be224a287d08abe3ebea8Raph Levien                xoff += yoff * ctx->paint.skewX;
986ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                LayoutGlyph glyph = {font_ix, glyph_ix, x + xoff, y + yoff};
987ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                mGlyphs.push_back(glyph);
988ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                float xAdvance = HBFixedToFloat(positions[i].x_advance);
9896740536e3927d25bf5c2567e5f6e8c175973cbb7Raph Levien                if ((ctx->paint.paintFlags & LinearTextFlag) == 0) {
9906740536e3927d25bf5c2567e5f6e8c175973cbb7Raph Levien                    xAdvance = roundf(xAdvance);
9916740536e3927d25bf5c2567e5f6e8c175973cbb7Raph Levien                }
992ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                MinikinRect glyphBounds;
99358ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                hb_glyph_extents_t extents = {};
99458ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                if (is_color_bitmap_font && hb_font_get_glyph_extents(hbFont, glyph_ix, &extents)) {
99558ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                    // Note that it is technically possible for a TrueType font to have outline and
99658ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                    // embedded bitmap at the same time. We ignore modified bbox of hinted outline
99758ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                    // glyphs in that case.
99858ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                    glyphBounds.mLeft = roundf(HBFixedToFloat(extents.x_bearing));
99958ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                    glyphBounds.mTop = roundf(HBFixedToFloat(-extents.y_bearing));
100058ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                    glyphBounds.mRight = roundf(HBFixedToFloat(extents.x_bearing + extents.width));
100158ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                    glyphBounds.mBottom =
100258ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                            roundf(HBFixedToFloat(-extents.y_bearing - extents.height));
100358ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                } else {
100458ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                    ctx->paint.font->GetBounds(&glyphBounds, glyph_ix, ctx->paint);
100558ee6af0c3f5ccef659119c8ffbeb8536fbb59e0Seigo Nonaka                }
1006ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                glyphBounds.offset(x + xoff, y + yoff);
1007ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                mBounds.join(glyphBounds);
1008c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                if (static_cast<size_t>(info[i].cluster - clusterOffset) < count) {
1009c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                    mAdvances[info[i].cluster - clusterOffset] += xAdvance;
1010d692d6a9791145d41d7778cdf6b40b20c2be8cb4Raph Levien                } else {
1011bae347682989d2627081310129a5b60541ed6ad0Seigo Nonaka                    ALOGE("cluster %zu (start %zu) out of bounds of count %zu",
1012c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                        info[i].cluster - clusterOffset, start, count);
1013d692d6a9791145d41d7778cdf6b40b20c2be8cb4Raph Levien                }
1014ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien                x += xAdvance;
1015ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien            }
10168e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod            if (numGlyphs)
10178e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod            {
1018c7ef4000c1e840c3d3b66e85a40ebd34a5a2a8eeRoozbeh Pournader                mAdvances[info[numGlyphs - 1].cluster - clusterOffset] += letterSpaceHalfRight;
10196740536e3927d25bf5c2567e5f6e8c175973cbb7Raph Levien                x += letterSpaceHalfRight;
10208e7a3dae37e9a22b2c054aec852615843d71caf6Behdad Esfahbod            }
10219cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien        }
10229cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien    }
1023bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levien    mAdvance = x;
10249cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien}
10259cc9bbe1461f359f0b27c5e7645c17dda001ab1dRaph Levien
1026acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonakavoid Layout::appendLayout(Layout* src, size_t start, float extraAdvance) {
102756c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod    int fontMapStack[16];
102856c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod    int* fontMap;
102956c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod    if (src->mFaces.size() < sizeof(fontMapStack) / sizeof(fontMapStack[0])) {
103056c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod        fontMap = fontMapStack;
103156c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod    } else {
103256c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod        fontMap = new int[src->mFaces.size()];
103356c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod    }
10344d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    for (size_t i = 0; i < src->mFaces.size(); i++) {
10354d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        int font_ix = findFace(src->mFaces[i], NULL);
103656c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod        fontMap[i] = font_ix;
10374d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
10384d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    int x0 = mAdvance;
10394d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    for (size_t i = 0; i < src->mGlyphs.size(); i++) {
10404d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        LayoutGlyph& srcGlyph = src->mGlyphs[i];
10414d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        int font_ix = fontMap[srcGlyph.font_ix];
10424d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        unsigned int glyph_id = srcGlyph.glyph_id;
10434d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        float x = x0 + srcGlyph.x;
10444d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        float y = srcGlyph.y;
10454d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        LayoutGlyph glyph = {font_ix, glyph_id, x, y};
10464d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        mGlyphs.push_back(glyph);
10474d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
10484d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    for (size_t i = 0; i < src->mAdvances.size(); i++) {
10494d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien        mAdvances[i + start] = src->mAdvances[i];
1050acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka        if (i == 0)
1051acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka          mAdvances[i + start] += extraAdvance;
10524d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    }
10534d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    MinikinRect srcBounds(src->mBounds);
10544d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    srcBounds.offset(x0, 0);
10554d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien    mBounds.join(srcBounds);
1056acd401d02981af51419f4b740abb2c41e4980fdbSeigo Nonaka    mAdvance += src->mAdvance + extraAdvance;
105756c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod
105856c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod    if (fontMap != fontMapStack) {
105956c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod        delete[] fontMap;
106056c7fb8c0633f1b02115bb4370854016f8c84fb4Behdad Esfahbod    }
10614d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien}
10624d4e6bc8118d15542f1f2a9218f0f7a91a29474fRaph Levien
1063ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Leviensize_t Layout::nGlyphs() const {
1064ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    return mGlyphs.size();
1065ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
1066ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
1067dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonakaconst MinikinFont* Layout::getFont(int i) const {
1068ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    const LayoutGlyph& glyph = mGlyphs[i];
10699a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien    return mFaces[glyph.font_ix].font;
10709a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien}
10719a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien
10729a5f713add8cfb91ac2c9ed5c917309053201ab6Raph LevienFontFakery Layout::getFakery(int i) const {
10739a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien    const LayoutGlyph& glyph = mGlyphs[i];
10749a5f713add8cfb91ac2c9ed5c917309053201ab6Raph Levien    return mFaces[glyph.font_ix].fakery;
1075ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
1076ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
1077ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienunsigned int Layout::getGlyphId(int i) const {
1078ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    const LayoutGlyph& glyph = mGlyphs[i];
1079ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    return glyph.glyph_id;
1080ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
1081ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
1082ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienfloat Layout::getX(int i) const {
1083ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    const LayoutGlyph& glyph = mGlyphs[i];
1084ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    return glyph.x;
1085ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
1086ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
1087ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienfloat Layout::getY(int i) const {
1088ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    const LayoutGlyph& glyph = mGlyphs[i];
1089ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    return glyph.y;
1090ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
1091ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
1092bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levienfloat Layout::getAdvance() const {
1093bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levien    return mAdvance;
1094bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levien}
1095bcc3dc5a2591a95a57e379e27cbad69c18e91e67Raph Levien
1096ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levienvoid Layout::getAdvances(float* advances) {
1097ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    memcpy(advances, &mAdvances[0], mAdvances.size() * sizeof(float));
1098ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
1099ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
1100dfbc6e374259f9d81940b5195ac013b02429af27Seigo Nonakavoid Layout::getBounds(MinikinRect* bounds) const {
1101ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien    bounds->set(mBounds);
1102ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien}
1103ecc2d34ac23a497988f21e5f415b53c007b9d8c5Raph Levien
110422e41754f6470ff1f4c0e0a56d01f7f555b59e21Raph Levienvoid Layout::purgeCaches() {
1105fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka    android::AutoMutex _l(gMinikinLock);
1106fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka    LayoutCache& layoutCache = LayoutEngine::getInstance().layoutCache;
1107fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka    layoutCache.clear();
110889e80237bc27af084c9ff316d4f47abf426eced8Seigo Nonaka    purgeHbFontCacheLocked();
110922e41754f6470ff1f4c0e0a56d01f7f555b59e21Raph Levien}
111022e41754f6470ff1f4c0e0a56d01f7f555b59e21Raph Levien
111114e2d136aaef271ba131f917cf5f27baa31ae5adSeigo Nonaka}  // namespace minikin
1112fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka
1113fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka// Unable to define the static data member outside of android.
1114fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka// TODO: introduce our own Singleton to drop android namespace.
1115fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonakanamespace android {
1116fd4124c53399581dd94eac5a9749bc07b474a294Seigo NonakaANDROID_SINGLETON_STATIC_INSTANCE(minikin::LayoutEngine);
1117fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka}  // namespace android
1118fd4124c53399581dd94eac5a9749bc07b474a294Seigo Nonaka
1119