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 #include <cutils/log.h> 19 20 #include <math.h> 21 22 #include <algorithm> 23 #include <fstream> 24 #include <iostream> // for debugging 25 #include <string> 26 #include <vector> 27 28 #include <utils/JenkinsHash.h> 29 #include <utils/LruCache.h> 30 #include <utils/Singleton.h> 31 #include <utils/String16.h> 32 33 #include <unicode/ubidi.h> 34 #include <hb-icu.h> 35 #include <hb-ot.h> 36 37 #include "FontLanguage.h" 38 #include "FontLanguageListCache.h" 39 #include "LayoutUtils.h" 40 #include "HbFontCache.h" 41 #include "MinikinInternal.h" 42 #include <minikin/MinikinFontFreeType.h> 43 #include <minikin/Layout.h> 44 45 using std::string; 46 using std::vector; 47 48 namespace minikin { 49 50 Bitmap::Bitmap(int width, int height) : width(width), height(height) { 51 buf = new uint8_t[width * height](); 52 } 53 54 Bitmap::~Bitmap() { 55 delete[] buf; 56 } 57 58 void Bitmap::writePnm(std::ofstream &o) const { 59 o << "P5" << std::endl; 60 o << width << " " << height << std::endl; 61 o << "255" << std::endl; 62 o.write((const char *)buf, width * height); 63 o.close(); 64 } 65 66 void Bitmap::drawGlyph(const android::GlyphBitmap& bitmap, int x, int y) { 67 int bmw = bitmap.width; 68 int bmh = bitmap.height; 69 x += bitmap.left; 70 y -= bitmap.top; 71 int x0 = std::max(0, x); 72 int x1 = std::min(width, x + bmw); 73 int y0 = std::max(0, y); 74 int y1 = std::min(height, y + bmh); 75 const unsigned char* src = bitmap.buffer + (y0 - y) * bmw + (x0 - x); 76 uint8_t* dst = buf + y0 * width; 77 for (int yy = y0; yy < y1; yy++) { 78 for (int xx = x0; xx < x1; xx++) { 79 int pixel = (int)dst[xx] + (int)src[xx - x]; 80 pixel = pixel > 0xff ? 0xff : pixel; 81 dst[xx] = pixel; 82 } 83 src += bmw; 84 dst += width; 85 } 86 } 87 88 } // namespace minikin 89 90 namespace android { 91 92 const int kDirection_Mask = 0x1; 93 94 struct LayoutContext { 95 MinikinPaint paint; 96 FontStyle style; 97 std::vector<hb_font_t*> hbFonts; // parallel to mFaces 98 99 void clearHbFonts() { 100 for (size_t i = 0; i < hbFonts.size(); i++) { 101 hb_font_set_funcs(hbFonts[i], nullptr, nullptr, nullptr); 102 hb_font_destroy(hbFonts[i]); 103 } 104 hbFonts.clear(); 105 } 106 }; 107 108 // Layout cache datatypes 109 110 class LayoutCacheKey { 111 public: 112 LayoutCacheKey(const FontCollection* collection, const MinikinPaint& paint, FontStyle style, 113 const uint16_t* chars, size_t start, size_t count, size_t nchars, bool dir) 114 : mChars(chars), mNchars(nchars), 115 mStart(start), mCount(count), mId(collection->getId()), mStyle(style), 116 mSize(paint.size), mScaleX(paint.scaleX), mSkewX(paint.skewX), 117 mLetterSpacing(paint.letterSpacing), 118 mPaintFlags(paint.paintFlags), mHyphenEdit(paint.hyphenEdit), mIsRtl(dir), 119 mHash(computeHash()) { 120 } 121 bool operator==(const LayoutCacheKey &other) const; 122 123 hash_t hash() const { 124 return mHash; 125 } 126 127 void copyText() { 128 uint16_t* charsCopy = new uint16_t[mNchars]; 129 memcpy(charsCopy, mChars, mNchars * sizeof(uint16_t)); 130 mChars = charsCopy; 131 } 132 void freeText() { 133 delete[] mChars; 134 mChars = NULL; 135 } 136 137 void doLayout(Layout* layout, LayoutContext* ctx, const FontCollection* collection) const { 138 layout->setFontCollection(collection); 139 layout->mAdvances.resize(mCount, 0); 140 ctx->clearHbFonts(); 141 layout->doLayoutRun(mChars, mStart, mCount, mNchars, mIsRtl, ctx); 142 } 143 144 private: 145 const uint16_t* mChars; 146 size_t mNchars; 147 size_t mStart; 148 size_t mCount; 149 uint32_t mId; // for the font collection 150 FontStyle mStyle; 151 float mSize; 152 float mScaleX; 153 float mSkewX; 154 float mLetterSpacing; 155 int32_t mPaintFlags; 156 HyphenEdit mHyphenEdit; 157 bool mIsRtl; 158 // Note: any fields added to MinikinPaint must also be reflected here. 159 // TODO: language matching (possibly integrate into style) 160 hash_t mHash; 161 162 hash_t computeHash() const; 163 }; 164 165 class LayoutCache : private OnEntryRemoved<LayoutCacheKey, Layout*> { 166 public: 167 LayoutCache() : mCache(kMaxEntries) { 168 mCache.setOnEntryRemovedListener(this); 169 } 170 171 void clear() { 172 mCache.clear(); 173 } 174 175 Layout* get(LayoutCacheKey& key, LayoutContext* ctx, const FontCollection* collection) { 176 Layout* layout = mCache.get(key); 177 if (layout == NULL) { 178 key.copyText(); 179 layout = new Layout(); 180 key.doLayout(layout, ctx, collection); 181 mCache.put(key, layout); 182 } 183 return layout; 184 } 185 186 private: 187 // callback for OnEntryRemoved 188 void operator()(LayoutCacheKey& key, Layout*& value) { 189 key.freeText(); 190 delete value; 191 } 192 193 LruCache<LayoutCacheKey, Layout*> mCache; 194 195 //static const size_t kMaxEntries = LruCache<LayoutCacheKey, Layout*>::kUnlimitedCapacity; 196 197 // TODO: eviction based on memory footprint; for now, we just use a constant 198 // number of strings 199 static const size_t kMaxEntries = 5000; 200 }; 201 202 static unsigned int disabledDecomposeCompatibility(hb_unicode_funcs_t*, hb_codepoint_t, 203 hb_codepoint_t*, void*) { 204 return 0; 205 } 206 207 class LayoutEngine : public Singleton<LayoutEngine> { 208 public: 209 LayoutEngine() { 210 unicodeFunctions = hb_unicode_funcs_create(hb_icu_get_unicode_funcs()); 211 /* Disable the function used for compatibility decomposition */ 212 hb_unicode_funcs_set_decompose_compatibility_func( 213 unicodeFunctions, disabledDecomposeCompatibility, NULL, NULL); 214 hbBuffer = hb_buffer_create(); 215 hb_buffer_set_unicode_funcs(hbBuffer, unicodeFunctions); 216 } 217 218 hb_buffer_t* hbBuffer; 219 hb_unicode_funcs_t* unicodeFunctions; 220 LayoutCache layoutCache; 221 }; 222 223 ANDROID_SINGLETON_STATIC_INSTANCE(LayoutEngine); 224 225 bool LayoutCacheKey::operator==(const LayoutCacheKey& other) const { 226 return mId == other.mId 227 && mStart == other.mStart 228 && mCount == other.mCount 229 && mStyle == other.mStyle 230 && mSize == other.mSize 231 && mScaleX == other.mScaleX 232 && mSkewX == other.mSkewX 233 && mLetterSpacing == other.mLetterSpacing 234 && mPaintFlags == other.mPaintFlags 235 && mHyphenEdit == other.mHyphenEdit 236 && mIsRtl == other.mIsRtl 237 && mNchars == other.mNchars 238 && !memcmp(mChars, other.mChars, mNchars * sizeof(uint16_t)); 239 } 240 241 hash_t LayoutCacheKey::computeHash() const { 242 uint32_t hash = JenkinsHashMix(0, mId); 243 hash = JenkinsHashMix(hash, mStart); 244 hash = JenkinsHashMix(hash, mCount); 245 hash = JenkinsHashMix(hash, hash_type(mStyle)); 246 hash = JenkinsHashMix(hash, hash_type(mSize)); 247 hash = JenkinsHashMix(hash, hash_type(mScaleX)); 248 hash = JenkinsHashMix(hash, hash_type(mSkewX)); 249 hash = JenkinsHashMix(hash, hash_type(mLetterSpacing)); 250 hash = JenkinsHashMix(hash, hash_type(mPaintFlags)); 251 hash = JenkinsHashMix(hash, hash_type(mHyphenEdit.hasHyphen())); 252 hash = JenkinsHashMix(hash, hash_type(mIsRtl)); 253 hash = JenkinsHashMixShorts(hash, mChars, mNchars); 254 return JenkinsHashWhiten(hash); 255 } 256 257 hash_t hash_type(const LayoutCacheKey& key) { 258 return key.hash(); 259 } 260 261 void MinikinRect::join(const MinikinRect& r) { 262 if (isEmpty()) { 263 set(r); 264 } else if (!r.isEmpty()) { 265 mLeft = std::min(mLeft, r.mLeft); 266 mTop = std::min(mTop, r.mTop); 267 mRight = std::max(mRight, r.mRight); 268 mBottom = std::max(mBottom, r.mBottom); 269 } 270 } 271 272 // Deprecated. Remove when callers are removed. 273 void Layout::init() { 274 } 275 276 void Layout::reset() { 277 mGlyphs.clear(); 278 mFaces.clear(); 279 mBounds.setEmpty(); 280 mAdvances.clear(); 281 mAdvance = 0; 282 } 283 284 void Layout::setFontCollection(const FontCollection* collection) { 285 mCollection = collection; 286 } 287 288 static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* /* hbFont */, void* fontData, 289 hb_codepoint_t glyph, void* /* userData */) { 290 MinikinPaint* paint = reinterpret_cast<MinikinPaint*>(fontData); 291 MinikinFont* font = paint->font; 292 float advance = font->GetHorizontalAdvance(glyph, *paint); 293 return 256 * advance + 0.5; 294 } 295 296 static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* /* hbFont */, void* /* fontData */, 297 hb_codepoint_t /* glyph */, hb_position_t* /* x */, hb_position_t* /* y */, 298 void* /* userData */) { 299 // Just return true, following the way that Harfbuzz-FreeType 300 // implementation does. 301 return true; 302 } 303 304 hb_font_funcs_t* getHbFontFuncs() { 305 static hb_font_funcs_t* hbFontFuncs = 0; 306 307 if (hbFontFuncs == 0) { 308 hbFontFuncs = hb_font_funcs_create(); 309 hb_font_funcs_set_glyph_h_advance_func(hbFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0); 310 hb_font_funcs_set_glyph_h_origin_func(hbFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0); 311 hb_font_funcs_make_immutable(hbFontFuncs); 312 } 313 return hbFontFuncs; 314 } 315 316 static float HBFixedToFloat(hb_position_t v) 317 { 318 return scalbnf (v, -8); 319 } 320 321 static hb_position_t HBFloatToFixed(float v) 322 { 323 return scalbnf (v, +8); 324 } 325 326 void Layout::dump() const { 327 for (size_t i = 0; i < mGlyphs.size(); i++) { 328 const LayoutGlyph& glyph = mGlyphs[i]; 329 std::cout << glyph.glyph_id << ": " << glyph.x << ", " << glyph.y << std::endl; 330 } 331 } 332 333 int Layout::findFace(FakedFont face, LayoutContext* ctx) { 334 unsigned int ix; 335 for (ix = 0; ix < mFaces.size(); ix++) { 336 if (mFaces[ix].font == face.font) { 337 return ix; 338 } 339 } 340 mFaces.push_back(face); 341 // Note: ctx == NULL means we're copying from the cache, no need to create 342 // corresponding hb_font object. 343 if (ctx != NULL) { 344 hb_font_t* font = getHbFontLocked(face.font); 345 hb_font_set_funcs(font, getHbFontFuncs(), &ctx->paint, 0); 346 ctx->hbFonts.push_back(font); 347 } 348 return ix; 349 } 350 351 static hb_script_t codePointToScript(hb_codepoint_t codepoint) { 352 static hb_unicode_funcs_t* u = 0; 353 if (!u) { 354 u = LayoutEngine::getInstance().unicodeFunctions; 355 } 356 return hb_unicode_script(u, codepoint); 357 } 358 359 static hb_codepoint_t decodeUtf16(const uint16_t* chars, size_t len, ssize_t* iter) { 360 const uint16_t v = chars[(*iter)++]; 361 // test whether v in (0xd800..0xdfff), lead or trail surrogate 362 if ((v & 0xf800) == 0xd800) { 363 // test whether v in (0xd800..0xdbff), lead surrogate 364 if (size_t(*iter) < len && (v & 0xfc00) == 0xd800) { 365 const uint16_t v2 = chars[(*iter)++]; 366 // test whether v2 in (0xdc00..0xdfff), trail surrogate 367 if ((v2 & 0xfc00) == 0xdc00) { 368 // (0xd800 0xdc00) in utf-16 maps to 0x10000 in ucs-32 369 const hb_codepoint_t delta = (0xd800 << 10) + 0xdc00 - 0x10000; 370 return (((hb_codepoint_t)v) << 10) + v2 - delta; 371 } 372 (*iter) -= 1; 373 return 0xFFFDu; 374 } else { 375 return 0xFFFDu; 376 } 377 } else { 378 return v; 379 } 380 } 381 382 static hb_script_t getScriptRun(const uint16_t* chars, size_t len, ssize_t* iter) { 383 if (size_t(*iter) == len) { 384 return HB_SCRIPT_UNKNOWN; 385 } 386 uint32_t cp = decodeUtf16(chars, len, iter); 387 hb_script_t current_script = codePointToScript(cp); 388 for (;;) { 389 if (size_t(*iter) == len) 390 break; 391 const ssize_t prev_iter = *iter; 392 cp = decodeUtf16(chars, len, iter); 393 const hb_script_t script = codePointToScript(cp); 394 if (script != current_script) { 395 if (current_script == HB_SCRIPT_INHERITED || 396 current_script == HB_SCRIPT_COMMON) { 397 current_script = script; 398 } else if (script == HB_SCRIPT_INHERITED || 399 script == HB_SCRIPT_COMMON) { 400 continue; 401 } else { 402 *iter = prev_iter; 403 break; 404 } 405 } 406 } 407 if (current_script == HB_SCRIPT_INHERITED) { 408 current_script = HB_SCRIPT_COMMON; 409 } 410 411 return current_script; 412 } 413 414 /** 415 * Disable certain scripts (mostly those with cursive connection) from having letterspacing 416 * applied. See https://github.com/behdad/harfbuzz/issues/64 for more details. 417 */ 418 static bool isScriptOkForLetterspacing(hb_script_t script) { 419 return !( 420 script == HB_SCRIPT_ARABIC || 421 script == HB_SCRIPT_NKO || 422 script == HB_SCRIPT_PSALTER_PAHLAVI || 423 script == HB_SCRIPT_MANDAIC || 424 script == HB_SCRIPT_MONGOLIAN || 425 script == HB_SCRIPT_PHAGS_PA || 426 script == HB_SCRIPT_DEVANAGARI || 427 script == HB_SCRIPT_BENGALI || 428 script == HB_SCRIPT_GURMUKHI || 429 script == HB_SCRIPT_MODI || 430 script == HB_SCRIPT_SHARADA || 431 script == HB_SCRIPT_SYLOTI_NAGRI || 432 script == HB_SCRIPT_TIRHUTA || 433 script == HB_SCRIPT_OGHAM 434 ); 435 } 436 437 class BidiText { 438 public: 439 class Iter { 440 public: 441 struct RunInfo { 442 int32_t mRunStart; 443 int32_t mRunLength; 444 bool mIsRtl; 445 }; 446 447 Iter(UBiDi* bidi, size_t start, size_t end, size_t runIndex, size_t runCount, bool isRtl); 448 449 bool operator!= (const Iter& other) const { 450 return mIsEnd != other.mIsEnd || mNextRunIndex != other.mNextRunIndex 451 || mBidi != other.mBidi; 452 } 453 454 const RunInfo& operator* () const { 455 return mRunInfo; 456 } 457 458 const Iter& operator++ () { 459 updateRunInfo(); 460 return *this; 461 } 462 463 private: 464 UBiDi* const mBidi; 465 bool mIsEnd; 466 size_t mNextRunIndex; 467 const size_t mRunCount; 468 const int32_t mStart; 469 const int32_t mEnd; 470 RunInfo mRunInfo; 471 472 void updateRunInfo(); 473 }; 474 475 BidiText(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags); 476 477 ~BidiText() { 478 if (mBidi) { 479 ubidi_close(mBidi); 480 } 481 } 482 483 Iter begin () const { 484 return Iter(mBidi, mStart, mEnd, 0, mRunCount, mIsRtl); 485 } 486 487 Iter end() const { 488 return Iter(mBidi, mStart, mEnd, mRunCount, mRunCount, mIsRtl); 489 } 490 491 private: 492 const size_t mStart; 493 const size_t mEnd; 494 const size_t mBufSize; 495 UBiDi* mBidi; 496 size_t mRunCount; 497 bool mIsRtl; 498 499 DISALLOW_COPY_AND_ASSIGN(BidiText); 500 }; 501 502 BidiText::Iter::Iter(UBiDi* bidi, size_t start, size_t end, size_t runIndex, size_t runCount, 503 bool isRtl) 504 : mBidi(bidi), mIsEnd(runIndex == runCount), mNextRunIndex(runIndex), mRunCount(runCount), 505 mStart(start), mEnd(end), mRunInfo() { 506 if (mRunCount == 1) { 507 mRunInfo.mRunStart = start; 508 mRunInfo.mRunLength = end - start; 509 mRunInfo.mIsRtl = isRtl; 510 mNextRunIndex = mRunCount; 511 return; 512 } 513 updateRunInfo(); 514 } 515 516 void BidiText::Iter::updateRunInfo() { 517 if (mNextRunIndex == mRunCount) { 518 // All runs have been iterated. 519 mIsEnd = true; 520 return; 521 } 522 int32_t startRun = -1; 523 int32_t lengthRun = -1; 524 const UBiDiDirection runDir = ubidi_getVisualRun(mBidi, mNextRunIndex, &startRun, &lengthRun); 525 mNextRunIndex++; 526 if (startRun == -1 || lengthRun == -1) { 527 ALOGE("invalid visual run"); 528 // skip the invalid run. 529 updateRunInfo(); 530 return; 531 } 532 const int32_t runEnd = std::min(startRun + lengthRun, mEnd); 533 mRunInfo.mRunStart = std::max(startRun, mStart); 534 mRunInfo.mRunLength = runEnd - mRunInfo.mRunStart; 535 if (mRunInfo.mRunLength <= 0) { 536 // skip the empty run. 537 updateRunInfo(); 538 return; 539 } 540 mRunInfo.mIsRtl = (runDir == UBIDI_RTL); 541 } 542 543 BidiText::BidiText(const uint16_t* buf, size_t start, size_t count, size_t bufSize, int bidiFlags) 544 : mStart(start), mEnd(start + count), mBufSize(bufSize), mBidi(NULL), mRunCount(1), 545 mIsRtl((bidiFlags & kDirection_Mask) != 0) { 546 if (bidiFlags == kBidi_Force_LTR || bidiFlags == kBidi_Force_RTL) { 547 // force single run. 548 return; 549 } 550 mBidi = ubidi_open(); 551 if (!mBidi) { 552 ALOGE("error creating bidi object"); 553 return; 554 } 555 UErrorCode status = U_ZERO_ERROR; 556 UBiDiLevel bidiReq = bidiFlags; 557 if (bidiFlags == kBidi_Default_LTR) { 558 bidiReq = UBIDI_DEFAULT_LTR; 559 } else if (bidiFlags == kBidi_Default_RTL) { 560 bidiReq = UBIDI_DEFAULT_RTL; 561 } 562 ubidi_setPara(mBidi, buf, mBufSize, bidiReq, NULL, &status); 563 if (!U_SUCCESS(status)) { 564 ALOGE("error calling ubidi_setPara, status = %d", status); 565 return; 566 } 567 const int paraDir = ubidi_getParaLevel(mBidi) & kDirection_Mask; 568 const ssize_t rc = ubidi_countRuns(mBidi, &status); 569 if (!U_SUCCESS(status) || rc < 0) { 570 ALOGW("error counting bidi runs, status = %d", status); 571 } 572 if (!U_SUCCESS(status) || rc <= 1) { 573 mIsRtl = (paraDir == kBidi_RTL); 574 return; 575 } 576 mRunCount = rc; 577 } 578 579 void Layout::doLayout(const uint16_t* buf, size_t start, size_t count, size_t bufSize, 580 int bidiFlags, const FontStyle &style, const MinikinPaint &paint) { 581 AutoMutex _l(gMinikinLock); 582 583 LayoutContext ctx; 584 ctx.style = style; 585 ctx.paint = paint; 586 587 reset(); 588 mAdvances.resize(count, 0); 589 590 for (const BidiText::Iter::RunInfo& runInfo : BidiText(buf, start, count, bufSize, bidiFlags)) { 591 doLayoutRunCached(buf, runInfo.mRunStart, runInfo.mRunLength, bufSize, runInfo.mIsRtl, &ctx, 592 start, mCollection, this, NULL); 593 } 594 ctx.clearHbFonts(); 595 } 596 597 float Layout::measureText(const uint16_t* buf, size_t start, size_t count, size_t bufSize, 598 int bidiFlags, const FontStyle &style, const MinikinPaint &paint, 599 const FontCollection* collection, float* advances) { 600 AutoMutex _l(gMinikinLock); 601 602 LayoutContext ctx; 603 ctx.style = style; 604 ctx.paint = paint; 605 606 float advance = 0; 607 for (const BidiText::Iter::RunInfo& runInfo : BidiText(buf, start, count, bufSize, bidiFlags)) { 608 float* advancesForRun = advances ? advances + (runInfo.mRunStart - start) : advances; 609 advance += doLayoutRunCached(buf, runInfo.mRunStart, runInfo.mRunLength, bufSize, 610 runInfo.mIsRtl, &ctx, 0, collection, NULL, advancesForRun); 611 } 612 613 ctx.clearHbFonts(); 614 return advance; 615 } 616 617 float Layout::doLayoutRunCached(const uint16_t* buf, size_t start, size_t count, size_t bufSize, 618 bool isRtl, LayoutContext* ctx, size_t dstStart, const FontCollection* collection, 619 Layout* layout, float* advances) { 620 HyphenEdit hyphen = ctx->paint.hyphenEdit; 621 float advance = 0; 622 if (!isRtl) { 623 // left to right 624 size_t wordstart = 625 start == bufSize ? start : getPrevWordBreakForCache(buf, start + 1, bufSize); 626 size_t wordend; 627 for (size_t iter = start; iter < start + count; iter = wordend) { 628 wordend = getNextWordBreakForCache(buf, iter, bufSize); 629 // Only apply hyphen to the last word in the string. 630 ctx->paint.hyphenEdit = wordend >= start + count ? hyphen : HyphenEdit(); 631 size_t wordcount = std::min(start + count, wordend) - iter; 632 advance += doLayoutWord(buf + wordstart, iter - wordstart, wordcount, 633 wordend - wordstart, isRtl, ctx, iter - dstStart, collection, layout, 634 advances ? advances + (iter - start) : advances); 635 wordstart = wordend; 636 } 637 } else { 638 // right to left 639 size_t wordstart; 640 size_t end = start + count; 641 size_t wordend = end == 0 ? 0 : getNextWordBreakForCache(buf, end - 1, bufSize); 642 for (size_t iter = end; iter > start; iter = wordstart) { 643 wordstart = getPrevWordBreakForCache(buf, iter, bufSize); 644 // Only apply hyphen to the last (leftmost) word in the string. 645 ctx->paint.hyphenEdit = iter == end ? hyphen : HyphenEdit(); 646 size_t bufStart = std::max(start, wordstart); 647 advance += doLayoutWord(buf + wordstart, bufStart - wordstart, iter - bufStart, 648 wordend - wordstart, isRtl, ctx, bufStart - dstStart, collection, layout, 649 advances ? advances + (bufStart - start) : advances); 650 wordend = wordstart; 651 } 652 } 653 return advance; 654 } 655 656 float Layout::doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize, 657 bool isRtl, LayoutContext* ctx, size_t bufStart, const FontCollection* collection, 658 Layout* layout, float* advances) { 659 LayoutCache& cache = LayoutEngine::getInstance().layoutCache; 660 LayoutCacheKey key(collection, ctx->paint, ctx->style, buf, start, count, bufSize, isRtl); 661 bool skipCache = ctx->paint.skipCache(); 662 if (skipCache) { 663 Layout layoutForWord; 664 key.doLayout(&layoutForWord, ctx, collection); 665 if (layout) { 666 layout->appendLayout(&layoutForWord, bufStart); 667 } 668 if (advances) { 669 layoutForWord.getAdvances(advances); 670 } 671 return layoutForWord.getAdvance(); 672 } else { 673 Layout* layoutForWord = cache.get(key, ctx, collection); 674 if (layout) { 675 layout->appendLayout(layoutForWord, bufStart); 676 } 677 if (advances) { 678 layoutForWord->getAdvances(advances); 679 } 680 return layoutForWord->getAdvance(); 681 } 682 } 683 684 static void addFeatures(const string &str, vector<hb_feature_t>* features) { 685 if (!str.size()) 686 return; 687 688 const char* start = str.c_str(); 689 const char* end = start + str.size(); 690 691 while (start < end) { 692 static hb_feature_t feature; 693 const char* p = strchr(start, ','); 694 if (!p) 695 p = end; 696 /* We do not allow setting features on ranges. As such, reject any 697 * setting that has non-universal range. */ 698 if (hb_feature_from_string (start, p - start, &feature) 699 && feature.start == 0 && feature.end == (unsigned int) -1) 700 features->push_back(feature); 701 start = p + 1; 702 } 703 } 704 705 void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t bufSize, 706 bool isRtl, LayoutContext* ctx) { 707 hb_buffer_t* buffer = LayoutEngine::getInstance().hbBuffer; 708 vector<FontCollection::Run> items; 709 mCollection->itemize(buf + start, count, ctx->style, &items); 710 if (isRtl) { 711 std::reverse(items.begin(), items.end()); 712 } 713 714 vector<hb_feature_t> features; 715 // Disable default-on non-required ligature features if letter-spacing 716 // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property 717 // "When the effective spacing between two characters is not zero (due to 718 // either justification or a non-zero value of letter-spacing), user agents 719 // should not apply optional ligatures." 720 if (fabs(ctx->paint.letterSpacing) > 0.03) 721 { 722 static const hb_feature_t no_liga = { HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u }; 723 static const hb_feature_t no_clig = { HB_TAG('c', 'l', 'i', 'g'), 0, 0, ~0u }; 724 features.push_back(no_liga); 725 features.push_back(no_clig); 726 } 727 addFeatures(ctx->paint.fontFeatureSettings, &features); 728 729 double size = ctx->paint.size; 730 double scaleX = ctx->paint.scaleX; 731 732 float x = mAdvance; 733 float y = 0; 734 for (size_t run_ix = 0; run_ix < items.size(); run_ix++) { 735 FontCollection::Run &run = items[run_ix]; 736 if (run.fakedFont.font == NULL) { 737 ALOGE("no font for run starting u+%04x length %d", buf[run.start], run.end - run.start); 738 continue; 739 } 740 int font_ix = findFace(run.fakedFont, ctx); 741 ctx->paint.font = mFaces[font_ix].font; 742 ctx->paint.fakery = mFaces[font_ix].fakery; 743 hb_font_t* hbFont = ctx->hbFonts[font_ix]; 744 #ifdef VERBOSE_DEBUG 745 ALOGD("Run %zu, font %d [%d:%d]", run_ix, font_ix, run.start, run.end); 746 #endif 747 748 hb_font_set_ppem(hbFont, size * scaleX, size); 749 hb_font_set_scale(hbFont, HBFloatToFixed(size * scaleX), HBFloatToFixed(size)); 750 751 // TODO: if there are multiple scripts within a font in an RTL run, 752 // we need to reorder those runs. This is unlikely with our current 753 // font stack, but should be done for correctness. 754 ssize_t srunend; 755 for (ssize_t srunstart = run.start; srunstart < run.end; srunstart = srunend) { 756 srunend = srunstart; 757 hb_script_t script = getScriptRun(buf + start, run.end, &srunend); 758 759 double letterSpace = 0.0; 760 double letterSpaceHalfLeft = 0.0; 761 double letterSpaceHalfRight = 0.0; 762 763 if (ctx->paint.letterSpacing != 0.0 && isScriptOkForLetterspacing(script)) { 764 letterSpace = ctx->paint.letterSpacing * size * scaleX; 765 if ((ctx->paint.paintFlags & LinearTextFlag) == 0) { 766 letterSpace = round(letterSpace); 767 letterSpaceHalfLeft = floor(letterSpace * 0.5); 768 } else { 769 letterSpaceHalfLeft = letterSpace * 0.5; 770 } 771 letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft; 772 } 773 774 hb_buffer_clear_contents(buffer); 775 hb_buffer_set_script(buffer, script); 776 hb_buffer_set_direction(buffer, isRtl? HB_DIRECTION_RTL : HB_DIRECTION_LTR); 777 const FontLanguages& langList = 778 FontLanguageListCache::getById(ctx->style.getLanguageListId()); 779 if (langList.size() != 0) { 780 const FontLanguage* hbLanguage = &langList[0]; 781 for (size_t i = 0; i < langList.size(); ++i) { 782 if (langList[i].supportsHbScript(script)) { 783 hbLanguage = &langList[i]; 784 break; 785 } 786 } 787 hb_buffer_set_language(buffer, 788 hb_language_from_string(hbLanguage->getString().c_str(), -1)); 789 } 790 hb_buffer_add_utf16(buffer, buf, bufSize, srunstart + start, srunend - srunstart); 791 if (ctx->paint.hyphenEdit.hasHyphen() && srunend > srunstart) { 792 // TODO: check whether this is really the desired semantics. It could have the 793 // effect of assigning the hyphen width to a nonspacing mark 794 unsigned int lastCluster = start + srunend - 1; 795 796 hb_codepoint_t hyphenChar = 0x2010; // HYPHEN 797 hb_codepoint_t glyph; 798 // Fallback to ASCII HYPHEN-MINUS if the font didn't have a glyph for HYPHEN. Note 799 // that we intentionally don't do anything special if the font doesn't have a 800 // HYPHEN-MINUS either, so a tofu could be shown, hinting towards something 801 // missing. 802 if (!hb_font_get_glyph(hbFont, hyphenChar, 0, &glyph)) { 803 hyphenChar = 0x002D; // HYPHEN-MINUS 804 } 805 hb_buffer_add(buffer, hyphenChar, lastCluster); 806 } 807 hb_shape(hbFont, buffer, features.empty() ? NULL : &features[0], features.size()); 808 unsigned int numGlyphs; 809 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs); 810 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL); 811 if (numGlyphs) 812 { 813 mAdvances[info[0].cluster - start] += letterSpaceHalfLeft; 814 x += letterSpaceHalfLeft; 815 } 816 for (unsigned int i = 0; i < numGlyphs; i++) { 817 #ifdef VERBOSE_DEBUG 818 ALOGD("%d %d %d %d", 819 positions[i].x_advance, positions[i].y_advance, 820 positions[i].x_offset, positions[i].y_offset); 821 ALOGD("DoLayout %u: %f; %d, %d", 822 info[i].codepoint, HBFixedToFloat(positions[i].x_advance), 823 positions[i].x_offset, positions[i].y_offset); 824 #endif 825 if (i > 0 && info[i - 1].cluster != info[i].cluster) { 826 mAdvances[info[i - 1].cluster - start] += letterSpaceHalfRight; 827 mAdvances[info[i].cluster - start] += letterSpaceHalfLeft; 828 x += letterSpace; 829 } 830 831 hb_codepoint_t glyph_ix = info[i].codepoint; 832 float xoff = HBFixedToFloat(positions[i].x_offset); 833 float yoff = -HBFixedToFloat(positions[i].y_offset); 834 xoff += yoff * ctx->paint.skewX; 835 LayoutGlyph glyph = {font_ix, glyph_ix, x + xoff, y + yoff}; 836 mGlyphs.push_back(glyph); 837 float xAdvance = HBFixedToFloat(positions[i].x_advance); 838 if ((ctx->paint.paintFlags & LinearTextFlag) == 0) { 839 xAdvance = roundf(xAdvance); 840 } 841 MinikinRect glyphBounds; 842 ctx->paint.font->GetBounds(&glyphBounds, glyph_ix, ctx->paint); 843 glyphBounds.offset(x + xoff, y + yoff); 844 mBounds.join(glyphBounds); 845 if (info[i].cluster - start < count) { 846 mAdvances[info[i].cluster - start] += xAdvance; 847 } else { 848 ALOGE("cluster %zu (start %zu) out of bounds of count %zu", 849 info[i].cluster - start, start, count); 850 } 851 x += xAdvance; 852 } 853 if (numGlyphs) 854 { 855 mAdvances[info[numGlyphs - 1].cluster - start] += letterSpaceHalfRight; 856 x += letterSpaceHalfRight; 857 } 858 } 859 } 860 mAdvance = x; 861 } 862 863 void Layout::appendLayout(Layout* src, size_t start) { 864 int fontMapStack[16]; 865 int* fontMap; 866 if (src->mFaces.size() < sizeof(fontMapStack) / sizeof(fontMapStack[0])) { 867 fontMap = fontMapStack; 868 } else { 869 fontMap = new int[src->mFaces.size()]; 870 } 871 for (size_t i = 0; i < src->mFaces.size(); i++) { 872 int font_ix = findFace(src->mFaces[i], NULL); 873 fontMap[i] = font_ix; 874 } 875 int x0 = mAdvance; 876 for (size_t i = 0; i < src->mGlyphs.size(); i++) { 877 LayoutGlyph& srcGlyph = src->mGlyphs[i]; 878 int font_ix = fontMap[srcGlyph.font_ix]; 879 unsigned int glyph_id = srcGlyph.glyph_id; 880 float x = x0 + srcGlyph.x; 881 float y = srcGlyph.y; 882 LayoutGlyph glyph = {font_ix, glyph_id, x, y}; 883 mGlyphs.push_back(glyph); 884 } 885 for (size_t i = 0; i < src->mAdvances.size(); i++) { 886 mAdvances[i + start] = src->mAdvances[i]; 887 } 888 MinikinRect srcBounds(src->mBounds); 889 srcBounds.offset(x0, 0); 890 mBounds.join(srcBounds); 891 mAdvance += src->mAdvance; 892 893 if (fontMap != fontMapStack) { 894 delete[] fontMap; 895 } 896 } 897 898 void Layout::draw(minikin::Bitmap* surface, int x0, int y0, float size) const { 899 /* 900 TODO: redo as MinikinPaint settings 901 if (mProps.hasTag(minikinHinting)) { 902 int hintflags = mProps.value(minikinHinting).getIntValue(); 903 if (hintflags & 1) load_flags |= FT_LOAD_NO_HINTING; 904 if (hintflags & 2) load_flags |= FT_LOAD_NO_AUTOHINT; 905 } 906 */ 907 for (size_t i = 0; i < mGlyphs.size(); i++) { 908 const LayoutGlyph& glyph = mGlyphs[i]; 909 MinikinFont* mf = mFaces[glyph.font_ix].font; 910 MinikinFontFreeType* face = static_cast<MinikinFontFreeType*>(mf); 911 GlyphBitmap glyphBitmap; 912 MinikinPaint paint; 913 paint.size = size; 914 bool ok = face->Render(glyph.glyph_id, paint, &glyphBitmap); 915 #ifdef VERBOSE_DEBUG 916 ALOGD("glyphBitmap.width=%d, glyphBitmap.height=%d (%d, %d) x=%f, y=%f, ok=%d", 917 glyphBitmap.width, glyphBitmap.height, glyphBitmap.left, glyphBitmap.top, glyph.x, glyph.y, ok); 918 #endif 919 if (ok) { 920 surface->drawGlyph(glyphBitmap, 921 x0 + int(floor(glyph.x + 0.5)), y0 + int(floor(glyph.y + 0.5))); 922 } 923 } 924 } 925 926 size_t Layout::nGlyphs() const { 927 return mGlyphs.size(); 928 } 929 930 MinikinFont* Layout::getFont(int i) const { 931 const LayoutGlyph& glyph = mGlyphs[i]; 932 return mFaces[glyph.font_ix].font; 933 } 934 935 FontFakery Layout::getFakery(int i) const { 936 const LayoutGlyph& glyph = mGlyphs[i]; 937 return mFaces[glyph.font_ix].fakery; 938 } 939 940 unsigned int Layout::getGlyphId(int i) const { 941 const LayoutGlyph& glyph = mGlyphs[i]; 942 return glyph.glyph_id; 943 } 944 945 float Layout::getX(int i) const { 946 const LayoutGlyph& glyph = mGlyphs[i]; 947 return glyph.x; 948 } 949 950 float Layout::getY(int i) const { 951 const LayoutGlyph& glyph = mGlyphs[i]; 952 return glyph.y; 953 } 954 955 float Layout::getAdvance() const { 956 return mAdvance; 957 } 958 959 void Layout::getAdvances(float* advances) { 960 memcpy(advances, &mAdvances[0], mAdvances.size() * sizeof(float)); 961 } 962 963 void Layout::getBounds(MinikinRect* bounds) { 964 bounds->set(mBounds); 965 } 966 967 void Layout::purgeCaches() { 968 AutoMutex _l(gMinikinLock); 969 LayoutCache& layoutCache = LayoutEngine::getInstance().layoutCache; 970 layoutCache.clear(); 971 purgeHbFontCacheLocked(); 972 } 973 974 } // namespace android 975