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