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