OSDN Git Service

am 6d191ed9: Added the necessary Harfbuzz scripts for rendering Japanese text correctly.
[android-x86/frameworks-base.git] / core / jni / android / graphics / TextLayoutCache.cpp
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     init();
343
344     mBuffer = hb_buffer_create();
345 }
346
347 void TextLayoutShaper::init() {
348     mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
349 }
350
351 void TextLayoutShaper::unrefTypefaces() {
352     SkSafeUnref(mDefaultTypeface);
353 }
354
355 TextLayoutShaper::~TextLayoutShaper() {
356     hb_buffer_destroy(mBuffer);
357
358     unrefTypefaces();
359 }
360
361 void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
362         size_t start, size_t count, size_t contextCount, int dirFlags) {
363
364     computeValues(paint, chars, start, count, contextCount, dirFlags,
365             &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs, &value->mPos);
366 #if DEBUG_ADVANCES
367     ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
368             contextCount, value->mTotalAdvance);
369 #endif
370 }
371
372 void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
373         size_t start, size_t count, size_t contextCount, int dirFlags,
374         Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
375         Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
376         *outTotalAdvance = 0;
377         if (!count) {
378             return;
379         }
380
381         UBiDiLevel bidiReq = 0;
382         bool forceLTR = false;
383         bool forceRTL = false;
384
385         switch (dirFlags & kBidi_Mask) {
386             case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
387             case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
388             case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
389             case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
390             case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR
391             case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
392         }
393
394         bool useSingleRun = false;
395         bool isRTL = forceRTL;
396         if (forceLTR || forceRTL) {
397             useSingleRun = true;
398         } else {
399             UBiDi* bidi = ubidi_open();
400             if (bidi) {
401                 UErrorCode status = U_ZERO_ERROR;
402 #if DEBUG_GLYPHS
403                 ALOGD("******** ComputeValues -- start");
404                 ALOGD("      -- string = '%s'", String8(chars + start, count).string());
405                 ALOGD("      -- start = %d", start);
406                 ALOGD("      -- count = %d", count);
407                 ALOGD("      -- contextCount = %d", contextCount);
408                 ALOGD("      -- bidiReq = %d", bidiReq);
409 #endif
410                 ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
411                 if (U_SUCCESS(status)) {
412                     int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
413                     ssize_t rc = ubidi_countRuns(bidi, &status);
414 #if DEBUG_GLYPHS
415                     ALOGD("      -- dirFlags = %d", dirFlags);
416                     ALOGD("      -- paraDir = %d", paraDir);
417                     ALOGD("      -- run-count = %d", int(rc));
418 #endif
419                     if (U_SUCCESS(status) && rc == 1) {
420                         // Normal case: one run, status is ok
421                         isRTL = (paraDir == 1);
422                         useSingleRun = true;
423                     } else if (!U_SUCCESS(status) || rc < 1) {
424                         ALOGW("Need to force to single run -- string = '%s',"
425                                 " status = %d, rc = %d",
426                                 String8(chars + start, count).string(), status, int(rc));
427                         isRTL = (paraDir == 1);
428                         useSingleRun = true;
429                     } else {
430                         int32_t end = start + count;
431                         for (size_t i = 0; i < size_t(rc); ++i) {
432                             int32_t startRun = -1;
433                             int32_t lengthRun = -1;
434                             UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
435
436                             if (startRun == -1 || lengthRun == -1) {
437                                 // Something went wrong when getting the visual run, need to clear
438                                 // already computed data before doing a single run pass
439                                 ALOGW("Visual run is not valid");
440                                 outGlyphs->clear();
441                                 outAdvances->clear();
442                                 outPos->clear();
443                                 *outTotalAdvance = 0;
444                                 isRTL = (paraDir == 1);
445                                 useSingleRun = true;
446                                 break;
447                             }
448
449                             if (startRun >= end) {
450                                 continue;
451                             }
452                             int32_t endRun = startRun + lengthRun;
453                             if (endRun <= int32_t(start)) {
454                                 continue;
455                             }
456                             if (startRun < int32_t(start)) {
457                                 startRun = int32_t(start);
458                             }
459                             if (endRun > end) {
460                                 endRun = end;
461                             }
462
463                             lengthRun = endRun - startRun;
464                             isRTL = (runDir == UBIDI_RTL);
465 #if DEBUG_GLYPHS
466                             ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
467                                     i, startRun, lengthRun, isRTL);
468 #endif
469                             computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
470                                     outAdvances, outTotalAdvance, outGlyphs, outPos);
471
472                         }
473                     }
474                 } else {
475                     ALOGW("Cannot set Para");
476                     useSingleRun = true;
477                     isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
478                 }
479                 ubidi_close(bidi);
480             } else {
481                 ALOGW("Cannot ubidi_open()");
482                 useSingleRun = true;
483                 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
484             }
485         }
486
487         // Default single run case
488         if (useSingleRun){
489 #if DEBUG_GLYPHS
490             ALOGD("Using a SINGLE BiDi Run "
491                     "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
492 #endif
493             computeRunValues(paint, chars, start, count, contextCount, isRTL,
494                     outAdvances, outTotalAdvance, outGlyphs, outPos);
495         }
496
497 #if DEBUG_GLYPHS
498         ALOGD("      -- Total returned glyphs-count = %d", outGlyphs->size());
499         ALOGD("******** ComputeValues -- end");
500 #endif
501 }
502
503 #define HB_IsHighSurrogate(ucs) \
504     (((ucs) & 0xfc00) == 0xd800)
505
506 #define HB_IsLowSurrogate(ucs) \
507     (((ucs) & 0xfc00) == 0xdc00)
508
509 #ifndef HB_SurrogateToUcs4
510 #define HB_SurrogateToUcs4_(high, low) \
511     (((hb_codepoint_t)(high))<<10) + (low) - 0x35fdc00;
512 #endif
513
514 #define HB_InvalidCodePoint ~0u
515
516 hb_codepoint_t
517 utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) {
518   const uint16_t v = chars[(*iter)++];
519   if (HB_IsHighSurrogate(v)) {
520     // surrogate pair
521     if (size_t(*iter) >= len) {
522       // the surrogate is incomplete.
523       return HB_InvalidCodePoint;
524     }
525     const uint16_t v2 = chars[(*iter)++];
526     if (!HB_IsLowSurrogate(v2)) {
527       // invalidate surrogate pair.
528       (*iter)--;
529       return HB_InvalidCodePoint;
530     }
531
532     return HB_SurrogateToUcs4(v, v2);
533   }
534
535   if (HB_IsLowSurrogate(v)) {
536     // this isn't a valid code point
537     return HB_InvalidCodePoint;
538   }
539
540   return v;
541 }
542
543 hb_codepoint_t
544 utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) {
545   const uint16_t v = chars[(*iter)--];
546   if (HB_IsLowSurrogate(v)) {
547     // surrogate pair
548     if (*iter < 0) {
549       // the surrogate is incomplete.
550       return HB_InvalidCodePoint;
551     }
552     const uint16_t v2 = chars[(*iter)--];
553     if (!HB_IsHighSurrogate(v2)) {
554       // invalidate surrogate pair.
555       (*iter)++;
556       return HB_InvalidCodePoint;
557     }
558
559     return HB_SurrogateToUcs4(v2, v);
560   }
561
562   if (HB_IsHighSurrogate(v)) {
563     // this isn't a valid code point
564     return HB_InvalidCodePoint;
565   }
566
567   return v;
568 }
569
570 struct ScriptRun {
571     hb_script_t script;
572     size_t pos;
573     size_t length;
574 };
575
576 hb_script_t code_point_to_script(hb_codepoint_t codepoint) {
577     static hb_unicode_funcs_t* u;
578     if (!u) {
579         u = hb_icu_get_unicode_funcs();
580     }
581     return hb_unicode_script(u, codepoint);
582 }
583
584 bool
585 hb_utf16_script_run_next(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
586   if (size_t(*iter) == len)
587     return false;
588
589   run->pos = *iter;
590   const uint32_t init_cp = utf16_to_code_point(chars, len, iter);
591   const hb_script_t init_script = code_point_to_script(init_cp);
592   hb_script_t current_script = init_script;
593   run->script = init_script;
594
595   for (;;) {
596     if (size_t(*iter) == len)
597       break;
598     const ssize_t prev_iter = *iter;
599     const uint32_t cp = utf16_to_code_point(chars, len, iter);
600     const hb_script_t script = code_point_to_script(cp);
601
602     if (script != current_script) {
603         /* BEGIN android-changed
604            The condition was not correct by doing "a == b == constant"
605            END android-changed */
606       if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
607         // If we started off as inherited, we take whatever we can find.
608         run->script = script;
609         current_script = script;
610         continue;
611       } else if (script == HB_SCRIPT_INHERITED) {
612         continue;
613       } else {
614         *iter = prev_iter;
615         break;
616       }
617     }
618   }
619
620   if (run->script == HB_SCRIPT_INHERITED)
621     run->script = HB_SCRIPT_COMMON;
622
623   run->length = *iter - run->pos;
624   return true;
625 }
626
627 bool
628 hb_utf16_script_run_prev(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
629   if (*iter == -1)
630     return false;
631
632   const size_t ending_index = *iter;
633   const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter);
634   const hb_script_t init_script = code_point_to_script(init_cp);
635   hb_script_t current_script = init_script;
636   run->script = init_script;
637
638   for (;;) {
639     if (*iter < 0)
640       break;
641     const ssize_t prev_iter = *iter;
642     const uint32_t cp = utf16_to_code_point_prev(chars, len, iter);
643     const hb_script_t script = code_point_to_script(cp);
644
645     if (script != current_script) {
646       if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
647         // If we started off as inherited, we take whatever we can find.
648         run->script = script;
649         current_script = script;
650         continue;
651       } else if (script == HB_SCRIPT_INHERITED) {
652         /* BEGIN android-changed
653            We apply the same fix for Chrome to Android.
654            Chrome team will talk with upsteam about it.
655            Just assume that whatever follows this combining character is within
656            the same script.  This is incorrect if you had language1 + combining
657            char + language 2, but that is rare and this code is suspicious
658            anyway.
659            END android-changed */
660         continue;
661       } else {
662         *iter = prev_iter;
663         break;
664       }
665     }
666   }
667
668   if (run->script == HB_SCRIPT_INHERITED)
669     run->script = HB_SCRIPT_COMMON;
670
671   run->pos = *iter + 1;
672   run->length = ending_index - *iter;
673   return true;
674 }
675
676
677 static void logGlyphs(hb_buffer_t* buffer) {
678     unsigned int numGlyphs;
679     hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
680     hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
681     ALOGD("         -- glyphs count=%d", numGlyphs);
682     for (size_t i = 0; i < numGlyphs; i++) {
683         ALOGD("         -- glyph[%d] = %d, cluster = %u, advance = %0.2f, offset.x = %0.2f, offset.y = %0.2f", i,
684                 info[i].codepoint,
685                 info[i].cluster,
686                 HBFixedToFloat(positions[i].x_advance),
687                 HBFixedToFloat(positions[i].x_offset),
688                 HBFixedToFloat(positions[i].y_offset));
689     }
690 }
691
692 void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars,
693         size_t start, size_t count, size_t contextCount, bool isRTL,
694         Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
695         Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
696     if (!count) {
697         // We cannot shape an empty run.
698         return;
699     }
700
701     // To be filled in later
702     for (size_t i = 0; i < count; i++) {
703         outAdvances->add(0);
704     }
705
706     // Set the string properties
707     const UChar* chars = contextChars + start;
708
709     // Define shaping paint properties
710     mShapingPaint.setTextSize(paint->getTextSize());
711     float skewX = paint->getTextSkewX();
712     mShapingPaint.setTextSkewX(skewX);
713     mShapingPaint.setTextScaleX(paint->getTextScaleX());
714     mShapingPaint.setFlags(paint->getFlags());
715     mShapingPaint.setHinting(paint->getHinting());
716     mShapingPaint.setFontVariant(paint->getFontVariant());
717     mShapingPaint.setLanguage(paint->getLanguage());
718
719     // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
720     // into the shaperItem
721     ssize_t indexFontRun = isRTL ? count - 1 : 0;
722     jfloat totalAdvance = *outTotalAdvance;
723     ScriptRun run;  // relative to chars
724     while ((isRTL) ?
725             hb_utf16_script_run_prev(&run, chars, count, &indexFontRun):
726             hb_utf16_script_run_next(&run, chars, count, &indexFontRun)) {
727
728 #if DEBUG_GLYPHS
729         ALOGD("-------- Start of Script Run --------");
730         ALOGD("Shaping Script Run with");
731         ALOGD("         -- isRTL = %d", isRTL);
732         ALOGD("         -- HB script = %c%c%c%c", HB_UNTAG(run.script));
733         ALOGD("         -- run.pos = %d", int(run.pos));
734         ALOGD("         -- run.length = %d", int(run.length));
735         ALOGD("         -- run = '%s'", String8(chars + run.pos, run.length).string());
736         ALOGD("         -- string = '%s'", String8(chars, count).string());
737 #endif
738
739         hb_buffer_reset(mBuffer);
740         // Note: if we want to set unicode functions, etc., this is the place.
741         
742         hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
743         hb_buffer_set_script(mBuffer, run.script);
744         // Should set language here (for bug 7004056)
745         hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length);
746
747         // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
748         // and shape the Font run
749         size_t glyphBaseCount = shapeFontRun(paint);
750         unsigned int numGlyphs;
751         hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
752         hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(mBuffer, NULL);
753
754 #if DEBUG_GLYPHS
755         ALOGD("Got from Harfbuzz");
756         ALOGD("         -- glyphBaseCount = %d", glyphBaseCount);
757         ALOGD("         -- num_glyph = %d", numGlyphs);
758         ALOGD("         -- isDevKernText = %d", paint->isDevKernText());
759         ALOGD("         -- initial totalAdvance = %f", totalAdvance);
760
761         logGlyphs(mBuffer);
762 #endif
763
764         for (size_t i = 0; i < numGlyphs; i++) {
765             size_t cluster = info[i].cluster - start;
766             float xAdvance = HBFixedToFloat(positions[i].x_advance);
767             outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster);
768             outGlyphs->add(info[i].codepoint + glyphBaseCount);
769             float xo = HBFixedToFloat(positions[i].x_offset);
770             float yo = -HBFixedToFloat(positions[i].y_offset);
771             outPos->add(totalAdvance + xo + yo * skewX);
772             outPos->add(yo);
773             totalAdvance += xAdvance;
774         }
775     }
776
777     *outTotalAdvance = totalAdvance;
778
779 #if DEBUG_GLYPHS
780     ALOGD("         -- final totalAdvance = %f", totalAdvance);
781     ALOGD("-------- End of Script Run --------");
782 #endif
783 }
784
785 /**
786  * Return the first typeface in the logical change, starting with this typeface,
787  * that contains the specified unichar, or NULL if none is found.
788  * 
789  * Note that this function does _not_ increment the reference count on the typeface, as the
790  * assumption is that its lifetime is managed elsewhere - in particular, the fallback typefaces
791  * for the default font live in a global cache.
792  */
793 SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
794         hb_script_t script) {
795     SkTypeface::Style currentStyle = SkTypeface::kNormal;
796     if (typeface) {
797         currentStyle = typeface->style();
798     }
799     typeface = SkCreateTypefaceForScriptNG(script, currentStyle);
800 #if DEBUG_GLYPHS
801     ALOGD("Using Harfbuzz Script %d, Style %d", script, currentStyle);
802 #endif
803     return typeface;
804 }
805
806 bool TextLayoutShaper::isComplexScript(hb_script_t script) {
807     switch (script) {
808     case HB_SCRIPT_COMMON:
809     case HB_SCRIPT_GREEK:
810     case HB_SCRIPT_CYRILLIC:
811     case HB_SCRIPT_HANGUL:
812     case HB_SCRIPT_INHERITED:
813     case HB_SCRIPT_HAN:
814     case HB_SCRIPT_KATAKANA:
815     case HB_SCRIPT_HIRAGANA:
816         return false;
817     default:
818         return true;
819     }
820 }
821
822 size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) {
823     // Update Harfbuzz Shaper
824
825     SkTypeface* typeface = paint->getTypeface();
826
827     // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
828     // This is needed as the Typeface used for shaping can be not the default one
829     // when we are shaping any script that needs to use a fallback Font.
830     // If we are a "common" script we dont need to shift
831     size_t baseGlyphCount = 0;
832     hb_codepoint_t firstUnichar = 0;
833     if (isComplexScript(hb_buffer_get_script(mBuffer))) {
834         unsigned int numGlyphs;
835         hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
836         for (size_t i = 0; i < numGlyphs; i++) {
837             firstUnichar = info[i].codepoint;
838             if (firstUnichar != ' ') {
839                 break;
840             }
841         }
842         baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
843     }
844
845     if (baseGlyphCount != 0) {
846         typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer));
847         if (!typeface) {
848             typeface = mDefaultTypeface;
849             SkSafeRef(typeface);
850 #if DEBUG_GLYPHS
851             ALOGD("Using Default Typeface");
852 #endif
853         }
854     } else {
855         if (!typeface) {
856             typeface = mDefaultTypeface;
857 #if DEBUG_GLYPHS
858             ALOGD("Using Default Typeface");
859 #endif
860         }
861         SkSafeRef(typeface);
862     }
863
864     mShapingPaint.setTypeface(typeface);
865     hb_face_t* face = referenceCachedHBFace(typeface);
866
867     float sizeY = paint->getTextSize();
868     float sizeX = sizeY * paint->getTextScaleX();
869     hb_font_t* font = createFont(face, &mShapingPaint, sizeX, sizeY);
870     hb_face_destroy(face);
871
872 #if DEBUG_GLYPHS
873     ALOGD("Run typeface = %p, uniqueID = %d, face = %p",
874             typeface, typeface->uniqueID(), face);
875 #endif
876     SkSafeUnref(typeface);
877
878     hb_shape(font, mBuffer, NULL, 0);
879     hb_font_destroy(font);
880
881     return baseGlyphCount;
882 }
883
884 hb_face_t* TextLayoutShaper::referenceCachedHBFace(SkTypeface* typeface) {
885     SkFontID fontId = typeface->uniqueID();
886     ssize_t index = mCachedHBFaces.indexOfKey(fontId);
887     if (index >= 0) {
888         return hb_face_reference(mCachedHBFaces.valueAt(index));
889     }
890     // TODO: destroy function
891     hb_face_t* face = hb_face_create_for_tables(harfbuzzSkiaReferenceTable, typeface, NULL);
892 #if DEBUG_GLYPHS
893     ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
894 #endif
895     mCachedHBFaces.add(fontId, face);
896     return hb_face_reference(face);
897 }
898
899 void TextLayoutShaper::purgeCaches() {
900     size_t cacheSize = mCachedHBFaces.size();
901     for (size_t i = 0; i < cacheSize; i++) {
902         hb_face_destroy(mCachedHBFaces.valueAt(i));
903     }
904     mCachedHBFaces.clear();
905     unrefTypefaces();
906     init();
907 }
908
909 TextLayoutEngine::TextLayoutEngine() {
910     mShaper = new TextLayoutShaper();
911 #if USE_TEXT_LAYOUT_CACHE
912     mTextLayoutCache = new TextLayoutCache(mShaper);
913 #else
914     mTextLayoutCache = NULL;
915 #endif
916 }
917
918 TextLayoutEngine::~TextLayoutEngine() {
919     delete mTextLayoutCache;
920     delete mShaper;
921 }
922
923 sp<TextLayoutValue> TextLayoutEngine::getValue(const SkPaint* paint, const jchar* text,
924         jint start, jint count, jint contextCount, jint dirFlags) {
925     sp<TextLayoutValue> value;
926 #if USE_TEXT_LAYOUT_CACHE
927     value = mTextLayoutCache->getValue(paint, text, start, count,
928             contextCount, dirFlags);
929     if (value == NULL) {
930         ALOGE("Cannot get TextLayoutCache value for text = '%s'",
931                 String8(text + start, count).string());
932     }
933 #else
934     value = new TextLayoutValue(count);
935     mShaper->computeValues(value.get(), paint,
936             reinterpret_cast<const UChar*>(text), start, count, contextCount, dirFlags);
937 #endif
938     return value;
939 }
940
941 void TextLayoutEngine::purgeCaches() {
942 #if USE_TEXT_LAYOUT_CACHE
943     mTextLayoutCache->purgeCaches();
944 #if DEBUG_GLYPHS
945     ALOGD("Purged TextLayoutEngine caches");
946 #endif
947 #endif
948 }
949
950
951 } // namespace android