TextLayoutCache.cpp revision 689e515ed2b8064c15e54d8ab69d87de54c5e0d6
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#include "TextLayoutCache.h" 18#include "TextLayout.h" 19 20namespace android { 21 22TextLayoutCache::TextLayoutCache() : 23 mCache(GenerationCache<TextLayoutCacheKey, sp<TextLayoutCacheValue> >::kUnlimitedCapacity), 24 mSize(0), mMaxSize(MB(DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB)), 25 mCacheHitCount(0), mNanosecondsSaved(0) { 26 init(); 27} 28 29TextLayoutCache::TextLayoutCache(uint32_t max): 30 mCache(GenerationCache<TextLayoutCacheKey, sp<TextLayoutCacheValue> >::kUnlimitedCapacity), 31 mSize(0), mMaxSize(max), 32 mCacheHitCount(0), mNanosecondsSaved(0) { 33 init(); 34} 35 36TextLayoutCache::~TextLayoutCache() { 37 mCache.clear(); 38} 39 40void TextLayoutCache::init() { 41 mCache.setOnEntryRemovedListener(this); 42 43 mDebugLevel = readRtlDebugLevel(); 44 mDebugEnabled = mDebugLevel & kRtlDebugCaches; 45 LOGD("Using TextLayoutCache debug level: %d - Debug Enabled: %d", mDebugLevel, mDebugEnabled); 46 47 mCacheStartTime = systemTime(SYSTEM_TIME_MONOTONIC); 48 if (mDebugEnabled) { 49 LOGD("TextLayoutCache start time: %lld", mCacheStartTime); 50 } 51 mInitialized = true; 52 53 if (mDebugEnabled) { 54#if RTL_USE_HARFBUZZ 55 LOGD("TextLayoutCache is using HARFBUZZ"); 56#else 57 LOGD("TextLayoutCache is using ICU"); 58#endif 59 } 60 61 if (mDebugEnabled) { 62 LOGD("TextLayoutCache initialization is done"); 63 } 64} 65 66/* 67 * Size management 68 */ 69 70uint32_t TextLayoutCache::getSize() { 71 return mSize; 72} 73 74uint32_t TextLayoutCache::getMaxSize() { 75 return mMaxSize; 76} 77 78void TextLayoutCache::setMaxSize(uint32_t maxSize) { 79 mMaxSize = maxSize; 80 removeOldests(); 81} 82 83void TextLayoutCache::removeOldests() { 84 while (mSize > mMaxSize) { 85 mCache.removeOldest(); 86 } 87} 88 89/** 90 * Callbacks 91 */ 92void TextLayoutCache::operator()(TextLayoutCacheKey& text, sp<TextLayoutCacheValue>& desc) { 93 if (desc != NULL) { 94 size_t totalSizeToDelete = text.getSize() + desc->getSize(); 95 mSize -= totalSizeToDelete; 96 if (mDebugEnabled) { 97 LOGD("Cache value deleted, size = %d", totalSizeToDelete); 98 } 99 desc.clear(); 100 } 101} 102 103/* 104 * Cache clearing 105 */ 106void TextLayoutCache::clear() { 107 mCache.clear(); 108} 109 110/* 111 * Caching 112 */ 113sp<TextLayoutCacheValue> TextLayoutCache::getValue(SkPaint* paint, 114 const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) { 115 AutoMutex _l(mLock); 116 nsecs_t startTime = 0; 117 if (mDebugEnabled) { 118 startTime = systemTime(SYSTEM_TIME_MONOTONIC); 119 } 120 121 // Create the key 122 TextLayoutCacheKey key(paint, text, start, count, contextCount, dirFlags); 123 124 // Get value from cache if possible 125 sp<TextLayoutCacheValue> value = mCache.get(key); 126 127 // Value not found for the key, we need to add a new value in the cache 128 if (value == NULL) { 129 value = new TextLayoutCacheValue(); 130 131 // Compute advances and store them 132 value->computeValues(paint, text, start, count, contextCount, dirFlags); 133 134 // Don't bother to add in the cache if the entry is too big 135 size_t size = key.getSize() + value->getSize(); 136 if (size <= mMaxSize) { 137 // Cleanup to make some room if needed 138 if (mSize + size > mMaxSize) { 139 if (mDebugEnabled) { 140 LOGD("TextLayoutCache: need to clean some entries " 141 "for making some room for a new entry"); 142 } 143 while (mSize + size > mMaxSize) { 144 // This will call the callback 145 mCache.removeOldest(); 146 } 147 } 148 149 // Update current cache size 150 mSize += size; 151 152 // Copy the text when we insert the new entry 153 key.internalTextCopy(); 154 mCache.put(key, value); 155 156 if (mDebugEnabled) { 157 // Update timing information for statistics 158 value->setElapsedTime(systemTime(SYSTEM_TIME_MONOTONIC) - startTime); 159 160 LOGD("CACHE MISS: Added entry for text='%s' with start=%d, count=%d, " 161 "contextCount=%d, entry size %d bytes, remaining space %d bytes" 162 " - Compute time in nanos: %d", 163 String8(text, contextCount).string(), start, count, contextCount, 164 size, mMaxSize - mSize, value->getElapsedTime()); 165 } 166 } else { 167 if (mDebugEnabled) { 168 LOGD("CACHE MISS: Calculated but not storing entry because it is too big " 169 "for text='%s' with start=%d, count=%d, contextCount=%d, " 170 "entry size %d bytes, remaining space %d bytes" 171 " - Compute time in nanos: %d", 172 String8(text, contextCount).string(), start, count, contextCount, 173 size, mMaxSize - mSize, value->getElapsedTime()); 174 } 175 value.clear(); 176 } 177 } else { 178 // This is a cache hit, just log timestamp and user infos 179 if (mDebugEnabled) { 180 nsecs_t elapsedTimeThruCacheGet = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; 181 mNanosecondsSaved += (value->getElapsedTime() - elapsedTimeThruCacheGet); 182 ++mCacheHitCount; 183 184 if (value->getElapsedTime() > 0) { 185 float deltaPercent = 100 * ((value->getElapsedTime() - elapsedTimeThruCacheGet) 186 / ((float)value->getElapsedTime())); 187 LOGD("CACHE HIT #%d for text='%s' with start=%d, count=%d, contextCount=%d " 188 "- Compute time in nanos: %d - " 189 "Cache get time in nanos: %lld - Gain in percent: %2.2f", 190 mCacheHitCount, String8(text, contextCount).string(), start, count, 191 contextCount, 192 value->getElapsedTime(), elapsedTimeThruCacheGet, deltaPercent); 193 } 194 if (mCacheHitCount % DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL == 0) { 195 dumpCacheStats(); 196 } 197 } 198 } 199 return value; 200} 201 202void TextLayoutCache::dumpCacheStats() { 203 float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize)); 204 float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000; 205 LOGD("------------------------------------------------"); 206 LOGD("TextLayoutCache stats"); 207 LOGD("------------------------------------------------"); 208 LOGD("running : %.0f seconds", timeRunningInSec); 209 LOGD("size : %d bytes", mMaxSize); 210 LOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent); 211 LOGD("hits : %d", mCacheHitCount); 212 LOGD("saved : %lld milliseconds", mNanosecondsSaved / 1000000); 213 LOGD("------------------------------------------------"); 214} 215 216/** 217 * TextLayoutCacheKey 218 */ 219TextLayoutCacheKey::TextLayoutCacheKey(): text(NULL), start(0), count(0), contextCount(0), 220 dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0), 221 hinting(SkPaint::kNo_Hinting) { 222} 223 224TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, 225 const UChar* text, size_t start, size_t count, 226 size_t contextCount, int dirFlags) : 227 text(text), start(start), count(count), contextCount(contextCount), 228 dirFlags(dirFlags) { 229 typeface = paint->getTypeface(); 230 textSize = paint->getTextSize(); 231 textSkewX = paint->getTextSkewX(); 232 textScaleX = paint->getTextScaleX(); 233 flags = paint->getFlags(); 234 hinting = paint->getHinting(); 235} 236 237bool TextLayoutCacheKey::operator<(const TextLayoutCacheKey& rhs) const { 238 LTE_INT(count) { 239 LTE_INT(contextCount) { 240 LTE_INT(start) { 241 LTE_INT(typeface) { 242 LTE_FLOAT(textSize) { 243 LTE_FLOAT(textSkewX) { 244 LTE_FLOAT(textScaleX) { 245 LTE_INT(flags) { 246 LTE_INT(hinting) { 247 LTE_INT(dirFlags) { 248 return strncmp16(text, rhs.text, contextCount) < 0; 249 } 250 } 251 } 252 } 253 } 254 } 255 } 256 } 257 } 258 } 259 return false; 260} 261 262void TextLayoutCacheKey::internalTextCopy() { 263 textCopy.setTo(text, contextCount); 264 text = textCopy.string(); 265} 266 267size_t TextLayoutCacheKey::getSize() { 268 return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount; 269} 270 271/** 272 * TextLayoutCacheValue 273 */ 274TextLayoutCacheValue::TextLayoutCacheValue() : 275 mAdvances(NULL), mTotalAdvance(0), mAdvancesCount(0), 276 mGlyphs(NULL), mGlyphsCount(0), mElapsedTime(0) { 277} 278 279TextLayoutCacheValue::~TextLayoutCacheValue() { 280 delete[] mAdvances; 281 delete[] mGlyphs; 282} 283 284void TextLayoutCacheValue::setElapsedTime(uint32_t time) { 285 mElapsedTime = time; 286} 287 288uint32_t TextLayoutCacheValue::getElapsedTime() { 289 return mElapsedTime; 290} 291 292void TextLayoutCacheValue::computeValues(SkPaint* paint, const UChar* chars, size_t start, 293 size_t count, size_t contextCount, int dirFlags) { 294 mAdvancesCount = count; 295 mAdvances = new float[count]; 296 297#if RTL_USE_HARFBUZZ 298 computeValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, 299 mAdvances, &mTotalAdvance, &mGlyphs, &mGlyphsCount); 300#else 301 computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags, 302 mAdvances, &mTotalAdvance); 303#endif 304#if DEBUG_ADVANCES 305 LOGD("Advances - count=%d - countextCount=%d - totalAdvance=%f - " 306 "adv[0]=%f adv[1]=%f adv[2]=%f adv[3]=%f", count, contextCount, mTotalAdvance, 307 mAdvances[0], mAdvances[1], mAdvances[2], mAdvances[3]); 308#endif 309} 310 311size_t TextLayoutCacheValue::getSize() { 312 return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * mAdvancesCount + 313 sizeof(jchar) * mGlyphsCount; 314} 315 316void TextLayoutCacheValue::setupShaperItem(HB_ShaperItem* shaperItem, HB_FontRec* font, 317 FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, 318 size_t contextCount, int dirFlags) { 319 bool isRTL = dirFlags & 0x1; 320 321 font->klass = &harfbuzzSkiaClass; 322 font->userData = 0; 323 // The values which harfbuzzSkiaClass returns are already scaled to 324 // pixel units, so we just set all these to one to disable further 325 // scaling. 326 font->x_ppem = 1; 327 font->y_ppem = 1; 328 font->x_scale = 1; 329 font->y_scale = 1; 330 331 memset(shaperItem, 0, sizeof(*shaperItem)); 332 shaperItem->font = font; 333 shaperItem->face = HB_NewFace(shaperItem->font, harfbuzzSkiaGetTable); 334 335 shaperItem->kerning_applied = false; 336 337 // We cannot know, ahead of time, how many glyphs a given script run 338 // will produce. We take a guess that script runs will not produce more 339 // than twice as many glyphs as there are code points plus a bit of 340 // padding and fallback if we find that we are wrong. 341 createGlyphArrays(shaperItem, (contextCount + 2) * 2); 342 343 // Free memory for clusters if needed and recreate the clusters array 344 if (shaperItem->log_clusters) { 345 delete shaperItem->log_clusters; 346 } 347 shaperItem->log_clusters = new unsigned short[contextCount]; 348 349 shaperItem->item.pos = start; 350 shaperItem->item.length = count; 351 shaperItem->item.bidiLevel = isRTL; 352 shaperItem->item.script = isRTL ? HB_Script_Arabic : HB_Script_Common; 353 354 shaperItem->string = chars; 355 shaperItem->stringLength = contextCount; 356 357 fontData->typeFace = paint->getTypeface(); 358 fontData->textSize = paint->getTextSize(); 359 fontData->textSkewX = paint->getTextSkewX(); 360 fontData->textScaleX = paint->getTextScaleX(); 361 fontData->flags = paint->getFlags(); 362 fontData->hinting = paint->getHinting(); 363 364 shaperItem->font->userData = fontData; 365} 366 367void TextLayoutCacheValue::shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontRec* font, 368 FontData* fontData, SkPaint* paint, const UChar* chars, size_t start, size_t count, 369 size_t contextCount, int dirFlags) { 370 // Setup Harfbuzz Shaper 371 setupShaperItem(shaperItem, font, fontData, paint, chars, start, count, 372 contextCount, dirFlags); 373 374 // Shape 375 resetGlyphArrays(shaperItem); 376 while (!HB_ShapeItem(shaperItem)) { 377 // We overflowed our arrays. Resize and retry. 378 // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size. 379 deleteGlyphArrays(shaperItem); 380 createGlyphArrays(shaperItem, shaperItem->num_glyphs << 1); 381 resetGlyphArrays(shaperItem); 382 } 383} 384 385struct GlyphRun { 386 inline GlyphRun() {} 387 inline GlyphRun(jchar* glyphs, size_t glyphsCount, bool isRTL) : 388 glyphs(glyphs), glyphsCount(glyphsCount), isRTL(isRTL) { } 389 jchar* glyphs; 390 size_t glyphsCount; 391 int isRTL; 392}; 393 394void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, 395 size_t start, size_t count, size_t contextCount, int dirFlags, 396 jfloat* outAdvances, jfloat* outTotalAdvance, 397 jchar** outGlyphs, size_t* outGlyphsCount) { 398 399 UBiDiLevel bidiReq = 0; 400 bool forceLTR = false; 401 bool forceRTL = false; 402 403 switch (dirFlags) { 404 case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level 405 case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level 406 case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break; 407 case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break; 408 case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR 409 case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL 410 } 411 412 if (forceLTR || forceRTL) { 413#if DEBUG_GLYPHS 414 LOGD("computeValuesWithHarfbuzz -- forcing run with LTR=%d RTL=%d", 415 forceLTR, forceRTL); 416#endif 417 computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, 418 outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount); 419 } else { 420 UBiDi* bidi = ubidi_open(); 421 if (bidi) { 422 UErrorCode status = U_ZERO_ERROR; 423 LOGD("computeValuesWithHarfbuzz -- bidiReq=%d", bidiReq); 424 ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status); 425 if (U_SUCCESS(status)) { 426 int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl 427 size_t rc = ubidi_countRuns(bidi, &status); 428#if DEBUG_GLYPHS 429 LOGD("computeValuesWithHarfbuzz -- dirFlags=%d run-count=%d paraDir=%d", dirFlags, rc, paraDir); 430#endif 431 432 if (rc == 1 || !U_SUCCESS(status)) { 433 LOGD("HERE !!!"); 434 computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, 435 dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount); 436 ubidi_close(bidi); 437 return; 438 } 439 440 size_t runIndex = 0; 441 Vector<GlyphRun> glyphRuns; 442 for (size_t i = 0; i < rc; ++i) { 443 int32_t startRun; 444 int32_t lengthRun; 445 UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun); 446 447 int newFlags = (runDir == UBIDI_RTL) ? kDirection_RTL : kDirection_LTR; 448 jfloat runTotalAdvance = 0; 449 jchar* runGlyphs; 450 size_t runGlyphsCount = 0; 451 452#if DEBUG_GLYPHS 453 LOGD("computeValuesWithHarfbuzz -- run-start=%d run-len=%d newFlags=%d", 454 startRun, lengthRun, newFlags); 455#endif 456 computeRunValuesWithHarfbuzz(paint, chars, startRun, 457 lengthRun, contextCount, newFlags, 458 outAdvances + runIndex, &runTotalAdvance, 459 &runGlyphs, &runGlyphsCount); 460 461 runIndex += lengthRun; 462 463 *outTotalAdvance += runTotalAdvance; 464 *outGlyphsCount += runGlyphsCount; 465 466#if DEBUG_GLYPHS 467 LOGD("computeValuesWithHarfbuzz -- run=%d run-glyphs-count=%d", 468 i, runGlyphsCount); 469 for (size_t j = 0; j < runGlyphsCount; j++) { 470 LOGD(" -- glyphs[%d]=%d", j, runGlyphs[j]); 471 } 472#endif 473 glyphRuns.push(GlyphRun(runGlyphs, runGlyphsCount, newFlags)); 474 } 475 476#if DEBUG_GLYPHS 477 LOGD("computeValuesWithHarfbuzz -- total-glyphs-count=%d", *outGlyphsCount); 478#endif 479 *outGlyphs = new jchar[*outGlyphsCount]; 480 jchar* glyphs = *outGlyphs; 481 for (size_t i = 0; i < glyphRuns.size(); i++) { 482 const GlyphRun& glyphRun = glyphRuns.itemAt(i); 483 if (glyphRun.isRTL) { 484 for (size_t n = 0; n < glyphRun.glyphsCount; n++) { 485 glyphs[glyphRun.glyphsCount - n - 1] = glyphRun.glyphs[n]; 486 } 487 } else { 488 memcpy(glyphs, glyphRun.glyphs, glyphRun.glyphsCount * sizeof(jchar)); 489 } 490 glyphs += glyphRun.glyphsCount; 491 delete[] glyphRun.glyphs; 492 } 493 } 494 ubidi_close(bidi); 495 } 496 } 497} 498 499void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, 500 size_t start, size_t count, size_t contextCount, int dirFlags, 501 jfloat* outAdvances, jfloat* outTotalAdvance, 502 jchar** outGlyphs, size_t* outGlyphsCount) { 503 504 bool isRTL = dirFlags & 0x1; 505 506 HB_ShaperItem shaperItem; 507 HB_FontRec font; 508 FontData fontData; 509 shapeWithHarfbuzz(&shaperItem, &font, &fontData, paint, chars, start, count, 510 contextCount, dirFlags); 511 512#if DEBUG_GLYPHS 513 LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs, 514 shaperItem.kerning_applied); 515 LOGD(" -- string= '%s'", String8(chars + start, count).string()); 516 LOGD(" -- isDevKernText=%d", paint->isDevKernText()); 517#endif 518 519 // Get Advances and their total 520 jfloat totalAdvance = 0; 521 for (size_t i = 0; i < count; i++) { 522 totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]); 523#if DEBUG_ADVANCES 524 LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i], 525 totalAdvance); 526#endif 527 } 528 *outTotalAdvance = totalAdvance; 529 530 // Get Glyphs 531 if (outGlyphs) { 532 *outGlyphsCount = shaperItem.num_glyphs; 533 *outGlyphs = new jchar[shaperItem.num_glyphs]; 534 for (size_t i = 0; i < shaperItem.num_glyphs; i++) { 535 (*outGlyphs)[i] = (jchar) shaperItem.glyphs[i]; 536 } 537 } 538 539 // Cleaning 540 deleteGlyphArrays(&shaperItem); 541 HB_FreeFace(shaperItem.face); 542} 543 544void TextLayoutCacheValue::computeAdvancesWithICU(SkPaint* paint, const UChar* chars, 545 size_t start, size_t count, size_t contextCount, int dirFlags, 546 jfloat* outAdvances, jfloat* outTotalAdvance) { 547 SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount); 548 jchar* buffer = tempBuffer.get(); 549 SkScalar* scalarArray = (SkScalar*)outAdvances; 550 551 // this is where we'd call harfbuzz 552 // for now we just use ushape.c 553 size_t widths; 554 const jchar* text; 555 if (dirFlags & 0x1) { // rtl, call arabic shaping in case 556 UErrorCode status = U_ZERO_ERROR; 557 // Use fixed length since we need to keep start and count valid 558 u_shapeArabic(chars, contextCount, buffer, contextCount, 559 U_SHAPE_LENGTH_FIXED_SPACES_NEAR | 560 U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE | 561 U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); 562 // we shouldn't fail unless there's an out of memory condition, 563 // in which case we're hosed anyway 564 for (int i = start, e = i + count; i < e; ++i) { 565 if (buffer[i] == UNICODE_NOT_A_CHAR) { 566 buffer[i] = UNICODE_ZWSP; // zero-width-space for skia 567 } 568 } 569 text = buffer + start; 570 widths = paint->getTextWidths(text, count << 1, scalarArray); 571 } else { 572 text = chars + start; 573 widths = paint->getTextWidths(text, count << 1, scalarArray); 574 } 575 576 jfloat totalAdvance = 0; 577 if (widths < count) { 578#if DEBUG_ADVANCES 579 LOGD("ICU -- count=%d", widths); 580#endif 581 // Skia operates on code points, not code units, so surrogate pairs return only 582 // one value. Expand the result so we have one value per UTF-16 code unit. 583 584 // Note, skia's getTextWidth gets confused if it encounters a surrogate pair, 585 // leaving the remaining widths zero. Not nice. 586 for (size_t i = 0, p = 0; i < widths; ++i) { 587 totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]); 588 if (p < count && 589 text[p] >= UNICODE_FIRST_LOW_SURROGATE && 590 text[p] < UNICODE_FIRST_PRIVATE_USE && 591 text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE && 592 text[p-1] < UNICODE_FIRST_LOW_SURROGATE) { 593 outAdvances[p++] = 0; 594 } 595#if DEBUG_ADVANCES 596 LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); 597#endif 598 } 599 } else { 600#if DEBUG_ADVANCES 601 LOGD("ICU -- count=%d", count); 602#endif 603 for (size_t i = 0; i < count; i++) { 604 totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]); 605#if DEBUG_ADVANCES 606 LOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); 607#endif 608 } 609 } 610 *outTotalAdvance = totalAdvance; 611} 612 613void TextLayoutCacheValue::deleteGlyphArrays(HB_ShaperItem* shaperItem) { 614 delete[] shaperItem->glyphs; 615 delete[] shaperItem->attributes; 616 delete[] shaperItem->advances; 617 delete[] shaperItem->offsets; 618} 619 620void TextLayoutCacheValue::createGlyphArrays(HB_ShaperItem* shaperItem, int size) { 621 shaperItem->glyphs = new HB_Glyph[size]; 622 shaperItem->attributes = new HB_GlyphAttributes[size]; 623 shaperItem->advances = new HB_Fixed[size]; 624 shaperItem->offsets = new HB_FixedPoint[size]; 625 shaperItem->num_glyphs = size; 626} 627 628void TextLayoutCacheValue::resetGlyphArrays(HB_ShaperItem* shaperItem) { 629 int size = shaperItem->num_glyphs; 630 // All the types here don't have pointers. It is safe to reset to 631 // zero unless Harfbuzz breaks the compatibility in the future. 632 memset(shaperItem->glyphs, 0, size * sizeof(shaperItem->glyphs[0])); 633 memset(shaperItem->attributes, 0, size * sizeof(shaperItem->attributes[0])); 634 memset(shaperItem->advances, 0, size * sizeof(shaperItem->advances[0])); 635 memset(shaperItem->offsets, 0, size * sizeof(shaperItem->offsets[0])); 636} 637 638 639} // namespace android 640