Layout.cpp 33.2 KB
Newer Older
R
Raph Levien 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

17 18 19
#define LOG_TAG "Minikin"
#include <cutils/log.h>

20 21
#include <math.h>

R
Raph Levien 已提交
22
#include <algorithm>
R
Raph Levien 已提交
23 24
#include <fstream>
#include <iostream>  // for debugging
25 26
#include <string>
#include <vector>
R
Raph Levien 已提交
27

28 29 30 31 32
#include <utils/JenkinsHash.h>
#include <utils/LruCache.h>
#include <utils/Singleton.h>
#include <utils/String16.h>

R
Raph Levien 已提交
33
#include <unicode/ubidi.h>
34
#include <hb-icu.h>
35
#include <hb-ot.h>
36

37
#include "FontLanguage.h"
S
Seigo Nonaka 已提交
38
#include "FontLanguageListCache.h"
39
#include "LayoutUtils.h"
40
#include "HbFontCache.h"
R
Raph Levien 已提交
41
#include "MinikinInternal.h"
R
Raph Levien 已提交
42
#include <minikin/MinikinFontFreeType.h>
R
Raph Levien 已提交
43 44 45 46 47
#include <minikin/Layout.h>

using std::string;
using std::vector;

J
John Reck 已提交
48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
namespace minikin {

Bitmap::Bitmap(int width, int height) : width(width), height(height) {
    buf = new uint8_t[width * height]();
}

Bitmap::~Bitmap() {
    delete[] buf;
}

void Bitmap::writePnm(std::ofstream &o) const {
    o << "P5" << std::endl;
    o << width << " " << height << std::endl;
    o << "255" << std::endl;
    o.write((const char *)buf, width * height);
    o.close();
}

S
Seigo Nonaka 已提交
66
void Bitmap::drawGlyph(const GlyphBitmap& bitmap, int x, int y) {
J
John Reck 已提交
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
    int bmw = bitmap.width;
    int bmh = bitmap.height;
    x += bitmap.left;
    y -= bitmap.top;
    int x0 = std::max(0, x);
    int x1 = std::min(width, x + bmw);
    int y0 = std::max(0, y);
    int y1 = std::min(height, y + bmh);
    const unsigned char* src = bitmap.buffer + (y0 - y) * bmw + (x0 - x);
    uint8_t* dst = buf + y0 * width;
    for (int yy = y0; yy < y1; yy++) {
        for (int xx = x0; xx < x1; xx++) {
            int pixel = (int)dst[xx] + (int)src[xx - x];
            pixel = pixel > 0xff ? 0xff : pixel;
            dst[xx] = pixel;
        }
        src += bmw;
        dst += width;
    }
}

R
Raph Levien 已提交
88 89
const int kDirection_Mask = 0x1;

B
Behdad Esfahbod 已提交
90 91 92 93 94 95 96
struct LayoutContext {
    MinikinPaint paint;
    FontStyle style;
    std::vector<hb_font_t*> hbFonts;  // parallel to mFaces

    void clearHbFonts() {
        for (size_t i = 0; i < hbFonts.size(); i++) {
97
            hb_font_set_funcs(hbFonts[i], nullptr, nullptr, nullptr);
R
Raph Levien 已提交
98
            hb_font_destroy(hbFonts[i]);
B
Behdad Esfahbod 已提交
99 100 101 102 103
        }
        hbFonts.clear();
    }
};

104 105 106 107 108 109
// Layout cache datatypes

class LayoutCacheKey {
public:
    LayoutCacheKey(const FontCollection* collection, const MinikinPaint& paint, FontStyle style,
            const uint16_t* chars, size_t start, size_t count, size_t nchars, bool dir)
110 111
            : mChars(chars), mNchars(nchars),
            mStart(start), mCount(count), mId(collection->getId()), mStyle(style),
R
Raph Levien 已提交
112
            mSize(paint.size), mScaleX(paint.scaleX), mSkewX(paint.skewX),
B
Behdad Esfahbod 已提交
113
            mLetterSpacing(paint.letterSpacing),
114 115
            mPaintFlags(paint.paintFlags), mHyphenEdit(paint.hyphenEdit), mIsRtl(dir),
            mHash(computeHash()) {
116 117
    }
    bool operator==(const LayoutCacheKey &other) const;
118

S
Seigo Nonaka 已提交
119
    android::hash_t hash() const {
120 121
        return mHash;
    }
122

B
Behdad Esfahbod 已提交
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
    void copyText() {
        uint16_t* charsCopy = new uint16_t[mNchars];
        memcpy(charsCopy, mChars, mNchars * sizeof(uint16_t));
        mChars = charsCopy;
    }
    void freeText() {
        delete[] mChars;
        mChars = NULL;
    }

