1 /* 2 * Copyright (C) 2011 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 "TextLayoutCache" 18 19 #include <utils/JenkinsHash.h> 20 21 #include "TextLayoutCache.h" 22 #include "TextLayout.h" 23 #include "SkFontHost.h" 24 #include "SkTypeface_android.h" 25 #include "HarfBuzzNGFaceSkia.h" 26 #include <unicode/unistr.h> 27 #include <unicode/uchar.h> 28 #include <hb-icu.h> 29 30 namespace android { 31 32 //-------------------------------------------------------------------------------------------------- 33 34 ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine); 35 36 //-------------------------------------------------------------------------------------------------- 37 38 TextLayoutCache::TextLayoutCache(TextLayoutShaper* shaper) : 39 mShaper(shaper), 40 mCache(LruCache<TextLayoutCacheKey, sp<TextLayoutValue> >::kUnlimitedCapacity), 41 mSize(0), mMaxSize(MB(DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB)), 42 mCacheHitCount(0), mNanosecondsSaved(0) { 43 init(); 44 } 45 46 TextLayoutCache::~TextLayoutCache() { 47 mCache.clear(); 48 } 49 50 void TextLayoutCache::init() { 51 mCache.setOnEntryRemovedListener(this); 52 53 mDebugLevel = readRtlDebugLevel(); 54 mDebugEnabled = mDebugLevel & kRtlDebugCaches; 55 ALOGD("Using debug level = %d - Debug Enabled = %d", mDebugLevel, mDebugEnabled); 56 57 mCacheStartTime = systemTime(SYSTEM_TIME_MONOTONIC); 58 59 if (mDebugEnabled) { 60 ALOGD("Initialization is done - Start time = %lld", mCacheStartTime); 61 } 62 63 mInitialized = true; 64 } 65 66 /** 67 * Callbacks 68 */ 69 void TextLayoutCache::operator()(TextLayoutCacheKey& text, sp<TextLayoutValue>& desc) { 70 size_t totalSizeToDelete = text.getSize() + desc->getSize(); 71 mSize -= totalSizeToDelete; 72 if (mDebugEnabled) { 73 ALOGD("Cache value %p deleted, size = %d", desc.get(), totalSizeToDelete); 74 } 75 } 76 77 /* 78 * Cache clearing 79 */ 80 void TextLayoutCache::purgeCaches() { 81 AutoMutex _l(mLock); 82 mCache.clear(); 83 mShaper->purgeCaches(); 84 } 85 86 /* 87 * Caching 88 */ 89 sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint, 90 const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) { 91 AutoMutex _l(mLock); 92 nsecs_t startTime = 0; 93 if (mDebugEnabled) { 94 startTime = systemTime(SYSTEM_TIME_MONOTONIC); 95 } 96 97 // Create the key 98 TextLayoutCacheKey key(paint, text, start, count, contextCount, dirFlags); 99 100 // Get value from cache if possible 101 sp<TextLayoutValue> value = mCache.get(key); 102 103 // Value not found for the key, we need to add a new value in the cache 104 if (value == NULL) { 105 if (mDebugEnabled) { 106 startTime = systemTime(SYSTEM_TIME_MONOTONIC); 107 } 108 109 value = new TextLayoutValue(contextCount); 110 111 // Compute advances and store them 112 mShaper->computeValues(value.get(), paint, 113 reinterpret_cast<const UChar*>(key.getText()), start, count, 114 size_t(contextCount), int(dirFlags)); 115 116 if (mDebugEnabled) { 117 value->setElapsedTime(systemTime(SYSTEM_TIME_MONOTONIC) - startTime); 118 } 119 120 // Don't bother to add in the cache if the entry is too big 121 size_t size = key.getSize() + value->getSize(); 122 if (size <= mMaxSize) { 123 // Cleanup to make some room if needed 124 if (mSize + size > mMaxSize) { 125 if (mDebugEnabled) { 126 ALOGD("Need to clean some entries for making some room for a new entry"); 127 } 128 while (mSize + size > mMaxSize) { 129 // This will call the callback 130 bool removedOne = mCache.removeOldest(); 131 LOG_ALWAYS_FATAL_IF(!removedOne, "The cache is non-empty but we " 132 "failed to remove the oldest entry. " 133 "mSize = %u, size = %u, mMaxSize = %u, mCache.size() = %u", 134 mSize, size, mMaxSize, mCache.size()); 135 } 136 } 137 138 // Update current cache size 139 mSize += size; 140 141 bool putOne = mCache.put(key, value); 142 LOG_ALWAYS_FATAL_IF(!putOne, "Failed to put an entry into the cache. " 143 "This indicates that the cache already has an entry with the " 144 "same key but it should not since we checked earlier!" 145 " - start = %d, count = %d, contextCount = %d - Text = '%s'", 146 start, count, contextCount, String8(key.getText() + start, count).string()); 147 148 if (mDebugEnabled) { 149 nsecs_t totalTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; 150 ALOGD("CACHE MISS: Added entry %p " 151 "with start = %d, count = %d, contextCount = %d, " 152 "entry size %d bytes, remaining space %d bytes" 153 " - Compute time %0.6f ms - Put time %0.6f ms - Text = '%s'", 154 value.get(), start, count, contextCount, size, mMaxSize - mSize, 155 value->getElapsedTime() * 0.000001f, 156 (totalTime - value->getElapsedTime()) * 0.000001f, 157 String8(key.getText() + start, count).string()); 158 } 159 } else { 160 if (mDebugEnabled) { 161 ALOGD("CACHE MISS: Calculated but not storing entry because it is too big " 162 "with start = %d, count = %d, contextCount = %d, " 163 "entry size %d bytes, remaining space %d bytes" 164 " - Compute time %0.6f ms - Text = '%s'", 165 start, count, contextCount, size, mMaxSize - mSize, 166 value->getElapsedTime() * 0.000001f, 167 String8(key.getText() + start, count).string()); 168 } 169 } 170 } else { 171 // This is a cache hit, just log timestamp and user infos 172 if (mDebugEnabled) { 173 nsecs_t elapsedTimeThruCacheGet = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; 174 mNanosecondsSaved += (value->getElapsedTime() - elapsedTimeThruCacheGet); 175 ++mCacheHitCount; 176 177 if (value->getElapsedTime() > 0) { 178 float deltaPercent = 100 * ((value->getElapsedTime() - elapsedTimeThruCacheGet) 179 / ((float)value->getElapsedTime())); 180 ALOGD("CACHE HIT #%d with start = %d, count = %d, contextCount = %d" 181 "- Compute time %0.6f ms - " 182 "Cache get time %0.6f ms - Gain in percent: %2.2f - Text = '%s'", 183 mCacheHitCount, start, count, contextCount, 184 value->getElapsedTime() * 0.000001f, 185 elapsedTimeThruCacheGet * 0.000001f, 186 deltaPercent, 187 String8(key.getText() + start, count).string()); 188 } 189 if (mCacheHitCount % DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL == 0) { 190 dumpCacheStats(); 191 } 192 } 193 } 194 return value; 195 } 196 197 void TextLayoutCache::dumpCacheStats() { 198 float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize)); 199 float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000; 200 201 size_t cacheSize = mCache.size(); 202 203 ALOGD("------------------------------------------------"); 204 ALOGD("Cache stats"); 205 ALOGD("------------------------------------------------"); 206 ALOGD("pid : %d", getpid()); 207 ALOGD("running : %.0f seconds", timeRunningInSec); 208 ALOGD("entries : %d", cacheSize); 209 ALOGD("max size : %d bytes", mMaxSize); 210 ALOGD("used : %d bytes according to mSize", mSize); 211 ALOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent); 212 ALOGD("hits : %d", mCacheHitCount); 213 ALOGD("saved : %0.6f ms", mNanosecondsSaved * 0.000001f); 214 ALOGD("------------------------------------------------"); 215 } 216 217 /** 218 * TextLayoutCacheKey 219 */ 220 TextLayoutCacheKey::TextLayoutCacheKey(): start(0), count(0), contextCount(0), 221 dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0), 222 hinting(SkPaint::kNo_Hinting), variant(SkPaint::kDefault_Variant), language() { 223 } 224 225 TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text, 226 size_t start, size_t count, size_t contextCount, int dirFlags) : 227 start(start), count(count), contextCount(contextCount), 228 dirFlags(dirFlags) { 229 textCopy.setTo(text, contextCount); 230 typeface = paint->getTypeface(); 231 textSize = paint->getTextSize(); 232 textSkewX = paint->getTextSkewX(); 233 textScaleX = paint->getTextScaleX(); 234 flags = paint->getFlags(); 235 hinting = paint->getHinting(); 236 variant = paint->getFontVariant(); 237 language = paint->getLanguage(); 238 } 239 240 TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) : 241 textCopy(other.textCopy), 242 start(other.start), 243 count(other.count), 244 contextCount(other.contextCount), 245 dirFlags(other.dirFlags), 246 typeface(other.typeface), 247 textSize(other.textSize), 248 textSkewX(other.textSkewX), 249 textScaleX(other.textScaleX), 250 flags(other.flags), 251 hinting(other.hinting), 252 variant(other.variant), 253 language(other.language) { 254 } 255 256 int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) { 257 int deltaInt = lhs.start - rhs.start; 258 if (deltaInt != 0) return (deltaInt); 259 260 deltaInt = lhs.count - rhs.count; 261 if (deltaInt != 0) return (deltaInt); 262 263 deltaInt = lhs.contextCount - rhs.contextCount; 264 if (deltaInt != 0) return (deltaInt); 265 266 if (lhs.typeface < rhs.typeface) return -1; 267 if (lhs.typeface > rhs.typeface) return +1; 268 269 if (lhs.textSize < rhs.textSize) return -1; 270 if (lhs.textSize > rhs.textSize) return +1; 271 272 if (lhs.textSkewX < rhs.textSkewX) return -1; 273 if (lhs.textSkewX > rhs.textSkewX) return +1; 274 275 if (lhs.textScaleX < rhs.textScaleX) return -1; 276 if (lhs.textScaleX > rhs.textScaleX) return +1; 277 278 deltaInt = lhs.flags - rhs.flags; 279 if (deltaInt != 0) return (deltaInt); 280 281 deltaInt = lhs.hinting - rhs.hinting; 282 if (deltaInt != 0) return (deltaInt); 283 284 deltaInt = lhs.dirFlags - rhs.dirFlags; 285 if (deltaInt) return (deltaInt); 286 287 deltaInt = lhs.variant - rhs.variant; 288 if (deltaInt) return (deltaInt); 289 290 if (lhs.language < rhs.language) return -1; 291 if (lhs.language > rhs.language) return +1; 292 293 return memcmp(lhs.getText(), rhs.getText(), lhs.contextCount * sizeof(UChar)); 294 } 295 296 size_t TextLayoutCacheKey::getSize() const { 297 return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount; 298 } 299 300 hash_t TextLayoutCacheKey::hash() const { 301 uint32_t hash = JenkinsHashMix(0, start); 302 hash = JenkinsHashMix(hash, count); 303 /* contextCount not needed because it's included in text, below */ 304 hash = JenkinsHashMix(hash, hash_type(typeface)); 305 hash = JenkinsHashMix(hash, hash_type(textSize)); 306 hash = JenkinsHashMix(hash, hash_type(textSkewX)); 307 hash = JenkinsHashMix(hash, hash_type(textScaleX)); 308 hash = JenkinsHashMix(hash, flags); 309 hash = JenkinsHashMix(hash, hinting); 310 hash = JenkinsHashMix(hash, variant); 311 // Note: leaving out language is not problematic, as equality comparisons 312 // are still valid - the only bad thing that could happen is collisions. 313 hash = JenkinsHashMixShorts(hash, getText(), contextCount); 314 return JenkinsHashWhiten(hash); 315 } 316 317 /** 318 * TextLayoutCacheValue 319 */ 320 TextLayoutValue::TextLayoutValue(size_t contextCount) : 321 mTotalAdvance(0), mElapsedTime(0) { 322 // Give a hint for advances and glyphs vectors size 323 mAdvances.setCapacity(contextCount); 324 mGlyphs.setCapacity(contextCount); 325 mPos.setCapacity(contextCount * 2); 326 } 327 328 size_t TextLayoutValue::getSize() const { 329 return sizeof(TextLayoutValue) + sizeof(jfloat) * mAdvances.capacity() + 330 sizeof(jchar) * mGlyphs.capacity() + sizeof(jfloat) * mPos.capacity(); 331 } 332 333 void TextLayoutValue::setElapsedTime(uint32_t time) { 334 mElapsedTime = time; 335 } 336 337 uint32_t TextLayoutValue::getElapsedTime() { 338 return mElapsedTime; 339 } 340 341 TextLayoutShaper::TextLayoutShaper() { 342 mBuffer = hb_buffer_create(); 343 } 344 345 TextLayoutShaper::~TextLayoutShaper() { 346 hb_buffer_destroy(mBuffer); 347 } 348 349 void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars, 350 size_t start, size_t count, size_t contextCount, int dirFlags) { 351 352 computeValues(paint, chars, start, count, contextCount, dirFlags, 353 &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs, &value->mPos); 354 #if DEBUG_ADVANCES 355 ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count, 356 contextCount, value->mTotalAdvance); 357 #endif 358 } 359 360 void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars, 361 size_t start, size_t count, size_t contextCount, int dirFlags, 362 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, 363 Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) { 364 *outTotalAdvance = 0; 365 if (!count) { 366 return; 367 } 368 369 UBiDiLevel bidiReq = 0; 370 bool forceLTR = false; 371 bool forceRTL = false; 372 373 switch (dirFlags & kBidi_Mask) { 374 case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level 375 case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level 376 case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break; 377 case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break; 378 case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR 379 case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL 380 } 381 382 bool useSingleRun = false; 383 bool isRTL = forceRTL; 384 if (forceLTR || forceRTL) { 385 useSingleRun = true; 386 } else { 387 UBiDi* bidi = ubidi_open(); 388 if (bidi) { 389 UErrorCode status = U_ZERO_ERROR; 390 #if DEBUG_GLYPHS 391 ALOGD("******** ComputeValues -- start"); 392 ALOGD(" -- string = '%s'", String8(chars + start, count).string()); 393 ALOGD(" -- start = %d", start); 394 ALOGD(" -- count = %d", count); 395 ALOGD(" -- contextCount = %d", contextCount); 396 ALOGD(" -- bidiReq = %d", bidiReq); 397 #endif 398 ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status); 399 if (U_SUCCESS(status)) { 400 int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl 401 ssize_t rc = ubidi_countRuns(bidi, &status); 402 #if DEBUG_GLYPHS 403 ALOGD(" -- dirFlags = %d", dirFlags); 404 ALOGD(" -- paraDir = %d", paraDir); 405 ALOGD(" -- run-count = %d", int(rc)); 406 #endif 407 if (U_SUCCESS(status) && rc == 1) { 408 // Normal case: one run, status is ok 409 isRTL = (paraDir == 1); 410 useSingleRun = true; 411 } else if (!U_SUCCESS(status) || rc < 1) { 412 ALOGW("Need to force to single run -- string = '%s'," 413 " status = %d, rc = %d", 414 String8(chars + start, count).string(), status, int(rc)); 415 isRTL = (paraDir == 1); 416 useSingleRun = true; 417 } else { 418 int32_t end = start + count; 419 for (size_t i = 0; i < size_t(rc); ++i) { 420 int32_t startRun = -1; 421 int32_t lengthRun = -1; 422 UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun); 423 424 if (startRun == -1 || lengthRun == -1) { 425 // Something went wrong when getting the visual run, need to clear 426 // already computed data before doing a single run pass 427 ALOGW("Visual run is not valid"); 428 outGlyphs->clear(); 429 outAdvances->clear(); 430 outPos->clear(); 431 *outTotalAdvance = 0; 432 isRTL = (paraDir == 1); 433 useSingleRun = true; 434 break; 435 } 436 437 if (startRun >= end) { 438 continue; 439 } 440 int32_t endRun = startRun + lengthRun; 441 if (endRun <= int32_t(start)) { 442 continue; 443 } 444 if (startRun < int32_t(start)) { 445 startRun = int32_t(start); 446 } 447 if (endRun > end) { 448 endRun = end; 449 } 450 451 lengthRun = endRun - startRun; 452 isRTL = (runDir == UBIDI_RTL); 453 #if DEBUG_GLYPHS 454 ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d", 455 i, startRun, lengthRun, isRTL); 456 #endif 457 computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL, 458 outAdvances, outTotalAdvance, outGlyphs, outPos); 459 460 } 461 } 462 } else { 463 ALOGW("Cannot set Para"); 464 useSingleRun = true; 465 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL); 466 } 467 ubidi_close(bidi); 468 } else { 469 ALOGW("Cannot ubidi_open()"); 470 useSingleRun = true; 471 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL); 472 } 473 } 474 475 // Default single run case 476 if (useSingleRun){ 477 #if DEBUG_GLYPHS 478 ALOGD("Using a SINGLE BiDi Run " 479 "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL); 480 #endif 481 computeRunValues(paint, chars, start, count, contextCount, isRTL, 482 outAdvances, outTotalAdvance, outGlyphs, outPos); 483 } 484 485 #if DEBUG_GLYPHS 486 ALOGD(" -- Total returned glyphs-count = %d", outGlyphs->size()); 487 ALOGD("******** ComputeValues -- end"); 488 #endif 489 } 490 491 #define HB_IsHighSurrogate(ucs) \ 492 (((ucs) & 0xfc00) == 0xd800) 493 494 #define HB_IsLowSurrogate(ucs) \ 495 (((ucs) & 0xfc00) == 0xdc00) 496 497 #ifndef HB_SurrogateToUcs4 498 #define HB_SurrogateToUcs4_(high, low) \ 499 (((hb_codepoint_t)(high))<<10) + (low) - 0x35fdc00; 500 #endif 501 502 #define HB_InvalidCodePoint ~0u 503 504 hb_codepoint_t 505 utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) { 506 const uint16_t v = chars[(*iter)++]; 507 if (HB_IsHighSurrogate(v)) { 508 // surrogate pair 509 if (size_t(*iter) >= len) { 510 // the surrogate is incomplete. 511 return HB_InvalidCodePoint; 512 } 513 const uint16_t v2 = chars[(*iter)++]; 514 if (!HB_IsLowSurrogate(v2)) { 515 // invalidate surrogate pair. 516 (*iter)--; 517 return HB_InvalidCodePoint; 518 } 519 520 return HB_SurrogateToUcs4(v, v2); 521 } 522 523 if (HB_IsLowSurrogate(v)) { 524 // this isn't a valid code point 525 return HB_InvalidCodePoint; 526 } 527 528 return v; 529 } 530 531 hb_codepoint_t 532 utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) { 533 const uint16_t v = chars[(*iter)--]; 534 if (HB_IsLowSurrogate(v)) { 535 // surrogate pair 536 if (*iter < 0) { 537 // the surrogate is incomplete. 538 return HB_InvalidCodePoint; 539 } 540 const uint16_t v2 = chars[(*iter)--]; 541 if (!HB_IsHighSurrogate(v2)) { 542 // invalidate surrogate pair. 543 (*iter)++; 544 return HB_InvalidCodePoint; 545 } 546 547 return HB_SurrogateToUcs4(v2, v); 548 } 549 550 if (HB_IsHighSurrogate(v)) { 551 // this isn't a valid code point 552 return HB_InvalidCodePoint; 553 } 554 555 return v; 556 } 557 558 struct ScriptRun { 559 hb_script_t script; 560 size_t pos; 561 size_t length; 562 }; 563 564 hb_script_t code_point_to_script(hb_codepoint_t codepoint) { 565 static hb_unicode_funcs_t* u; 566 if (!u) { 567 u = hb_icu_get_unicode_funcs(); 568 } 569 return hb_unicode_script(u, codepoint); 570 } 571 572 bool 573 hb_utf16_script_run_next(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) { 574 if (size_t(*iter) == len) 575 return false; 576 577 run->pos = *iter; 578 const uint32_t init_cp = utf16_to_code_point(chars, len, iter); 579 const hb_script_t init_script = code_point_to_script(init_cp); 580 hb_script_t current_script = init_script; 581 run->script = init_script; 582 583 for (;;) { 584 if (size_t(*iter) == len) 585 break; 586 const ssize_t prev_iter = *iter; 587 const uint32_t cp = utf16_to_code_point(chars, len, iter); 588 const hb_script_t script = code_point_to_script(cp); 589 590 if (script != current_script) { 591 /* BEGIN android-changed 592 The condition was not correct by doing "a == b == constant" 593 END android-changed */ 594 if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) { 595 // If we started off as inherited, we take whatever we can find. 596 run->script = script; 597 current_script = script; 598 continue; 599 } else if (script == HB_SCRIPT_INHERITED) { 600 continue; 601 } else { 602 *iter = prev_iter; 603 break; 604 } 605 } 606 } 607 608 if (run->script == HB_SCRIPT_INHERITED) 609 run->script = HB_SCRIPT_COMMON; 610 611 run->length = *iter - run->pos; 612 return true; 613 } 614 615 bool 616 hb_utf16_script_run_prev(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) { 617 if (*iter == -1) 618 return false; 619 620 const size_t ending_index = *iter; 621 const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter); 622 const hb_script_t init_script = code_point_to_script(init_cp); 623 hb_script_t current_script = init_script; 624 run->script = init_script; 625 size_t break_iter = *iter; 626 627 for (;;) { 628 if (*iter < 0) 629 break; 630 const uint32_t cp = utf16_to_code_point_prev(chars, len, iter); 631 const hb_script_t script = code_point_to_script(cp); 632 633 if (script != current_script) { 634 if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) { 635 // If we started off as inherited, we take whatever we can find. 636 run->script = script; 637 current_script = script; 638 // In cases of script1 + inherited + script2, always group the inherited 639 // with script1. 640 break_iter = *iter; 641 continue; 642 } else if (script == HB_SCRIPT_INHERITED) { 643 continue; 644 } else { 645 *iter = break_iter; 646 break; 647 } 648 } else { 649 break_iter = *iter; 650 } 651 } 652 653 if (run->script == HB_SCRIPT_INHERITED) 654 run->script = HB_SCRIPT_COMMON; 655 656 run->pos = *iter + 1; 657 run->length = ending_index - *iter; 658 return true; 659 } 660 661 662 static void logGlyphs(hb_buffer_t* buffer) { 663 unsigned int numGlyphs; 664 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs); 665 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL); 666 ALOGD(" -- glyphs count=%d", numGlyphs); 667 for (size_t i = 0; i < numGlyphs; i++) { 668 ALOGD(" -- glyph[%d] = %d, cluster = %u, advance = %0.2f, offset.x = %0.2f, offset.y = %0.2f", i, 669 info[i].codepoint, 670 info[i].cluster, 671 HBFixedToFloat(positions[i].x_advance), 672 HBFixedToFloat(positions[i].x_offset), 673 HBFixedToFloat(positions[i].y_offset)); 674 } 675 } 676 677 void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars, 678 size_t start, size_t count, size_t contextCount, bool isRTL, 679 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, 680 Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) { 681 if (!count) { 682 // We cannot shape an empty run. 683 return; 684 } 685 686 // To be filled in later 687 for (size_t i = 0; i < count; i++) { 688 outAdvances->add(0); 689 } 690 691 // Set the string properties 692 const UChar* chars = contextChars + start; 693 694 // Define shaping paint properties 695 mShapingPaint.setTextSize(paint->getTextSize()); 696 float skewX = paint->getTextSkewX(); 697 mShapingPaint.setTextSkewX(skewX); 698 mShapingPaint.setTextScaleX(paint->getTextScaleX()); 699 mShapingPaint.setFlags(paint->getFlags()); 700 mShapingPaint.setHinting(paint->getHinting()); 701 mShapingPaint.setFontVariant(paint->getFontVariant()); 702 mShapingPaint.setLanguage(paint->getLanguage()); 703 704 // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script 705 // into the shaperItem 706 ssize_t indexFontRun = isRTL ? count - 1 : 0; 707 jfloat totalAdvance = *outTotalAdvance; 708 ScriptRun run; // relative to chars 709 while ((isRTL) ? 710 hb_utf16_script_run_prev(&run, chars, count, &indexFontRun): 711 hb_utf16_script_run_next(&run, chars, count, &indexFontRun)) { 712 713 #if DEBUG_GLYPHS 714 ALOGD("-------- Start of Script Run --------"); 715 ALOGD("Shaping Script Run with"); 716 ALOGD(" -- isRTL = %d", isRTL); 717 ALOGD(" -- HB script = %c%c%c%c", HB_UNTAG(run.script)); 718 ALOGD(" -- run.pos = %d", int(run.pos)); 719 ALOGD(" -- run.length = %d", int(run.length)); 720 ALOGD(" -- run = '%s'", String8(chars + run.pos, run.length).string()); 721 ALOGD(" -- string = '%s'", String8(chars, count).string()); 722 #endif 723 724 hb_buffer_reset(mBuffer); 725 // Note: if we want to set unicode functions, etc., this is the place. 726 727 hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); 728 hb_buffer_set_script(mBuffer, run.script); 729 // Should set language here (for bug 7004056) 730 hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length); 731 732 // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs 733 // and shape the Font run 734 size_t glyphBaseCount = shapeFontRun(paint); 735 unsigned int numGlyphs; 736 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs); 737 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(mBuffer, NULL); 738 739 #if DEBUG_GLYPHS 740 ALOGD("Got from Harfbuzz"); 741 ALOGD(" -- glyphBaseCount = %d", glyphBaseCount); 742 ALOGD(" -- num_glyph = %d", numGlyphs); 743 ALOGD(" -- isDevKernText = %d", paint->isDevKernText()); 744 ALOGD(" -- initial totalAdvance = %f", totalAdvance); 745 746 logGlyphs(mBuffer); 747 #endif 748 749 for (size_t i = 0; i < numGlyphs; i++) { 750 size_t cluster = info[i].cluster - start; 751 float xAdvance = HBFixedToFloat(positions[i].x_advance); 752 outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster); 753 outGlyphs->add(info[i].codepoint + glyphBaseCount); 754 float xo = HBFixedToFloat(positions[i].x_offset); 755 float yo = -HBFixedToFloat(positions[i].y_offset); 756 outPos->add(totalAdvance + xo + yo * skewX); 757 outPos->add(yo); 758 totalAdvance += xAdvance; 759 } 760 } 761 762 *outTotalAdvance = totalAdvance; 763 764 #if DEBUG_GLYPHS 765 ALOGD(" -- final totalAdvance = %f", totalAdvance); 766 ALOGD("-------- End of Script Run --------"); 767 #endif 768 } 769 770 /** 771 * Return the first typeface in the logical change, starting with this typeface, 772 * that contains the specified unichar, or NULL if none is found. 773 */ 774 SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface, 775 hb_script_t script) { 776 SkTypeface::Style currentStyle = SkTypeface::kNormal; 777 if (typeface) { 778 currentStyle = typeface->style(); 779 } 780 typeface = SkCreateTypefaceForScriptNG(script, currentStyle); 781 #if DEBUG_GLYPHS 782 ALOGD("Using Harfbuzz Script %c%c%c%c, Style %d", HB_UNTAG(script), currentStyle); 783 #endif 784 return typeface; 785 } 786 787 bool TextLayoutShaper::isComplexScript(hb_script_t script) { 788 switch (script) { 789 case HB_SCRIPT_COMMON: 790 case HB_SCRIPT_GREEK: 791 case HB_SCRIPT_CYRILLIC: 792 case HB_SCRIPT_HANGUL: 793 case HB_SCRIPT_INHERITED: 794 case HB_SCRIPT_HAN: 795 case HB_SCRIPT_KATAKANA: 796 case HB_SCRIPT_HIRAGANA: 797 return false; 798 default: 799 return true; 800 } 801 } 802 803 size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) { 804 // Update Harfbuzz Shaper 805 806 SkTypeface* typeface = paint->getTypeface(); 807 808 // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz 809 // This is needed as the Typeface used for shaping can be not the default one 810 // when we are shaping any script that needs to use a fallback Font. 811 // If we are a "common" script we dont need to shift 812 size_t baseGlyphCount = 0; 813 hb_codepoint_t firstUnichar = 0; 814 if (isComplexScript(hb_buffer_get_script(mBuffer))) { 815 unsigned int numGlyphs; 816 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs); 817 for (size_t i = 0; i < numGlyphs; i++) { 818 firstUnichar = info[i].codepoint; 819 if (firstUnichar != ' ') { 820 break; 821 } 822 } 823 baseGlyphCount = paint->getBaseGlyphCount(firstUnichar); 824 } 825 826 SkTypeface* scriptTypeface = NULL; 827 if (baseGlyphCount != 0) { 828 scriptTypeface = typefaceForScript(paint, typeface, 829 hb_buffer_get_script(mBuffer)); 830 #if DEBUG_GLYPHS 831 ALOGD("Using Default Typeface for script %c%c%c%c", 832 HB_UNTAG(hb_buffer_get_script(mBuffer))); 833 #endif 834 } 835 if (scriptTypeface) { 836 typeface = scriptTypeface; 837 } else { 838 baseGlyphCount = 0; 839 if (typeface) { 840 SkSafeRef(typeface); 841 } else { 842 typeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal); 843 #if DEBUG_GLYPHS 844 ALOGD("Using Default Typeface (normal style)"); 845 #endif 846 } 847 } 848 849 mShapingPaint.setTypeface(typeface); 850 hb_face_t* face = referenceCachedHBFace(typeface); 851 852 float sizeY = paint->getTextSize(); 853 float sizeX = sizeY * paint->getTextScaleX(); 854 hb_font_t* font = createFont(face, &mShapingPaint, sizeX, sizeY); 855 hb_face_destroy(face); 856 857 #if DEBUG_GLYPHS 858 ALOGD("Run typeface = %p, uniqueID = %d, face = %p", 859 typeface, typeface->uniqueID(), face); 860 #endif 861 SkSafeUnref(typeface); 862 863 hb_shape(font, mBuffer, NULL, 0); 864 hb_font_destroy(font); 865 866 return baseGlyphCount; 867 } 868 869 hb_face_t* TextLayoutShaper::referenceCachedHBFace(SkTypeface* typeface) { 870 SkFontID fontId = typeface->uniqueID(); 871 ssize_t index = mCachedHBFaces.indexOfKey(fontId); 872 if (index >= 0) { 873 return hb_face_reference(mCachedHBFaces.valueAt(index)); 874 } 875 // TODO: destroy function 876 hb_face_t* face = hb_face_create_for_tables(harfbuzzSkiaReferenceTable, typeface, NULL); 877 #if DEBUG_GLYPHS 878 ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface); 879 #endif 880 mCachedHBFaces.add(fontId, face); 881 return hb_face_reference(face); 882 } 883 884 void TextLayoutShaper::purgeCaches() { 885 size_t cacheSize = mCachedHBFaces.size(); 886 for (size_t i = 0; i < cacheSize; i++) { 887 hb_face_destroy(mCachedHBFaces.valueAt(i)); 888 } 889 mCachedHBFaces.clear(); 890 } 891 892 TextLayoutEngine::TextLayoutEngine() { 893 mShaper = new TextLayoutShaper(); 894 #if USE_TEXT_LAYOUT_CACHE 895 mTextLayoutCache = new TextLayoutCache(mShaper); 896 #else 897 mTextLayoutCache = NULL; 898 #endif 899 } 900 901 TextLayoutEngine::~TextLayoutEngine() { 902 delete mTextLayoutCache; 903 delete mShaper; 904 } 905 906 sp<TextLayoutValue> TextLayoutEngine::getValue(const SkPaint* paint, const jchar* text, 907 jint start, jint count, jint contextCount, jint dirFlags) { 908 sp<TextLayoutValue> value; 909 #if USE_TEXT_LAYOUT_CACHE 910 value = mTextLayoutCache->getValue(paint, text, start, count, 911 contextCount, dirFlags); 912 if (value == NULL) { 913 ALOGE("Cannot get TextLayoutCache value for text = '%s'", 914 String8(text + start, count).string()); 915 } 916 #else 917 value = new TextLayoutValue(count); 918 mShaper->computeValues(value.get(), paint, 919 reinterpret_cast<const UChar*>(text), start, count, contextCount, dirFlags); 920 #endif 921 return value; 922 } 923 924 void TextLayoutEngine::purgeCaches() { 925 #if USE_TEXT_LAYOUT_CACHE 926 mTextLayoutCache->purgeCaches(); 927 #if DEBUG_GLYPHS 928 ALOGD("Purged TextLayoutEngine caches"); 929 #endif 930 #endif 931 } 932 933 934 } // namespace android 935