blob: 81bf4d5de603d7dba60de99b47d233a755e0427e [file] [log] [blame]
Fabrice Di Megliod313c662011-02-24 19:56:18 -08001/*
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
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070017#define LOG_TAG "TextLayoutCache"
18
Fabrice Di Megliod313c662011-02-24 19:56:18 -080019#include "TextLayoutCache.h"
Fabrice Di Meglio689e5152011-04-13 16:07:37 -070020#include "TextLayout.h"
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -070021#include "SkFontHost.h"
Fabrice Di Megliod313c662011-02-24 19:56:18 -080022
Fabrice Di Meglio5de5b1a2011-08-09 14:37:22 -070023extern "C" {
24 #include "harfbuzz-unicode.h"
25}
26
Fabrice Di Megliod313c662011-02-24 19:56:18 -080027namespace android {
28
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070029//--------------------------------------------------------------------------------------------------
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -070030#define TYPEFACE_ARABIC "/system/fonts/DroidNaskh-Regular.ttf"
31#define TYPE_FACE_HEBREW_REGULAR "/system/fonts/DroidSansHebrew-Regular.ttf"
32#define TYPE_FACE_HEBREW_BOLD "/system/fonts/DroidSansHebrew-Bold.ttf"
33
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070034#if USE_TEXT_LAYOUT_CACHE
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -070035
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070036 ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutCache);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -070037
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070038#endif
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -080039
Fabrice Di Megliob02d0ca2011-12-08 14:05:44 -080040 ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
41
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070042//--------------------------------------------------------------------------------------------------
43
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -070044TextLayoutCache::TextLayoutCache() :
45 mCache(GenerationCache<TextLayoutCacheKey, sp<TextLayoutCacheValue> >::kUnlimitedCapacity),
Fabrice Di Megliod313c662011-02-24 19:56:18 -080046 mSize(0), mMaxSize(MB(DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB)),
47 mCacheHitCount(0), mNanosecondsSaved(0) {
48 init();
49}
50
Fabrice Di Megliod313c662011-02-24 19:56:18 -080051TextLayoutCache::~TextLayoutCache() {
52 mCache.clear();
53}
54
55void TextLayoutCache::init() {
56 mCache.setOnEntryRemovedListener(this);
57
58 mDebugLevel = readRtlDebugLevel();
59 mDebugEnabled = mDebugLevel & kRtlDebugCaches;
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -080060 LOGD("Using debug level = %d - Debug Enabled = %d", mDebugLevel, mDebugEnabled);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080061
62 mCacheStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
Fabrice Di Meglio9f82b582011-03-08 12:02:59 -080063
64 if (mDebugEnabled) {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -080065 LOGD("Initialization is done - Start time = %lld", mCacheStartTime);
Fabrice Di Meglio9f82b582011-03-08 12:02:59 -080066 }
67
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070068 mInitialized = true;
Fabrice Di Megliod313c662011-02-24 19:56:18 -080069}
70
Fabrice Di Megliod313c662011-02-24 19:56:18 -080071/**
72 * Callbacks
73 */
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -070074void TextLayoutCache::operator()(TextLayoutCacheKey& text, sp<TextLayoutCacheValue>& desc) {
Jeff Brown06daa7b2011-11-11 15:14:56 -080075 size_t totalSizeToDelete = text.getSize() + desc->getSize();
76 mSize -= totalSizeToDelete;
77 if (mDebugEnabled) {
78 LOGD("Cache value %p deleted, size = %d", desc.get(), totalSizeToDelete);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080079 }
80}
81
82/*
83 * Cache clearing
84 */
85void TextLayoutCache::clear() {
86 mCache.clear();
87}
88
89/*
90 * Caching
91 */
Fabrice Di Meglio9c418db2011-09-18 12:54:38 -070092sp<TextLayoutCacheValue> TextLayoutCache::getValue(SkPaint* paint,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -070093 const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) {
Fabrice Di Megliod313c662011-02-24 19:56:18 -080094 AutoMutex _l(mLock);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080095 nsecs_t startTime = 0;
96 if (mDebugEnabled) {
97 startTime = systemTime(SYSTEM_TIME_MONOTONIC);
98 }
99
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700100 // Create the key
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700101 TextLayoutCacheKey key(paint, text, start, count, contextCount, dirFlags);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800102
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700103 // Get value from cache if possible
104 sp<TextLayoutCacheValue> value = mCache.get(key);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800105
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700106 // Value not found for the key, we need to add a new value in the cache
107 if (value == NULL) {
Fabrice Di Meglio010d5c42011-04-21 15:33:50 -0700108 if (mDebugEnabled) {
109 startTime = systemTime(SYSTEM_TIME_MONOTONIC);
110 }
111
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800112 value = new TextLayoutCacheValue(contextCount);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800113
114 // Compute advances and store them
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800115 TextLayoutEngine::getInstance().computeValues(value.get(), paint,
116 reinterpret_cast<const UChar*>(text), start, count,
117 size_t(contextCount), int(dirFlags));
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800118
Jeff Brown06daa7b2011-11-11 15:14:56 -0800119 if (mDebugEnabled) {
120 value->setElapsedTime(systemTime(SYSTEM_TIME_MONOTONIC) - startTime);
121 }
Fabrice Di Meglio010d5c42011-04-21 15:33:50 -0700122
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800123 // Don't bother to add in the cache if the entry is too big
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700124 size_t size = key.getSize() + value->getSize();
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800125 if (size <= mMaxSize) {
126 // Cleanup to make some room if needed
127 if (mSize + size > mMaxSize) {
128 if (mDebugEnabled) {
Fabrice Di Meglio163268b2011-09-07 18:12:11 -0700129 LOGD("Need to clean some entries for making some room for a new entry");
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800130 }
131 while (mSize + size > mMaxSize) {
132 // This will call the callback
Jeff Brownd9e688c2011-11-11 15:40:13 -0800133 bool removedOne = mCache.removeOldest();
Jeff Brown06daa7b2011-11-11 15:14:56 -0800134 LOG_ALWAYS_FATAL_IF(!removedOne, "The cache is non-empty but we "
135 "failed to remove the oldest entry. "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800136 "mSize = %u, size = %u, mMaxSize = %u, mCache.size() = %u",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800137 mSize, size, mMaxSize, mCache.size());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800138 }
139 }
140
141 // Update current cache size
142 mSize += size;
143
144 // Copy the text when we insert the new entry
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700145 key.internalTextCopy();
Jeff Brown06daa7b2011-11-11 15:14:56 -0800146
147 bool putOne = mCache.put(key, value);
148 LOG_ALWAYS_FATAL_IF(!putOne, "Failed to put an entry into the cache. "
149 "This indicates that the cache already has an entry with the "
150 "same key but it should not since we checked earlier!"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800151 " - start = %d, count = %d, contextCount = %d - Text = '%s'",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800152 start, count, contextCount, String8(text + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800153
154 if (mDebugEnabled) {
Jeff Brown06daa7b2011-11-11 15:14:56 -0800155 nsecs_t totalTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
156 LOGD("CACHE MISS: Added entry %p "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800157 "with start = %d, count = %d, contextCount = %d, "
Jeff Brown06daa7b2011-11-11 15:14:56 -0800158 "entry size %d bytes, remaining space %d bytes"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800159 " - Compute time %0.6f ms - Put time %0.6f ms - Text = '%s'",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800160 value.get(), start, count, contextCount, size, mMaxSize - mSize,
161 value->getElapsedTime() * 0.000001f,
162 (totalTime - value->getElapsedTime()) * 0.000001f,
163 String8(text + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800164 }
165 } else {
166 if (mDebugEnabled) {
167 LOGD("CACHE MISS: Calculated but not storing entry because it is too big "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800168 "with start = %d, count = %d, contextCount = %d, "
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800169 "entry size %d bytes, remaining space %d bytes"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800170 " - Compute time %0.6f ms - Text = '%s'",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800171 start, count, contextCount, size, mMaxSize - mSize,
172 value->getElapsedTime() * 0.000001f,
173 String8(text + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800174 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700175 value.clear();
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800176 }
177 } else {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700178 // This is a cache hit, just log timestamp and user infos
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800179 if (mDebugEnabled) {
180 nsecs_t elapsedTimeThruCacheGet = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700181 mNanosecondsSaved += (value->getElapsedTime() - elapsedTimeThruCacheGet);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800182 ++mCacheHitCount;
183
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700184 if (value->getElapsedTime() > 0) {
185 float deltaPercent = 100 * ((value->getElapsedTime() - elapsedTimeThruCacheGet)
186 / ((float)value->getElapsedTime()));
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800187 LOGD("CACHE HIT #%d with start = %d, count = %d, contextCount = %d"
Jeff Brown06daa7b2011-11-11 15:14:56 -0800188 "- Compute time %0.6f ms - "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800189 "Cache get time %0.6f ms - Gain in percent: %2.2f - Text = '%s'",
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700190 mCacheHitCount, start, count, contextCount,
Jeff Brown06daa7b2011-11-11 15:14:56 -0800191 value->getElapsedTime() * 0.000001f,
192 elapsedTimeThruCacheGet * 0.000001f,
193 deltaPercent,
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800194 String8(text + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800195 }
196 if (mCacheHitCount % DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL == 0) {
197 dumpCacheStats();
198 }
199 }
200 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700201 return value;
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800202}
203
204void TextLayoutCache::dumpCacheStats() {
205 float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize));
206 float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000;
Jeff Brown06daa7b2011-11-11 15:14:56 -0800207
208 size_t bytes = 0;
209 size_t cacheSize = mCache.size();
210 for (size_t i = 0; i < cacheSize; i++) {
211 bytes += mCache.getKeyAt(i).getSize() + mCache.getValueAt(i)->getSize();
212 }
213
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800214 LOGD("------------------------------------------------");
Fabrice Di Meglio163268b2011-09-07 18:12:11 -0700215 LOGD("Cache stats");
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800216 LOGD("------------------------------------------------");
Fabrice Di Meglio010d5c42011-04-21 15:33:50 -0700217 LOGD("pid : %d", getpid());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800218 LOGD("running : %.0f seconds", timeRunningInSec);
Jeff Brown06daa7b2011-11-11 15:14:56 -0800219 LOGD("entries : %d", cacheSize);
220 LOGD("max size : %d bytes", mMaxSize);
221 LOGD("used : %d bytes according to mSize, %d bytes actual", mSize, bytes);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800222 LOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent);
223 LOGD("hits : %d", mCacheHitCount);
Jeff Brown06daa7b2011-11-11 15:14:56 -0800224 LOGD("saved : %0.6f ms", mNanosecondsSaved * 0.000001f);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800225 LOGD("------------------------------------------------");
226}
227
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700228/**
229 * TextLayoutCacheKey
230 */
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700231TextLayoutCacheKey::TextLayoutCacheKey(): text(NULL), start(0), count(0), contextCount(0),
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700232 dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
233 hinting(SkPaint::kNo_Hinting) {
234}
235
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700236TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text,
237 size_t start, size_t count, size_t contextCount, int dirFlags) :
238 text(text), start(start), count(count), contextCount(contextCount),
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700239 dirFlags(dirFlags) {
240 typeface = paint->getTypeface();
241 textSize = paint->getTextSize();
242 textSkewX = paint->getTextSkewX();
243 textScaleX = paint->getTextScaleX();
244 flags = paint->getFlags();
245 hinting = paint->getHinting();
246}
247
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700248TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) :
249 text(NULL),
250 textCopy(other.textCopy),
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700251 start(other.start),
Jeff Brown7aac2972011-09-19 16:17:58 -0700252 count(other.count),
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700253 contextCount(other.contextCount),
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700254 dirFlags(other.dirFlags),
255 typeface(other.typeface),
256 textSize(other.textSize),
257 textSkewX(other.textSkewX),
258 textScaleX(other.textScaleX),
259 flags(other.flags),
260 hinting(other.hinting) {
261 if (other.text) {
Jeff Brownf1f0c872011-11-11 15:03:05 -0800262 textCopy.setTo(other.text, other.contextCount);
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700263 }
264}
265
Fabrice Di Meglio717060b2011-09-27 15:53:42 -0700266int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700267 int deltaInt = lhs.start - rhs.start;
268 if (deltaInt != 0) return (deltaInt);
269
270 deltaInt = lhs.count - rhs.count;
271 if (deltaInt != 0) return (deltaInt);
272
273 deltaInt = lhs.contextCount - rhs.contextCount;
Fabrice Di Meglio717060b2011-09-27 15:53:42 -0700274 if (deltaInt != 0) return (deltaInt);
275
276 if (lhs.typeface < rhs.typeface) return -1;
277 if (lhs.typeface > rhs.typeface) return +1;
278
279 if (lhs.textSize < rhs.textSize) return -1;
280 if (lhs.textSize > rhs.textSize) return +1;
281
282 if (lhs.textSkewX < rhs.textSkewX) return -1;
283 if (lhs.textSkewX > rhs.textSkewX) return +1;
284
285 if (lhs.textScaleX < rhs.textScaleX) return -1;
286 if (lhs.textScaleX > rhs.textScaleX) return +1;
287
288 deltaInt = lhs.flags - rhs.flags;
289 if (deltaInt != 0) return (deltaInt);
290
291 deltaInt = lhs.hinting - rhs.hinting;
292 if (deltaInt != 0) return (deltaInt);
293
294 deltaInt = lhs.dirFlags - rhs.dirFlags;
295 if (deltaInt) return (deltaInt);
296
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700297 return memcmp(lhs.getText(), rhs.getText(), lhs.contextCount * sizeof(UChar));
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700298}
299
300void TextLayoutCacheKey::internalTextCopy() {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700301 textCopy.setTo(text, contextCount);
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700302 text = NULL;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700303}
304
Jeff Brown06daa7b2011-11-11 15:14:56 -0800305size_t TextLayoutCacheKey::getSize() const {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700306 return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700307}
308
309/**
310 * TextLayoutCacheValue
311 */
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800312TextLayoutCacheValue::TextLayoutCacheValue(size_t contextCount) :
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700313 mTotalAdvance(0), mElapsedTime(0) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800314 // Give a hint for advances and glyphs vectors size
315 mAdvances.setCapacity(contextCount);
316 mGlyphs.setCapacity(contextCount);
317}
318
319size_t TextLayoutCacheValue::getSize() const {
320 return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * mAdvances.capacity() +
321 sizeof(jchar) * mGlyphs.capacity();
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700322}
323
324void TextLayoutCacheValue::setElapsedTime(uint32_t time) {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700325 mElapsedTime = time;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700326}
327
328uint32_t TextLayoutCacheValue::getElapsedTime() {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700329 return mElapsedTime;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700330}
331
Jeff Brown738ef872011-12-05 17:19:49 -0800332TextLayoutEngine::TextLayoutEngine() : mShaperItemGlyphArraySize(0) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800333 mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, SkTypeface::kNormal);
334 mArabicTypeface = NULL;
335 mHebrewRegularTypeface = NULL;
336 mHebrewBoldTypeface = NULL;
337
338 mFontRec.klass = &harfbuzzSkiaClass;
339 mFontRec.userData = 0;
340
341 // The values which harfbuzzSkiaClass returns are already scaled to
342 // pixel units, so we just set all these to one to disable further
343 // scaling.
344 mFontRec.x_ppem = 1;
345 mFontRec.y_ppem = 1;
346 mFontRec.x_scale = 1;
347 mFontRec.y_scale = 1;
348
349 memset(&mShaperItem, 0, sizeof(mShaperItem));
350
351 mShaperItem.font = &mFontRec;
352 mShaperItem.font->userData = &mShapingPaint;
353}
354
355TextLayoutEngine::~TextLayoutEngine() {
356 // FIXME should free fonts and caches but since this class is a singleton,
357 // we don't bother at the moment
358}
359
360void TextLayoutEngine::computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700361 size_t start, size_t count, size_t contextCount, int dirFlags) {
Fabrice Di Meglio79df5322011-09-19 15:17:56 -0700362
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800363 computeValues(paint, chars, start, count, contextCount, dirFlags,
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800364 &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700365#if DEBUG_ADVANCES
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800366 LOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800367 contextCount, value->mTotalAdvance);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700368#endif
369}
370
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800371void TextLayoutEngine::computeValues(SkPaint* paint, const UChar* chars,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700372 size_t start, size_t count, size_t contextCount, int dirFlags,
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700373 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700374 Vector<jchar>* const outGlyphs) {
Jeff Browna03bded2011-12-05 17:36:16 -0800375 if (!count) {
376 *outTotalAdvance = 0;
377 return;
378 }
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700379
380 UBiDiLevel bidiReq = 0;
381 bool forceLTR = false;
382 bool forceRTL = false;
383
384 switch (dirFlags) {
385 case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
386 case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
387 case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
388 case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
389 case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR
390 case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
391 }
392
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700393 bool useSingleRun = false;
394 bool isRTL = forceRTL;
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700395 if (forceLTR || forceRTL) {
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700396 useSingleRun = true;
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700397 } else {
398 UBiDi* bidi = ubidi_open();
399 if (bidi) {
400 UErrorCode status = U_ZERO_ERROR;
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700401#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800402 LOGD("******** ComputeValues -- start");
403 LOGD(" -- string = '%s'", String8(chars + start, count).string());
404 LOGD(" -- start = %d", start);
405 LOGD(" -- count = %d", count);
406 LOGD(" -- contextCount = %d", contextCount);
407 LOGD(" -- bidiReq = %d", bidiReq);
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700408#endif
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700409 ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
410 if (U_SUCCESS(status)) {
411 int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700412 ssize_t rc = ubidi_countRuns(bidi, &status);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700413#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800414 LOGD(" -- dirFlags = %d", dirFlags);
415 LOGD(" -- paraDir = %d", paraDir);
Jeff Brown738ef872011-12-05 17:19:49 -0800416 LOGD(" -- run-count = %d", int(rc));
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700417#endif
Fabrice Di Megliobcf05a62011-11-01 17:53:34 -0700418 if (U_SUCCESS(status) && rc == 1) {
419 // Normal case: one run, status is ok
420 isRTL = (paraDir == 1);
421 useSingleRun = true;
422 } else if (!U_SUCCESS(status) || rc < 1) {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800423 LOGW("Need to force to single run -- string = '%s',"
424 " status = %d, rc = %d",
Jeff Brown738ef872011-12-05 17:19:49 -0800425 String8(chars + start, count).string(), status, int(rc));
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700426 isRTL = (paraDir == 1);
427 useSingleRun = true;
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700428 } else {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700429 int32_t end = start + count;
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700430 for (size_t i = 0; i < size_t(rc); ++i) {
431 int32_t startRun = -1;
432 int32_t lengthRun = -1;
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700433 UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700434
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700435 if (startRun == -1 || lengthRun == -1) {
436 // Something went wrong when getting the visual run, need to clear
437 // already computed data before doing a single run pass
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800438 LOGW("Visual run is not valid");
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700439 outGlyphs->clear();
440 outAdvances->clear();
441 *outTotalAdvance = 0;
442 isRTL = (paraDir == 1);
443 useSingleRun = true;
444 break;
445 }
446
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700447 if (startRun >= end) {
448 continue;
449 }
450 int32_t endRun = startRun + lengthRun;
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700451 if (endRun <= int32_t(start)) {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700452 continue;
453 }
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700454 if (startRun < int32_t(start)) {
455 startRun = int32_t(start);
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700456 }
457 if (endRun > end) {
458 endRun = end;
459 }
460
461 lengthRun = endRun - startRun;
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700462 isRTL = (runDir == UBIDI_RTL);
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700463 jfloat runTotalAdvance = 0;
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700464#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800465 LOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
466 i, startRun, lengthRun, isRTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700467#endif
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800468 computeRunValues(paint, chars + startRun, lengthRun, isRTL,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700469 outAdvances, &runTotalAdvance, outGlyphs);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700470
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700471 *outTotalAdvance += runTotalAdvance;
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700472 }
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700473 }
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700474 } else {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800475 LOGW("Cannot set Para");
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700476 useSingleRun = true;
477 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700478 }
479 ubidi_close(bidi);
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700480 } else {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800481 LOGW("Cannot ubidi_open()");
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700482 useSingleRun = true;
483 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700484 }
485 }
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700486
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700487 // Default single run case
488 if (useSingleRun){
489#if DEBUG_GLYPHS
Fabrice Di Meglio2e5e96e2011-11-30 20:08:16 -0800490 LOGD("Using a SINGLE BiDi Run "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800491 "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700492#endif
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800493 computeRunValues(paint, chars + start, count, isRTL,
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700494 outAdvances, outTotalAdvance, outGlyphs);
495 }
496
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700497#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800498 LOGD(" -- Total returned glyphs-count = %d", outGlyphs->size());
499 LOGD("******** ComputeValues -- end");
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700500#endif
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700501}
502
Fabrice Di Meglioabb0f292011-08-18 17:59:29 -0700503static void logGlyphs(HB_ShaperItem shaperItem) {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800504 LOGD(" -- glyphs count=%d", shaperItem.num_glyphs);
Fabrice Di Meglioabb0f292011-08-18 17:59:29 -0700505 for (size_t i = 0; i < shaperItem.num_glyphs; i++) {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800506 LOGD(" -- glyph[%d] = %d, offset.x = %f, offset.y = %f", i, shaperItem.glyphs[i],
Fabrice Di Meglioabb0f292011-08-18 17:59:29 -0700507 HBFixedToFloat(shaperItem.offsets[i].x),
508 HBFixedToFloat(shaperItem.offsets[i].y));
509 }
510}
511
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800512void TextLayoutEngine::computeRunValues(SkPaint* paint, const UChar* chars,
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700513 size_t count, bool isRTL,
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700514 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700515 Vector<jchar>* const outGlyphs) {
Jeff Browna03bded2011-12-05 17:36:16 -0800516 if (!count) {
517 // We cannot shape an empty run.
518 *outTotalAdvance = 0;
519 return;
520 }
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700521
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800522 // Set the string properties
523 mShaperItem.string = chars;
524 mShaperItem.stringLength = count;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700525
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800526 // Define shaping paint properties
527 mShapingPaint.setTextSize(paint->getTextSize());
528 mShapingPaint.setTextSkewX(paint->getTextSkewX());
529 mShapingPaint.setTextScaleX(paint->getTextScaleX());
530 mShapingPaint.setFlags(paint->getFlags());
531 mShapingPaint.setHinting(paint->getHinting());
532
533 // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
534 // into the shaperItem
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700535 ssize_t indexFontRun = isRTL ? count - 1 : 0;
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800536 unsigned numCodePoints = 0;
Jeff Browna03bded2011-12-05 17:36:16 -0800537 jfloat totalAdvance = 0;
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800538 while ((isRTL) ?
539 hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, chars,
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700540 count, &indexFontRun):
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800541 hb_utf16_script_run_next(&numCodePoints, &mShaperItem.item, chars,
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700542 count, &indexFontRun)) {
543
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800544 ssize_t startScriptRun = mShaperItem.item.pos;
545 size_t countScriptRun = mShaperItem.item.length;
546 ssize_t endScriptRun = startScriptRun + countScriptRun;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700547
Fabrice Di Meglio9c418db2011-09-18 12:54:38 -0700548#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800549 LOGD("-------- Start of Script Run --------");
550 LOGD("Shaping Script Run with");
551 LOGD(" -- isRTL = %d", isRTL);
552 LOGD(" -- HB script = %d", mShaperItem.item.script);
Fabrice Di Megliob02d0ca2011-12-08 14:05:44 -0800553 LOGD(" -- startFontRun = %d", int(startScriptRun));
554 LOGD(" -- endFontRun = %d", int(endScriptRun));
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800555 LOGD(" -- countFontRun = %d", countScriptRun);
556 LOGD(" -- run = '%s'", String8(chars + startScriptRun, countScriptRun).string());
557 LOGD(" -- string = '%s'", String8(chars, count).string());
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700558#endif
559
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800560 // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
561 // and shape the Font run
562 size_t glyphBaseCount = shapeFontRun(paint, isRTL);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700563
Fabrice Di Meglio78b868f2011-05-17 19:48:57 -0700564#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800565 LOGD("Got from Harfbuzz");
566 LOGD(" -- glyphBaseCount = %d", glyphBaseCount);
567 LOGD(" -- num_glypth = %d", mShaperItem.num_glyphs);
568 LOGD(" -- kerning_applied = %d", mShaperItem.kerning_applied);
569 LOGD(" -- isDevKernText = %d", paint->isDevKernText());
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700570
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800571 logGlyphs(mShaperItem);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700572#endif
573 if (isRTL) {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800574 endScriptRun = startScriptRun;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700575#if DEBUG_GLYPHS
Fabrice Di Megliob02d0ca2011-12-08 14:05:44 -0800576 LOGD("Updated endScriptRun = %d", int(endScriptRun));
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700577#endif
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700578 } else {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800579 startScriptRun = endScriptRun;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700580#if DEBUG_GLYPHS
Fabrice Di Megliob02d0ca2011-12-08 14:05:44 -0800581 LOGD("Updated startScriptRun = %d", int(startScriptRun));
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700582#endif
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700583 }
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700584
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800585 if (mShaperItem.advances == NULL || mShaperItem.num_glyphs == 0) {
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700586#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800587 LOGD("Advances array is empty or num_glypth = 0");
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700588#endif
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800589 outAdvances->insertAt(0, outAdvances->size(), countScriptRun);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700590 continue;
591 }
592
593 // Get Advances and their total
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800594 jfloat currentAdvance = HBFixedToFloat(mShaperItem.advances[mShaperItem.log_clusters[0]]);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700595 jfloat totalFontRunAdvance = currentAdvance;
596 outAdvances->add(currentAdvance);
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800597 for (size_t i = 1; i < countScriptRun; i++) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800598 size_t clusterPrevious = mShaperItem.log_clusters[i - 1];
599 size_t cluster = mShaperItem.log_clusters[i];
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700600 if (cluster == clusterPrevious) {
601 outAdvances->add(0);
602 } else {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800603 currentAdvance = HBFixedToFloat(mShaperItem.advances[mShaperItem.log_clusters[i]]);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700604 totalFontRunAdvance += currentAdvance;
605 outAdvances->add(currentAdvance);
606 }
607 }
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800608
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700609 totalAdvance += totalFontRunAdvance;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700610
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700611#if DEBUG_ADVANCES
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800612 LOGD("Returned advances");
613 for (size_t i = 0; i < countScriptRun; i++) {
614 LOGD(" -- hb-adv[%d] = %f, log_clusters = %d, total = %f", i,
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800615 (*outAdvances)[i], mShaperItem.log_clusters[i], totalFontRunAdvance);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700616 }
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700617#endif
618
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700619 // Get Glyphs and reverse them in place if RTL
620 if (outGlyphs) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800621 size_t countGlyphs = mShaperItem.num_glyphs;
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800622#if DEBUG_GLYPHS
623 LOGD("Returned script run glyphs -- count = %d", countGlyphs);
624#endif
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700625 for (size_t i = 0; i < countGlyphs; i++) {
626 jchar glyph = glyphBaseCount +
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800627 (jchar) mShaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700628#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800629 LOGD(" -- glyph[%d] = %d", i, glyph);
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700630#endif
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700631 outGlyphs->add(glyph);
632 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700633 }
634 }
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800635
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700636 *outTotalAdvance = totalAdvance;
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800637
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800638#if DEBUG_GLYPHS
639 LOGD("-------- End of Script Run --------");
640#endif
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700641}
642
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800643
644size_t TextLayoutEngine::shapeFontRun(SkPaint* paint, bool isRTL) {
645 // Reset kerning
646 mShaperItem.kerning_applied = false;
647
648 // Update Harfbuzz Shaper
649 mShaperItem.item.bidiLevel = isRTL;
650
651 SkTypeface* typeface = paint->getTypeface();
652
653 // Set the correct Typeface depending on the script
654 switch (mShaperItem.item.script) {
655 case HB_Script_Arabic:
656 typeface = getCachedTypeface(&mArabicTypeface, TYPEFACE_ARABIC);
657#if DEBUG_GLYPHS
658 LOGD("Using Arabic Typeface");
659#endif
660 break;
661
662 case HB_Script_Hebrew:
663 if (typeface) {
664 switch (typeface->style()) {
665 case SkTypeface::kBold:
666 case SkTypeface::kBoldItalic:
667 typeface = getCachedTypeface(&mHebrewBoldTypeface, TYPE_FACE_HEBREW_BOLD);
668#if DEBUG_GLYPHS
669 LOGD("Using Hebrew Bold/BoldItalic Typeface");
670#endif
671 break;
672
673 case SkTypeface::kNormal:
674 case SkTypeface::kItalic:
675 default:
676 typeface = getCachedTypeface(&mHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
677#if DEBUG_GLYPHS
678 LOGD("Using Hebrew Regular/Italic Typeface");
679#endif
680 break;
681 }
682 } else {
683 typeface = getCachedTypeface(&mHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
684#if DEBUG_GLYPHS
685 LOGD("Using Hebrew Regular Typeface");
686#endif
687 }
688 break;
689
690 default:
691 if (!typeface) {
692 typeface = mDefaultTypeface;
693#if DEBUG_GLYPHS
694 LOGD("Using Default Typeface");
695#endif
696 } else {
697#if DEBUG_GLYPHS
698 LOGD("Using Paint Typeface");
699#endif
700 }
701 break;
702 }
703
704 mShapingPaint.setTypeface(typeface);
705 mShaperItem.face = getCachedHBFace(typeface);
706
707#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800708 LOGD("Run typeface = %p, uniqueID = %d, hb_face = %p",
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800709 typeface, typeface->uniqueID(), mShaperItem.face);
710#endif
711
712 // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
713 // This is needed as the Typeface used for shaping can be not the default one
714 // when we are shaping any script that needs to use a fallback Font.
715 // If we are a "common" script we dont need to shift
716 size_t baseGlyphCount = 0;
717 switch (mShaperItem.item.script) {
718 case HB_Script_Arabic:
719 case HB_Script_Hebrew: {
720 const uint16_t* text16 = (const uint16_t*)mShaperItem.string;
721 SkUnichar firstUnichar = SkUTF16_NextUnichar(&text16);
722 baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
723 break;
724 }
725 default:
726 break;
727 }
728
729 // Shape
Jeff Browna03bded2011-12-05 17:36:16 -0800730 assert(mShaperItem.item.length > 0); // Harfbuzz will overwrite other memory if length is 0.
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800731 ensureShaperItemGlyphArrays(mShaperItem.item.length * 3 / 2);
732 mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
733 while (!HB_ShapeItem(&mShaperItem)) {
734 // We overflowed our glyph arrays. Resize and retry.
735 // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
736 ensureShaperItemGlyphArrays(mShaperItem.num_glyphs * 2);
737 mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
738 }
739 return baseGlyphCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700740}
741
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800742void TextLayoutEngine::ensureShaperItemGlyphArrays(size_t size) {
743 if (size > mShaperItemGlyphArraySize) {
744 deleteShaperItemGlyphArrays();
745 createShaperItemGlyphArrays(size);
746 }
747}
748
749void TextLayoutEngine::createShaperItemGlyphArrays(size_t size) {
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700750#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800751 LOGD("Creating Glyph Arrays with size = %d", size);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700752#endif
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800753 mShaperItemGlyphArraySize = size;
Jeff Brown738ef872011-12-05 17:19:49 -0800754
755 // These arrays are all indexed by glyph.
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800756 mShaperItem.glyphs = new HB_Glyph[size];
757 mShaperItem.attributes = new HB_GlyphAttributes[size];
758 mShaperItem.advances = new HB_Fixed[size];
759 mShaperItem.offsets = new HB_FixedPoint[size];
Jeff Brown738ef872011-12-05 17:19:49 -0800760
761 // Although the log_clusters array is indexed by character, Harfbuzz expects that
762 // it is big enough to hold one element per glyph. So we allocate log_clusters along
763 // with the other glyph arrays above.
764 mShaperItem.log_clusters = new unsigned short[size];
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800765}
766
767void TextLayoutEngine::deleteShaperItemGlyphArrays() {
768 delete[] mShaperItem.glyphs;
769 delete[] mShaperItem.attributes;
770 delete[] mShaperItem.advances;
771 delete[] mShaperItem.offsets;
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800772 delete[] mShaperItem.log_clusters;
773}
774
775SkTypeface* TextLayoutEngine::getCachedTypeface(SkTypeface** typeface, const char path[]) {
776 if (!*typeface) {
777 *typeface = SkTypeface::CreateFromFile(path);
778 (*typeface)->ref();
779#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800780 LOGD("Created SkTypeface from file '%s' with uniqueID = %d", path, (*typeface)->uniqueID());
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800781#endif
782 }
783 return *typeface;
784}
785
786HB_Face TextLayoutEngine::getCachedHBFace(SkTypeface* typeface) {
787 SkFontID fontId = typeface->uniqueID();
788 ssize_t index = mCachedHBFaces.indexOfKey(fontId);
789 if (index >= 0) {
790 return mCachedHBFaces.valueAt(index);
791 }
792 HB_Face face = HB_NewFace(typeface, harfbuzzSkiaGetTable);
793 if (face) {
794#if DEBUG_GLYPHS
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800795 LOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800796#endif
797 mCachedHBFaces.add(fontId, face);
798 }
799 return face;
Fabrice Di Meglio79df5322011-09-19 15:17:56 -0700800}
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700801
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800802} // namespace android