    void doLayout(Layout* layout, LayoutContext* ctx, const FontCollection* collection) const {
        layout->setFontCollection(collection);
        layout->mAdvances.resize(mCount, 0);
        ctx->clearHbFonts();
        layout->doLayoutRun(mChars, mStart, mCount, mNchars, mIsRtl, ctx);
    }

140
private:
B
Behdad Esfahbod 已提交
141 142
    const uint16_t* mChars;
    size_t mNchars;
143 144 145 146 147
    size_t mStart;
    size_t mCount;
    uint32_t mId;  // for the font collection
    FontStyle mStyle;
    float mSize;
R
Raph Levien 已提交
148 149
    float mScaleX;
    float mSkewX;
B
Behdad Esfahbod 已提交
150
    float mLetterSpacing;
R
Raph Levien 已提交
151
    int32_t mPaintFlags;
R
Raph Levien 已提交
152
    HyphenEdit mHyphenEdit;
153 154 155
    bool mIsRtl;
    // Note: any fields added to MinikinPaint must also be reflected here.
    // TODO: language matching (possibly integrate into style)
S
Seigo Nonaka 已提交
156
    android::hash_t mHash;
157

S
Seigo Nonaka 已提交
158
    android::hash_t computeHash() const;
159 160
};

S
Seigo Nonaka 已提交
161
class LayoutCache : private android::OnEntryRemoved<LayoutCacheKey, Layout*> {
162 163 164 165 166
public:
    LayoutCache() : mCache(kMaxEntries) {
        mCache.setOnEntryRemovedListener(this);
    }

B
Behdad Esfahbod 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
    void clear() {
        mCache.clear();
    }

    Layout* get(LayoutCacheKey& key, LayoutContext* ctx, const FontCollection* collection) {
        Layout* layout = mCache.get(key);
        if (layout == NULL) {
            key.copyText();
            layout = new Layout();
            key.doLayout(layout, ctx, collection);
            mCache.put(key, layout);
        }
        return layout;
    }

private:
183 184
    // callback for OnEntryRemoved
    void operator()(LayoutCacheKey& key, Layout*& value) {
B
Behdad Esfahbod 已提交
185
        key.freeText();
186 187 188
        delete value;
    }

S
Seigo Nonaka 已提交
189
    android::LruCache<LayoutCacheKey, Layout*> mCache;
B
Behdad Esfahbod 已提交
190

191 192 193 194 195 196 197
    //static const size_t kMaxEntries = LruCache<LayoutCacheKey, Layout*>::kUnlimitedCapacity;

    // TODO: eviction based on memory footprint; for now, we just use a constant
    // number of strings
    static const size_t kMaxEntries = 5000;
};

198 199 200 201 202
static unsigned int disabledDecomposeCompatibility(hb_unicode_funcs_t*, hb_codepoint_t,
                                                   hb_codepoint_t*, void*) {
    return 0;
}

S
Seigo Nonaka 已提交
203
class LayoutEngine : public ::android::Singleton<LayoutEngine> {
204 205
public:
    LayoutEngine() {
206 207 208 209
        unicodeFunctions = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
        /* Disable the function used for compatibility decomposition */
        hb_unicode_funcs_set_decompose_compatibility_func(
                unicodeFunctions, disabledDecomposeCompatibility, NULL, NULL);
210
        hbBuffer = hb_buffer_create();
211
        hb_buffer_set_unicode_funcs(hbBuffer, unicodeFunctions);
212 213 214
    }

    hb_buffer_t* hbBuffer;
215
    hb_unicode_funcs_t* unicodeFunctions;
216 217 218 219
    LayoutCache layoutCache;
};

bool LayoutCacheKey::operator==(const LayoutCacheKey& other) const {
R
Raph Levien 已提交
220 221 222 223 224 225 226
    return mId == other.mId
            && mStart == other.mStart
            && mCount == other.mCount
            && mStyle == other.mStyle
            && mSize == other.mSize
            && mScaleX == other.mScaleX
            && mSkewX == other.mSkewX
B
Behdad Esfahbod 已提交
227
            && mLetterSpacing == other.mLetterSpacing
R
Raph Levien 已提交
228
            && mPaintFlags == other.mPaintFlags
R
Raph Levien 已提交
229
            && mHyphenEdit == other.mHyphenEdit
R
Raph Levien 已提交
230
            && mIsRtl == other.mIsRtl
B
Behdad Esfahbod 已提交
231 232
            && mNchars == other.mNchars
            && !memcmp(mChars, other.mChars, mNchars * sizeof(uint16_t));
233 234
}

S
Seigo Nonaka 已提交
235 236 237 238 239 240 241 242 243 244 245 246 247 248
android::hash_t LayoutCacheKey::computeHash() const {
    uint32_t hash = android::JenkinsHashMix(0, mId);
    hash = android::JenkinsHashMix(hash, mStart);
    hash = android::JenkinsHashMix(hash, mCount);
    hash = android::JenkinsHashMix(hash, hash_type(mStyle));
    hash = android::JenkinsHashMix(hash, hash_type(mSize));
    hash = android::JenkinsHashMix(hash, hash_type(mScaleX));
    hash = android::JenkinsHashMix(hash, hash_type(mSkewX));
    hash = android::JenkinsHashMix(hash, hash_type(mLetterSpacing));
    hash = android::JenkinsHashMix(hash, hash_type(mPaintFlags));
    hash = android::JenkinsHashMix(hash, hash_type(mHyphenEdit.hasHyphen()));
    hash = android::JenkinsHashMix(hash, hash_type(mIsRtl));
    hash = android::JenkinsHashMixShorts(hash, mChars, mNchars);
    return android::JenkinsHashWhiten(hash);
249 250
}

S
Seigo Nonaka 已提交
251
android::hash_t hash_type(const LayoutCacheKey& key) {
252 253 254
    return key.hash();
}

255 256 257 258 259 260 261 262 263 264 265
void MinikinRect::join(const MinikinRect& r) {
    if (isEmpty()) {
        set(r);
    } else if (!r.isEmpty()) {
        mLeft = std::min(mLeft, r.mLeft);
        mTop = std::min(mTop, r.mTop);
        mRight = std::max(mRight, r.mRight);
        mBottom = std::max(mBottom, r.mBottom);
    }
}

266
// Deprecated. Remove when callers are removed.
R
Raph Levien 已提交
267 268 269
void Layout::init() {
}

270 271 272 273 274 275 276 277
void Layout::reset() {
    mGlyphs.clear();
    mFaces.clear();
    mBounds.setEmpty();
    mAdvances.clear();
    mAdvance = 0;
}

278
void Layout::setFontCollection(const FontCollection* collection) {
R
Raph Levien 已提交
279 280 281
    mCollection = collection;
}

282 283
static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* /* hbFont */, void* fontData,
        hb_codepoint_t glyph, void* /* userData */) {
284
    MinikinPaint* paint = reinterpret_cast<MinikinPaint*>(fontData);
R
Raph Levien 已提交
285 286 287
    MinikinFont* font = paint->font;
    float advance = font->GetHorizontalAdvance(glyph, *paint);
    return 256 * advance + 0.5;
R
Raph Levien 已提交
288 289
}

290 291 292
static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* /* hbFont */, void* /* fontData */,
        hb_codepoint_t /* glyph */, hb_position_t* /* x */, hb_position_t* /* y */,
        void* /* userData */) {
R
Raph Levien 已提交
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
    // Just return true, following the way that Harfbuzz-FreeType
    // implementation does.
    return true;
}

hb_font_funcs_t* getHbFontFuncs() {
    static hb_font_funcs_t* hbFontFuncs = 0;

    if (hbFontFuncs == 0) {
        hbFontFuncs = hb_font_funcs_create();
        hb_font_funcs_set_glyph_h_advance_func(hbFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
        hb_font_funcs_set_glyph_h_origin_func(hbFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
        hb_font_funcs_make_immutable(hbFontFuncs);
    }
    return hbFontFuncs;
}

static float HBFixedToFloat(hb_position_t v)
{
    return scalbnf (v, -8);
}

static hb_position_t HBFloatToFixed(float v)
{
    return scalbnf (v, +8);
}

void Layout::dump() const {
    for (size_t i = 0; i < mGlyphs.size(); i++) {
        const LayoutGlyph& glyph = mGlyphs[i];
        std::cout << glyph.glyph_id << ": " << glyph.x << ", " << glyph.y << std::endl;
    }
}

R
Raph Levien 已提交
327
int Layout::findFace(FakedFont face, LayoutContext* ctx) {
R
Raph Levien 已提交
328 329
    unsigned int ix;
    for (ix = 0; ix < mFaces.size(); ix++) {
R
Raph Levien 已提交
330
        if (mFaces[ix].font == face.font) {
R
Raph Levien 已提交
331 332 333 334
            return ix;
        }
    }
    mFaces.push_back(face);
335 336 337
    // Note: ctx == NULL means we're copying from the cache, no need to create
    // corresponding hb_font object.
    if (ctx != NULL) {
338 339
        hb_font_t* font = getHbFontLocked(face.font);
        hb_font_set_funcs(font, getHbFontFuncs(), &ctx->paint, 0);
340 341
        ctx->hbFonts.push_back(font);
    }
R
Raph Levien 已提交
342 343 344
    return ix;
}

345
static hb_script_t codePointToScript(hb_codepoint_t codepoint) {
346
    static hb_unicode_funcs_t* u = 0;
347
    if (!u) {
348
        u = LayoutEngine::getInstance().unicodeFunctions;
349 350 351 352
    }
    return hb_unicode_script(u, codepoint);
}

353
static hb_codepoint_t decodeUtf16(const uint16_t* chars, size_t len, ssize_t* iter) {
354 355 356 357 358 359 360 361 362 363 364 365
    const uint16_t v = chars[(*iter)++];
    // test whether v in (0xd800..0xdfff), lead or trail surrogate
    if ((v & 0xf800) == 0xd800) {
        // test whether v in (0xd800..0xdbff), lead surrogate
        if (size_t(*iter) < len && (v & 0xfc00) == 0xd800) {
            const uint16_t v2 = chars[(*iter)++];
            // test whether v2 in (0xdc00..0xdfff), trail surrogate
            if ((v2 & 0xfc00) == 0xdc00) {
                // (0xd800 0xdc00) in utf-16 maps to 0x10000 in ucs-32
                const hb_codepoint_t delta = (0xd800 << 10) + 0xdc00 - 0x10000;
                return (((hb_codepoint_t)v) << 10) + v2 - delta;
            }
366 367
            (*iter) -= 1;
            return 0xFFFDu;
368
        } else {
369
            return 0xFFFDu;
370 371 372 373 374 375
        }
    } else {
        return v;
    }
}

376
static hb_script_t getScriptRun(const uint16_t* chars, size_t len, ssize_t* iter) {
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407
    if (size_t(*iter) == len) {
        return HB_SCRIPT_UNKNOWN;
    }
    uint32_t cp = decodeUtf16(chars, len, iter);
    hb_script_t current_script = codePointToScript(cp);
    for (;;) {
        if (size_t(*iter) == len)
            break;
        const ssize_t prev_iter = *iter;
        cp = decodeUtf16(chars, len, iter);
        const hb_script_t script = codePointToScript(cp);
        if (script != current_script) {
            if (current_script == HB_SCRIPT_INHERITED ||
                current_script == HB_SCRIPT_COMMON) {
                current_script = script;
            } else if (script == HB_SCRIPT_INHERITED ||
                script == HB_SCRIPT_COMMON) {
                continue;
            } else {
                *iter = prev_iter;
                break;
            }
        }
    }
    if (current_script == HB_SCRIPT_INHERITED) {
        current_script = HB_SCRIPT_COMMON;
    }

    return current_script;
}

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430
/**
 * Disable certain scripts (mostly those with cursive connection) from having letterspacing
 * applied. See https://github.com/behdad/harfbuzz/issues/64 for more details.
 */
static bool isScriptOkForLetterspacing(hb_script_t script) {
    return !(
            script == HB_SCRIPT_ARABIC ||
            script == HB_SCRIPT_NKO ||
            script == HB_SCRIPT_PSALTER_PAHLAVI ||
            script == HB_SCRIPT_MANDAIC ||
            script == HB_SCRIPT_MONGOLIAN ||
            script == HB_SCRIPT_PHAGS_PA ||
            script == HB_SCRIPT_DEVANAGARI ||
            script == HB_SCRIPT_BENGALI ||
            script == HB_SCRIPT_GURMUKHI ||
            script == HB_SCRIPT_MODI ||
            script == HB_SCRIPT_SHARADA ||
            script == HB_SCRIPT_SYLOTI_NAGRI ||
            script == HB_SCRIPT_TIRHUTA ||
            script == HB_SCRIPT_OGHAM
            );
}

431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492
class BidiText {
public:
    class Iter {
    public:
        struct RunInfo {
            int32_t mRunStart;
            int32_t mRunLength;
            bool mIsRtl;
        };

        Iter(UBiDi* bidi, size_t start, size_t end, size_t runIndex, size_t runCount, bool isRtl);

        bool operator!= (const Iter& other) const {
            return mIsEnd != other.mIsEnd || mNextRunIndex != other.mNextRunIndex
                    || mBidi != other.mBidi;
        }

        const RunInfo& operator* () const {
            return mRunInfo;
        }

        const Iter& operator++ () {
            updateRunInfo();
            return *this;
        }

    private:
        UBiDi* const mBidi;
        bool mIsEnd;
        size_t mNextRunIndex;
        const size_t mRunCount;
        const int32_t mStart;
        const int32_t mEnd;
        RunInfo mRunInfo;

        void updateRunInfo();
    };

    BidiText(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags);

    ~BidiText() {
        if (mBidi) {
            ubidi_close(mBidi);
        }
    }

    Iter begin () const {
        return Iter(mBidi, mStart, mEnd, 0, mRunCount, mIsRtl);
    }

    Iter end() const {
        return Iter(mBidi, mStart, mEnd, mRunCount, mRunCount, mIsRtl);
    }

private:
    const size_t mStart;
    const size_t mEnd;
    const size_t mBufSize;
    UBiDi* mBidi;
    size_t mRunCount;
    bool mIsRtl;

493 494
    BidiText(const BidiText&) = delete;
    void operator=(const BidiText&) = delete;
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573
};

BidiText::Iter::Iter(UBiDi* bidi, size_t start, size_t end, size_t runIndex, size_t runCount,
        bool isRtl)
    : mBidi(bidi), mIsEnd(runIndex == runCount), mNextRunIndex(runIndex), mRunCount(runCount),
      mStart(start), mEnd(end), mRunInfo() {
    if (mRunCount == 1) {
        mRunInfo.mRunStart = start;
        mRunInfo.mRunLength = end - start;
        mRunInfo.mIsRtl = isRtl;
        mNextRunIndex = mRunCount;
        return;
    }
    updateRunInfo();
}

void BidiText::Iter::updateRunInfo() {
    if (mNextRunIndex == mRunCount) {
        // All runs have been iterated.
        mIsEnd = true;
        return;
    }
    int32_t startRun = -1;
    int32_t lengthRun = -1;
    const UBiDiDirection runDir = ubidi_getVisualRun(mBidi, mNextRunIndex, &startRun, &lengthRun);
    mNextRunIndex++;
    if (startRun == -1 || lengthRun == -1) {
        ALOGE("invalid visual run");
        // skip the invalid run.
        updateRunInfo();
        return;
    }
    const int32_t runEnd = std::min(startRun + lengthRun, mEnd);
    mRunInfo.mRunStart = std::max(startRun, mStart);
    mRunInfo.mRunLength = runEnd - mRunInfo.mRunStart;
    if (mRunInfo.mRunLength <= 0) {
        // skip the empty run.
        updateRunInfo();
        return;
    }
    mRunInfo.mIsRtl = (runDir == UBIDI_RTL);
}

BidiText::BidiText(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags)
    : mStart(start), mEnd(start + count), mBufSize(bufSize), mBidi(NULL), mRunCount(1),
      mIsRtl((bidiFlags & kDirection_Mask) != 0) {
    if (bidiFlags == kBidi_Force_LTR || bidiFlags == kBidi_Force_RTL) {
        // force single run.
        return;
    }
    mBidi = ubidi_open();
    if (!mBidi) {
        ALOGE("error creating bidi object");
        return;
    }
    UErrorCode status = U_ZERO_ERROR;
    UBiDiLevel bidiReq = bidiFlags;
    if (bidiFlags == kBidi_Default_LTR) {
        bidiReq = UBIDI_DEFAULT_LTR;
    } else if (bidiFlags == kBidi_Default_RTL) {
        bidiReq = UBIDI_DEFAULT_RTL;
    }
    ubidi_setPara(mBidi, buf, mBufSize, bidiReq, NULL, &status);
    if (!U_SUCCESS(status)) {
        ALOGE("error calling ubidi_setPara, status = %d", status);
        return;
    }
    const int paraDir = ubidi_getParaLevel(mBidi) & kDirection_Mask;
    const ssize_t rc = ubidi_countRuns(mBidi, &status);
    if (!U_SUCCESS(status) || rc < 0) {
        ALOGW("error counting bidi runs, status = %d", status);
    }
    if (!U_SUCCESS(status) || rc <= 1) {
        mIsRtl = (paraDir == kBidi_RTL);
        return;
    }
    mRunCount = rc;
}

574 575
void Layout::doLayout(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
        int bidiFlags, const FontStyle &style, const MinikinPaint &paint) {
S
Seigo Nonaka 已提交
576
    android::AutoMutex _l(gMinikinLock);
577 578 579 580 581

    LayoutContext ctx;
    ctx.style = style;
    ctx.paint = paint;

582
    reset();
583
    mAdvances.resize(count, 0);
584

585 586
    for (const BidiText::Iter::RunInfo& runInfo : BidiText(buf, start, count, bufSize, bidiFlags)) {
        doLayoutRunCached(buf, runInfo.mRunStart, runInfo.mRunLength, bufSize, runInfo.mIsRtl, &ctx,
587
                start, mCollection, this, NULL);
588
    }
B
Behdad Esfahbod 已提交
589
    ctx.clearHbFonts();
590 591
}

592 593 594
float Layout::measureText(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
        int bidiFlags, const FontStyle &style, const MinikinPaint &paint,
        const FontCollection* collection, float* advances) {
S
Seigo Nonaka 已提交
595
    android::AutoMutex _l(gMinikinLock);
596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614

    LayoutContext ctx;
    ctx.style = style;
    ctx.paint = paint;

    float advance = 0;
    for (const BidiText::Iter::RunInfo& runInfo : BidiText(buf, start, count, bufSize, bidiFlags)) {
        float* advancesForRun = advances ? advances + (runInfo.mRunStart - start) : advances;
        advance += doLayoutRunCached(buf, runInfo.mRunStart, runInfo.mRunLength, bufSize,
                runInfo.mIsRtl, &ctx, 0, collection, NULL, advancesForRun);
    }

    ctx.clearHbFonts();
    return advance;
}

float Layout::doLayoutRunCached(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
        bool isRtl, LayoutContext* ctx, size_t dstStart, const FontCollection* collection,
        Layout* layout, float* advances) {
615
    HyphenEdit hyphen = ctx->paint.hyphenEdit;
616
    float advance = 0;
617 618
    if (!isRtl) {
        // left to right
619 620
        size_t wordstart =
                start == bufSize ? start : getPrevWordBreakForCache(buf, start + 1, bufSize);
621 622
        size_t wordend;
        for (size_t iter = start; iter < start + count; iter = wordend) {
623
            wordend = getNextWordBreakForCache(buf, iter, bufSize);
624 625
            // Only apply hyphen to the last word in the string.
            ctx->paint.hyphenEdit = wordend >= start + count ? hyphen : HyphenEdit();
R
Raph Levien 已提交
626
            size_t wordcount = std::min(start + count, wordend) - iter;
627 628 629
            advance += doLayoutWord(buf + wordstart, iter - wordstart, wordcount,
                    wordend - wordstart, isRtl, ctx, iter - dstStart, collection, layout,
                    advances ? advances + (iter - start) : advances);
630 631 632 633 634 635
            wordstart = wordend;
        }
    } else {
        // right to left
        size_t wordstart;
        size_t end = start + count;
636
        size_t wordend = end == 0 ? 0 : getNextWordBreakForCache(buf, end - 1, bufSize);
637
        for (size_t iter = end; iter > start; iter = wordstart) {
638
            wordstart = getPrevWordBreakForCache(buf, iter, bufSize);
639 640
            // Only apply hyphen to the last (leftmost) word in the string.
            ctx->paint.hyphenEdit = iter == end ? hyphen : HyphenEdit();
R
Raph Levien 已提交
641
            size_t bufStart = std::max(start, wordstart);
642 643 644
            advance += doLayoutWord(buf + wordstart, bufStart - wordstart, iter - bufStart,
                    wordend - wordstart, isRtl, ctx, bufStart - dstStart, collection, layout,
                    advances ? advances + (bufStart - start) : advances);
645 646
            wordend = wordstart;
        }
R
Raph Levien 已提交
647
    }
648
    return advance;
R
Raph Levien 已提交
649 650
}

651 652 653
float Layout::doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
        bool isRtl, LayoutContext* ctx, size_t bufStart, const FontCollection* collection,
        Layout* layout, float* advances) {
654
    LayoutCache& cache = LayoutEngine::getInstance().layoutCache;
655
    LayoutCacheKey key(collection, ctx->paint, ctx->style, buf, start, count, bufSize, isRtl);
B
Behdad Esfahbod 已提交
656
    bool skipCache = ctx->paint.skipCache();
B
Behdad Esfahbod 已提交
657
    if (skipCache) {
658 659 660 661 662 663 664 665 666
        Layout layoutForWord;
        key.doLayout(&layoutForWord, ctx, collection);
        if (layout) {
            layout->appendLayout(&layoutForWord, bufStart);
        }
        if (advances) {
            layoutForWord.getAdvances(advances);
        }
        return layoutForWord.getAdvance();
B
Behdad Esfahbod 已提交
667
    } else {
668 669 670 671 672 673 674 675
        Layout* layoutForWord = cache.get(key, ctx, collection);
        if (layout) {
            layout->appendLayout(layoutForWord, bufStart);
        }
        if (advances) {
            layoutForWord->getAdvances(advances);
        }
        return layoutForWord->getAdvance();
676 677 678
    }
}

B
Behdad Esfahbod 已提交
679 680 681
static void addFeatures(const string &str, vector<hb_feature_t>* features) {
    if (!str.size())
        return;
R
Raph Levien 已提交
682

B
Behdad Esfahbod 已提交
683 684 685 686 687 688 689 690 691 692 693 694 695 696 697
    const char* start = str.c_str();
    const char* end = start + str.size();

    while (start < end) {
        static hb_feature_t feature;
        const char* p = strchr(start, ',');
        if (!p)
            p = end;
        /* We do not allow setting features on ranges.  As such, reject any
         * setting that has non-universal range. */
        if (hb_feature_from_string (start, p - start, &feature)
                && feature.start == 0 && feature.end == (unsigned int) -1)
            features->push_back(feature);
        start = p + 1;
    }
698 699
}

R
Raph Levien 已提交
700
void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
701 702
        bool isRtl, LayoutContext* ctx) {
    hb_buffer_t* buffer = LayoutEngine::getInstance().hbBuffer;
R
Raph Levien 已提交
703
    vector<FontCollection::Run> items;
704
    mCollection->itemize(buf + start, count, ctx->style, &items);
R
Raph Levien 已提交
705 706 707 708
    if (isRtl) {
        std::reverse(items.begin(), items.end());
    }

709
    vector<hb_feature_t> features;
B
Behdad Esfahbod 已提交
710 711 712 713 714 715 716 717 718 719 720 721
    // Disable default-on non-required ligature features if letter-spacing
    // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property
    // "When the effective spacing between two characters is not zero (due to
    // either justification or a non-zero value of letter-spacing), user agents
    // should not apply optional ligatures."
    if (fabs(ctx->paint.letterSpacing) > 0.03)
    {
        static const hb_feature_t no_liga = { HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u };
        static const hb_feature_t no_clig = { HB_TAG('c', 'l', 'i', 'g'), 0, 0, ~0u };
        features.push_back(no_liga);
        features.push_back(no_clig);
    }
B
Behdad Esfahbod 已提交
722
    addFeatures(ctx->paint.fontFeatureSettings, &features);
723

B
Behdad Esfahbod 已提交
724 725 726
    double size = ctx->paint.size;
    double scaleX = ctx->paint.scaleX;

R
Raph Levien 已提交
727
    float x = mAdvance;
R
Raph Levien 已提交
728 729 730
    float y = 0;
    for (size_t run_ix = 0; run_ix < items.size(); run_ix++) {
        FontCollection::Run &run = items[run_ix];
R
Raph Levien 已提交
731
        if (run.fakedFont.font == NULL) {
732 733 734
            ALOGE("no font for run starting u+%04x length %d", buf[run.start], run.end - run.start);
            continue;
        }
R
Raph Levien 已提交
735 736 737
        int font_ix = findFace(run.fakedFont, ctx);
        ctx->paint.font = mFaces[font_ix].font;
        ctx->paint.fakery = mFaces[font_ix].fakery;
738
        hb_font_t* hbFont = ctx->hbFonts[font_ix];
739
#ifdef VERBOSE_DEBUG
740
        ALOGD("Run %zu, font %d [%d:%d]", run_ix, font_ix, run.start, run.end);
R
Raph Levien 已提交
741
#endif
B
Behdad Esfahbod 已提交
742

R
Raph Levien 已提交
743 744
        hb_font_set_ppem(hbFont, size * scaleX, size);
        hb_font_set_scale(hbFont, HBFloatToFixed(size * scaleX), HBFloatToFixed(size));
R
Raph Levien 已提交
745

R
Raph Levien 已提交
746 747 748
        // TODO: if there are multiple scripts within a font in an RTL run,
        // we need to reorder those runs. This is unlikely with our current
        // font stack, but should be done for correctness.
R
Raph Levien 已提交
749 750
        ssize_t srunend;
        for (ssize_t srunstart = run.start; srunstart < run.end; srunstart = srunend) {
751
            srunend = srunstart;
R
Raph Levien 已提交
752
            hb_script_t script = getScriptRun(buf + start, run.end, &srunend);
753

754 755 756 757 758 759 760 761 762 763 764 765 766 767 768
            double letterSpace = 0.0;
            double letterSpaceHalfLeft = 0.0;
            double letterSpaceHalfRight = 0.0;

            if (ctx->paint.letterSpacing != 0.0 && isScriptOkForLetterspacing(script)) {
                letterSpace = ctx->paint.letterSpacing * size * scaleX;
                if ((ctx->paint.paintFlags & LinearTextFlag) == 0) {
                    letterSpace = round(letterSpace);
                    letterSpaceHalfLeft = floor(letterSpace * 0.5);
                } else {
                    letterSpaceHalfLeft = letterSpace * 0.5;
                }
                letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
            }

769
            hb_buffer_clear_contents(buffer);
770
            hb_buffer_set_script(buffer, script);
R
Raph Levien 已提交
771
            hb_buffer_set_direction(buffer, isRtl? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
S
Seigo Nonaka 已提交
772 773 774
            const FontLanguages& langList =
                    FontLanguageListCache::getById(ctx->style.getLanguageListId());
            if (langList.size() != 0) {
775 776 777 778 779 780 781 782 783
                const FontLanguage* hbLanguage = &langList[0];
                for (size_t i = 0; i < langList.size(); ++i) {
                    if (langList[i].supportsHbScript(script)) {
                        hbLanguage = &langList[i];
                        break;
                    }
                }
                hb_buffer_set_language(buffer,
                        hb_language_from_string(hbLanguage->getString().c_str(), -1));
R
Raph Levien 已提交
784
            }
R
Raph Levien 已提交
785
            hb_buffer_add_utf16(buffer, buf, bufSize, srunstart + start, srunend - srunstart);
786 787 788
            if (ctx->paint.hyphenEdit.hasHyphen() && srunend > srunstart) {
                // TODO: check whether this is really the desired semantics. It could have the
                // effect of assigning the hyphen width to a nonspacing mark
789
                unsigned int lastCluster = start + srunend - 1;
790 791 792 793 794 795 796 797 798 799 800

                hb_codepoint_t hyphenChar = 0x2010; // HYPHEN
                hb_codepoint_t glyph;
                // Fallback to ASCII HYPHEN-MINUS if the font didn't have a glyph for HYPHEN. Note
                // that we intentionally don't do anything special if the font doesn't have a
                // HYPHEN-MINUS either, so a tofu could be shown, hinting towards something
                // missing.
                if (!hb_font_get_glyph(hbFont, hyphenChar, 0, &glyph)) {
                    hyphenChar = 0x002D; // HYPHEN-MINUS
                }
                hb_buffer_add(buffer, hyphenChar, lastCluster);
801
            }
802
            hb_shape(hbFont, buffer, features.empty() ? NULL : &features[0], features.size());
803
            unsigned int numGlyphs;
804 805
            hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
            hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
B
Behdad Esfahbod 已提交
806 807
            if (numGlyphs)
            {
R
Raph Levien 已提交
808 809
                mAdvances[info[0].cluster - start] += letterSpaceHalfLeft;
                x += letterSpaceHalfLeft;
B
Behdad Esfahbod 已提交
810
            }
811
            for (unsigned int i = 0; i < numGlyphs; i++) {
812 813 814 815 816 817 818 819
#ifdef VERBOSE_DEBUG
                ALOGD("%d %d %d %d",
                        positions[i].x_advance, positions[i].y_advance,
                        positions[i].x_offset, positions[i].y_offset);
                ALOGD("DoLayout %u: %f; %d, %d",
                        info[i].codepoint, HBFixedToFloat(positions[i].x_advance),
                        positions[i].x_offset, positions[i].y_offset);
#endif
B
Behdad Esfahbod 已提交
820
                if (i > 0 && info[i - 1].cluster != info[i].cluster) {
R
Raph Levien 已提交
821 822 823
                    mAdvances[info[i - 1].cluster - start] += letterSpaceHalfRight;
                    mAdvances[info[i].cluster - start] += letterSpaceHalfLeft;
                    x += letterSpace;
B
Behdad Esfahbod 已提交
824 825
                }

826 827
                hb_codepoint_t glyph_ix = info[i].codepoint;
                float xoff = HBFixedToFloat(positions[i].x_offset);
R
Raph Levien 已提交
828 829
                float yoff = -HBFixedToFloat(positions[i].y_offset);
                xoff += yoff * ctx->paint.skewX;
830 831 832
                LayoutGlyph glyph = {font_ix, glyph_ix, x + xoff, y + yoff};
                mGlyphs.push_back(glyph);
                float xAdvance = HBFixedToFloat(positions[i].x_advance);
R
Raph Levien 已提交
833 834 835
                if ((ctx->paint.paintFlags & LinearTextFlag) == 0) {
                    xAdvance = roundf(xAdvance);
                }
836
                MinikinRect glyphBounds;
837
                ctx->paint.font->GetBounds(&glyphBounds, glyph_ix, ctx->paint);
838 839
                glyphBounds.offset(x + xoff, y + yoff);
                mBounds.join(glyphBounds);
840 841 842
                if (info[i].cluster - start < count) {
                    mAdvances[info[i].cluster - start] += xAdvance;
                } else {
843
                    ALOGE("cluster %zu (start %zu) out of bounds of count %zu",
844 845
                        info[i].cluster - start, start, count);
                }
846 847
                x += xAdvance;
            }
B
Behdad Esfahbod 已提交
848 849
            if (numGlyphs)
            {
R
Raph Levien 已提交
850 851
                mAdvances[info[numGlyphs - 1].cluster - start] += letterSpaceHalfRight;
                x += letterSpaceHalfRight;
B
Behdad Esfahbod 已提交
852
            }
R
Raph Levien 已提交
853 854
        }
    }
R
Raph Levien 已提交
855
    mAdvance = x;
R
Raph Levien 已提交
856 857
}

858
void Layout::appendLayout(Layout* src, size_t start) {
B
Behdad Esfahbod 已提交
859 860 861 862 863 864 865
    int fontMapStack[16];
    int* fontMap;
    if (src->mFaces.size() < sizeof(fontMapStack) / sizeof(fontMapStack[0])) {
        fontMap = fontMapStack;
    } else {
        fontMap = new int[src->mFaces.size()];
    }
866 867
    for (size_t i = 0; i < src->mFaces.size(); i++) {
        int font_ix = findFace(src->mFaces[i], NULL);
B
Behdad Esfahbod 已提交
868
        fontMap[i] = font_ix;
869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886
    }
    int x0 = mAdvance;
    for (size_t i = 0; i < src->mGlyphs.size(); i++) {
        LayoutGlyph& srcGlyph = src->mGlyphs[i];
        int font_ix = fontMap[srcGlyph.font_ix];
        unsigned int glyph_id = srcGlyph.glyph_id;
        float x = x0 + srcGlyph.x;
        float y = srcGlyph.y;
        LayoutGlyph glyph = {font_ix, glyph_id, x, y};
        mGlyphs.push_back(glyph);
    }
    for (size_t i = 0; i < src->mAdvances.size(); i++) {
        mAdvances[i + start] = src->mAdvances[i];
    }
    MinikinRect srcBounds(src->mBounds);
    srcBounds.offset(x0, 0);
    mBounds.join(srcBounds);
    mAdvance += src->mAdvance;
B
Behdad Esfahbod 已提交
887 888 889 890

    if (fontMap != fontMapStack) {
        delete[] fontMap;
    }
891 892
}

J
John Reck 已提交
893
void Layout::draw(minikin::Bitmap* surface, int x0, int y0, float size) const {
R
Raph Levien 已提交
894 895
    /*
    TODO: redo as MinikinPaint settings
R
Raph Levien 已提交
896 897 898 899 900
    if (mProps.hasTag(minikinHinting)) {
        int hintflags = mProps.value(minikinHinting).getIntValue();
        if (hintflags & 1) load_flags |= FT_LOAD_NO_HINTING;
        if (hintflags & 2) load_flags |= FT_LOAD_NO_AUTOHINT;
    }
R
Raph Levien 已提交
901
    */
R
Raph Levien 已提交
902 903
    for (size_t i = 0; i < mGlyphs.size(); i++) {
        const LayoutGlyph& glyph = mGlyphs[i];
R
Raph Levien 已提交
904
        MinikinFont* mf = mFaces[glyph.font_ix].font;
905
        MinikinFontFreeType* face = static_cast<MinikinFontFreeType*>(mf);
R
Raph Levien 已提交
906 907
        GlyphBitmap glyphBitmap;
        MinikinPaint paint;
908
        paint.size = size;
R
Raph Levien 已提交
909
        bool ok = face->Render(glyph.glyph_id, paint, &glyphBitmap);
910 911
#ifdef VERBOSE_DEBUG
        ALOGD("glyphBitmap.width=%d, glyphBitmap.height=%d (%d, %d) x=%f, y=%f, ok=%d",
R
Raph Levien 已提交
912
            glyphBitmap.width, glyphBitmap.height, glyphBitmap.left, glyphBitmap.top, glyph.x, glyph.y, ok);
913
#endif
R
Raph Levien 已提交
914 915 916 917
        if (ok) {
            surface->drawGlyph(glyphBitmap,
                x0 + int(floor(glyph.x + 0.5)), y0 + int(floor(glyph.y + 0.5)));
        }
R
Raph Levien 已提交
918 919 920
    }
}

921 922 923 924
size_t Layout::nGlyphs() const {
    return mGlyphs.size();
}

925
MinikinFont* Layout::getFont(int i) const {
926
    const LayoutGlyph& glyph = mGlyphs[i];
R
Raph Levien 已提交
927 928 929 930 931 932
    return mFaces[glyph.font_ix].font;
}

FontFakery Layout::getFakery(int i) const {
    const LayoutGlyph& glyph = mGlyphs[i];
    return mFaces[glyph.font_ix].fakery;
933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949
}

unsigned int Layout::getGlyphId(int i) const {
    const LayoutGlyph& glyph = mGlyphs[i];
    return glyph.glyph_id;
}

float Layout::getX(int i) const {
    const LayoutGlyph& glyph = mGlyphs[i];
    return glyph.x;
}

float Layout::getY(int i) const {
    const LayoutGlyph& glyph = mGlyphs[i];
    return glyph.y;
}

R
Raph Levien 已提交
950 951 952 953
float Layout::getAdvance() const {
    return mAdvance;
}

954 955 956 957 958 959 960 961
void Layout::getAdvances(float* advances) {
    memcpy(advances, &mAdvances[0], mAdvances.size() * sizeof(float));
}

void Layout::getBounds(MinikinRect* bounds) {
    bounds->set(mBounds);
}

R
Raph Levien 已提交
962
void Layout::purgeCaches() {
S
Seigo Nonaka 已提交
963
    android::AutoMutex _l(gMinikinLock);
R
Raph Levien 已提交
964
    LayoutCache& layoutCache = LayoutEngine::getInstance().layoutCache;
B
Behdad Esfahbod 已提交
965
    layoutCache.clear();
966
    purgeHbFontCacheLocked();
R
Raph Levien 已提交
967 968
}

S
Seigo Nonaka 已提交
969 970 971 972 973 974
}  // namespace minikin

// Unable to define the static data member outside of android.
// TODO: introduce our own Singleton to drop android namespace.
namespace android {
ANDROID_SINGLETON_STATIC_INSTANCE(minikin::LayoutEngine);
R
Raph Levien 已提交
975
}  // namespace android
S
Seigo Nonaka 已提交
976