blob: 75251c9910ef94df87efc01602beda0a90d98292 [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 Meglio902a5b32011-12-08 18:59:14 -080022#include <unicode/unistr.h>
23#include <unicode/normlzr.h>
Fabrice Di Megliod313c662011-02-24 19:56:18 -080024
Fabrice Di Meglio5de5b1a2011-08-09 14:37:22 -070025extern "C" {
26 #include "harfbuzz-unicode.h"
27}
28
Fabrice Di Megliod313c662011-02-24 19:56:18 -080029namespace android {
30
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070031//--------------------------------------------------------------------------------------------------
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -070032#define TYPEFACE_ARABIC "/system/fonts/DroidNaskh-Regular.ttf"
33#define TYPE_FACE_HEBREW_REGULAR "/system/fonts/DroidSansHebrew-Regular.ttf"
34#define TYPE_FACE_HEBREW_BOLD "/system/fonts/DroidSansHebrew-Bold.ttf"
Fabrice Di Meglio65194ad2011-12-13 16:26:08 -080035#define TYPEFACE_BENGALI "/system/fonts/Lohit-Bengali.ttf"
Fabrice Di Meglioa4d07702011-12-13 18:52:13 -080036#define TYPEFACE_THAI "/system/fonts/DroidSansThai.ttf"
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -070037
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070038#if USE_TEXT_LAYOUT_CACHE
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -070039
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070040 ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutCache);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -070041
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070042#endif
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -080043
Fabrice Di Megliob02d0ca2011-12-08 14:05:44 -080044 ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
45
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070046//--------------------------------------------------------------------------------------------------
47
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -070048TextLayoutCache::TextLayoutCache() :
49 mCache(GenerationCache<TextLayoutCacheKey, sp<TextLayoutCacheValue> >::kUnlimitedCapacity),
Fabrice Di Megliod313c662011-02-24 19:56:18 -080050 mSize(0), mMaxSize(MB(DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB)),
51 mCacheHitCount(0), mNanosecondsSaved(0) {
52 init();
53}
54
Fabrice Di Megliod313c662011-02-24 19:56:18 -080055TextLayoutCache::~TextLayoutCache() {
56 mCache.clear();
57}
58
59void TextLayoutCache::init() {
60 mCache.setOnEntryRemovedListener(this);
61
62 mDebugLevel = readRtlDebugLevel();
63 mDebugEnabled = mDebugLevel & kRtlDebugCaches;
Steve Block5baa3a62011-12-20 16:23:08 +000064 ALOGD("Using debug level = %d - Debug Enabled = %d", mDebugLevel, mDebugEnabled);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080065
66 mCacheStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
Fabrice Di Meglio9f82b582011-03-08 12:02:59 -080067
68 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +000069 ALOGD("Initialization is done - Start time = %lld", mCacheStartTime);
Fabrice Di Meglio9f82b582011-03-08 12:02:59 -080070 }
71
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070072 mInitialized = true;
Fabrice Di Megliod313c662011-02-24 19:56:18 -080073}
74
Fabrice Di Megliod313c662011-02-24 19:56:18 -080075/**
76 * Callbacks
77 */
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -070078void TextLayoutCache::operator()(TextLayoutCacheKey& text, sp<TextLayoutCacheValue>& desc) {
Jeff Brown06daa7b2011-11-11 15:14:56 -080079 size_t totalSizeToDelete = text.getSize() + desc->getSize();
80 mSize -= totalSizeToDelete;
81 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +000082 ALOGD("Cache value %p deleted, size = %d", desc.get(), totalSizeToDelete);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080083 }
84}
85
86/*
87 * Cache clearing
88 */
89void TextLayoutCache::clear() {
90 mCache.clear();
91}
92
93/*
94 * Caching
95 */
Fabrice Di Meglio9c418db2011-09-18 12:54:38 -070096sp<TextLayoutCacheValue> TextLayoutCache::getValue(SkPaint* paint,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -070097 const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) {
Fabrice Di Megliod313c662011-02-24 19:56:18 -080098 AutoMutex _l(mLock);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080099 nsecs_t startTime = 0;
100 if (mDebugEnabled) {
101 startTime = systemTime(SYSTEM_TIME_MONOTONIC);
102 }
103
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700104 // Create the key
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700105 TextLayoutCacheKey key(paint, text, start, count, contextCount, dirFlags);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800106
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700107 // Get value from cache if possible
108 sp<TextLayoutCacheValue> value = mCache.get(key);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800109
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700110 // Value not found for the key, we need to add a new value in the cache
111 if (value == NULL) {
Fabrice Di Meglio010d5c42011-04-21 15:33:50 -0700112 if (mDebugEnabled) {
113 startTime = systemTime(SYSTEM_TIME_MONOTONIC);
114 }
115
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800116 value = new TextLayoutCacheValue(contextCount);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800117
118 // Compute advances and store them
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800119 TextLayoutEngine::getInstance().computeValues(value.get(), paint,
120 reinterpret_cast<const UChar*>(text), start, count,
121 size_t(contextCount), int(dirFlags));
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800122
Jeff Brown06daa7b2011-11-11 15:14:56 -0800123 if (mDebugEnabled) {
124 value->setElapsedTime(systemTime(SYSTEM_TIME_MONOTONIC) - startTime);
125 }
Fabrice Di Meglio010d5c42011-04-21 15:33:50 -0700126
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800127 // Don't bother to add in the cache if the entry is too big
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700128 size_t size = key.getSize() + value->getSize();
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800129 if (size <= mMaxSize) {
130 // Cleanup to make some room if needed
131 if (mSize + size > mMaxSize) {
132 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000133 ALOGD("Need to clean some entries for making some room for a new entry");
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800134 }
135 while (mSize + size > mMaxSize) {
136 // This will call the callback
Jeff Brownd9e688c2011-11-11 15:40:13 -0800137 bool removedOne = mCache.removeOldest();
Jeff Brown06daa7b2011-11-11 15:14:56 -0800138 LOG_ALWAYS_FATAL_IF(!removedOne, "The cache is non-empty but we "
139 "failed to remove the oldest entry. "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800140 "mSize = %u, size = %u, mMaxSize = %u, mCache.size() = %u",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800141 mSize, size, mMaxSize, mCache.size());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800142 }
143 }
144
145 // Update current cache size
146 mSize += size;
147
148 // Copy the text when we insert the new entry
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700149 key.internalTextCopy();
Jeff Brown06daa7b2011-11-11 15:14:56 -0800150
151 bool putOne = mCache.put(key, value);
152 LOG_ALWAYS_FATAL_IF(!putOne, "Failed to put an entry into the cache. "
153 "This indicates that the cache already has an entry with the "
154 "same key but it should not since we checked earlier!"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800155 " - start = %d, count = %d, contextCount = %d - Text = '%s'",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800156 start, count, contextCount, String8(text + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800157
158 if (mDebugEnabled) {
Jeff Brown06daa7b2011-11-11 15:14:56 -0800159 nsecs_t totalTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
Steve Block5baa3a62011-12-20 16:23:08 +0000160 ALOGD("CACHE MISS: Added entry %p "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800161 "with start = %d, count = %d, contextCount = %d, "
Jeff Brown06daa7b2011-11-11 15:14:56 -0800162 "entry size %d bytes, remaining space %d bytes"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800163 " - Compute time %0.6f ms - Put time %0.6f ms - Text = '%s'",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800164 value.get(), start, count, contextCount, size, mMaxSize - mSize,
165 value->getElapsedTime() * 0.000001f,
166 (totalTime - value->getElapsedTime()) * 0.000001f,
167 String8(text + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800168 }
169 } else {
170 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000171 ALOGD("CACHE MISS: Calculated but not storing entry because it is too big "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800172 "with start = %d, count = %d, contextCount = %d, "
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800173 "entry size %d bytes, remaining space %d bytes"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800174 " - Compute time %0.6f ms - Text = '%s'",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800175 start, count, contextCount, size, mMaxSize - mSize,
176 value->getElapsedTime() * 0.000001f,
177 String8(text + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800178 }
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800179 }
180 } else {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700181 // This is a cache hit, just log timestamp and user infos
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800182 if (mDebugEnabled) {
183 nsecs_t elapsedTimeThruCacheGet = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700184 mNanosecondsSaved += (value->getElapsedTime() - elapsedTimeThruCacheGet);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800185 ++mCacheHitCount;
186
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700187 if (value->getElapsedTime() > 0) {
188 float deltaPercent = 100 * ((value->getElapsedTime() - elapsedTimeThruCacheGet)
189 / ((float)value->getElapsedTime()));
Steve Block5baa3a62011-12-20 16:23:08 +0000190 ALOGD("CACHE HIT #%d with start = %d, count = %d, contextCount = %d"
Jeff Brown06daa7b2011-11-11 15:14:56 -0800191 "- Compute time %0.6f ms - "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800192 "Cache get time %0.6f ms - Gain in percent: %2.2f - Text = '%s'",
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700193 mCacheHitCount, start, count, contextCount,
Jeff Brown06daa7b2011-11-11 15:14:56 -0800194 value->getElapsedTime() * 0.000001f,
195 elapsedTimeThruCacheGet * 0.000001f,
196 deltaPercent,
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800197 String8(text + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800198 }
199 if (mCacheHitCount % DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL == 0) {
200 dumpCacheStats();
201 }
202 }
203 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700204 return value;
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800205}
206
207void TextLayoutCache::dumpCacheStats() {
208 float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize));
209 float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000;
Jeff Brown06daa7b2011-11-11 15:14:56 -0800210
211 size_t bytes = 0;
212 size_t cacheSize = mCache.size();
213 for (size_t i = 0; i < cacheSize; i++) {
214 bytes += mCache.getKeyAt(i).getSize() + mCache.getValueAt(i)->getSize();
215 }
216
Steve Block5baa3a62011-12-20 16:23:08 +0000217 ALOGD("------------------------------------------------");
218 ALOGD("Cache stats");
219 ALOGD("------------------------------------------------");
220 ALOGD("pid : %d", getpid());
221 ALOGD("running : %.0f seconds", timeRunningInSec);
222 ALOGD("entries : %d", cacheSize);
223 ALOGD("max size : %d bytes", mMaxSize);
224 ALOGD("used : %d bytes according to mSize, %d bytes actual", mSize, bytes);
225 ALOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent);
226 ALOGD("hits : %d", mCacheHitCount);
227 ALOGD("saved : %0.6f ms", mNanosecondsSaved * 0.000001f);
228 ALOGD("------------------------------------------------");
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800229}
230
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700231/**
232 * TextLayoutCacheKey
233 */
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700234TextLayoutCacheKey::TextLayoutCacheKey(): text(NULL), start(0), count(0), contextCount(0),
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700235 dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
236 hinting(SkPaint::kNo_Hinting) {
237}
238
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700239TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text,
240 size_t start, size_t count, size_t contextCount, int dirFlags) :
241 text(text), start(start), count(count), contextCount(contextCount),
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700242 dirFlags(dirFlags) {
243 typeface = paint->getTypeface();
244 textSize = paint->getTextSize();
245 textSkewX = paint->getTextSkewX();
246 textScaleX = paint->getTextScaleX();
247 flags = paint->getFlags();
248 hinting = paint->getHinting();
249}
250
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700251TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) :
252 text(NULL),
253 textCopy(other.textCopy),
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700254 start(other.start),
Jeff Brown7aac2972011-09-19 16:17:58 -0700255 count(other.count),
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700256 contextCount(other.contextCount),
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700257 dirFlags(other.dirFlags),
258 typeface(other.typeface),
259 textSize(other.textSize),
260 textSkewX(other.textSkewX),
261 textScaleX(other.textScaleX),
262 flags(other.flags),
263 hinting(other.hinting) {
264 if (other.text) {
Jeff Brownf1f0c872011-11-11 15:03:05 -0800265 textCopy.setTo(other.text, other.contextCount);
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700266 }
267}
268
Fabrice Di Meglio717060b2011-09-27 15:53:42 -0700269int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700270 int deltaInt = lhs.start - rhs.start;
271 if (deltaInt != 0) return (deltaInt);
272
273 deltaInt = lhs.count - rhs.count;
274 if (deltaInt != 0) return (deltaInt);
275
276 deltaInt = lhs.contextCount - rhs.contextCount;
Fabrice Di Meglio717060b2011-09-27 15:53:42 -0700277 if (deltaInt != 0) return (deltaInt);
278
279 if (lhs.typeface < rhs.typeface) return -1;
280 if (lhs.typeface > rhs.typeface) return +1;
281
282 if (lhs.textSize < rhs.textSize) return -1;
283 if (lhs.textSize > rhs.textSize) return +1;
284
285 if (lhs.textSkewX < rhs.textSkewX) return -1;
286 if (lhs.textSkewX > rhs.textSkewX) return +1;
287
288 if (lhs.textScaleX < rhs.textScaleX) return -1;
289 if (lhs.textScaleX > rhs.textScaleX) return +1;
290
291 deltaInt = lhs.flags - rhs.flags;
292 if (deltaInt != 0) return (deltaInt);
293
294 deltaInt = lhs.hinting - rhs.hinting;
295 if (deltaInt != 0) return (deltaInt);
296
297 deltaInt = lhs.dirFlags - rhs.dirFlags;
298 if (deltaInt) return (deltaInt);
299
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700300 return memcmp(lhs.getText(), rhs.getText(), lhs.contextCount * sizeof(UChar));
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700301}
302
303void TextLayoutCacheKey::internalTextCopy() {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700304 textCopy.setTo(text, contextCount);
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700305 text = NULL;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700306}
307
Jeff Brown06daa7b2011-11-11 15:14:56 -0800308size_t TextLayoutCacheKey::getSize() const {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700309 return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700310}
311
312/**
313 * TextLayoutCacheValue
314 */
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800315TextLayoutCacheValue::TextLayoutCacheValue(size_t contextCount) :
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700316 mTotalAdvance(0), mElapsedTime(0) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800317 // Give a hint for advances and glyphs vectors size
318 mAdvances.setCapacity(contextCount);
319 mGlyphs.setCapacity(contextCount);
320}
321
322size_t TextLayoutCacheValue::getSize() const {
323 return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * mAdvances.capacity() +
324 sizeof(jchar) * mGlyphs.capacity();
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700325}
326
327void TextLayoutCacheValue::setElapsedTime(uint32_t time) {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700328 mElapsedTime = time;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700329}
330
331uint32_t TextLayoutCacheValue::getElapsedTime() {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700332 return mElapsedTime;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700333}
334
Jeff Brown738ef872011-12-05 17:19:49 -0800335TextLayoutEngine::TextLayoutEngine() : mShaperItemGlyphArraySize(0) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800336 mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, SkTypeface::kNormal);
337 mArabicTypeface = NULL;
338 mHebrewRegularTypeface = NULL;
339 mHebrewBoldTypeface = NULL;
340
341 mFontRec.klass = &harfbuzzSkiaClass;
342 mFontRec.userData = 0;
343
344 // The values which harfbuzzSkiaClass returns are already scaled to
345 // pixel units, so we just set all these to one to disable further
346 // scaling.
347 mFontRec.x_ppem = 1;
348 mFontRec.y_ppem = 1;
349 mFontRec.x_scale = 1;
350 mFontRec.y_scale = 1;
351
352 memset(&mShaperItem, 0, sizeof(mShaperItem));
353
354 mShaperItem.font = &mFontRec;
355 mShaperItem.font->userData = &mShapingPaint;
356}
357
358TextLayoutEngine::~TextLayoutEngine() {
359 // FIXME should free fonts and caches but since this class is a singleton,
360 // we don't bother at the moment
361}
362
363void TextLayoutEngine::computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700364 size_t start, size_t count, size_t contextCount, int dirFlags) {
Fabrice Di Meglio79df5322011-09-19 15:17:56 -0700365
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800366 computeValues(paint, chars, start, count, contextCount, dirFlags,
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800367 &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700368#if DEBUG_ADVANCES
Steve Block5baa3a62011-12-20 16:23:08 +0000369 ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800370 contextCount, value->mTotalAdvance);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700371#endif
372}
373
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800374void TextLayoutEngine::computeValues(SkPaint* paint, const UChar* chars,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700375 size_t start, size_t count, size_t contextCount, int dirFlags,
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700376 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700377 Vector<jchar>* const outGlyphs) {
Jeff Browna03bded2011-12-05 17:36:16 -0800378 if (!count) {
379 *outTotalAdvance = 0;
380 return;
381 }
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700382
383 UBiDiLevel bidiReq = 0;
384 bool forceLTR = false;
385 bool forceRTL = false;
386
387 switch (dirFlags) {
388 case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
389 case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
390 case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
391 case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
392 case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR
393 case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
394 }
395
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700396 bool useSingleRun = false;
397 bool isRTL = forceRTL;
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700398 if (forceLTR || forceRTL) {
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700399 useSingleRun = true;
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700400 } else {
401 UBiDi* bidi = ubidi_open();
402 if (bidi) {
403 UErrorCode status = U_ZERO_ERROR;
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700404#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000405 ALOGD("******** ComputeValues -- start");
406 ALOGD(" -- string = '%s'", String8(chars + start, count).string());
407 ALOGD(" -- start = %d", start);
408 ALOGD(" -- count = %d", count);
409 ALOGD(" -- contextCount = %d", contextCount);
410 ALOGD(" -- bidiReq = %d", bidiReq);
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700411#endif
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700412 ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
413 if (U_SUCCESS(status)) {
414 int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700415 ssize_t rc = ubidi_countRuns(bidi, &status);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700416#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000417 ALOGD(" -- dirFlags = %d", dirFlags);
418 ALOGD(" -- paraDir = %d", paraDir);
419 ALOGD(" -- run-count = %d", int(rc));
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700420#endif
Fabrice Di Megliobcf05a62011-11-01 17:53:34 -0700421 if (U_SUCCESS(status) && rc == 1) {
422 // Normal case: one run, status is ok
423 isRTL = (paraDir == 1);
424 useSingleRun = true;
425 } else if (!U_SUCCESS(status) || rc < 1) {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800426 LOGW("Need to force to single run -- string = '%s',"
427 " status = %d, rc = %d",
Jeff Brown738ef872011-12-05 17:19:49 -0800428 String8(chars + start, count).string(), status, int(rc));
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700429 isRTL = (paraDir == 1);
430 useSingleRun = true;
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700431 } else {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700432 int32_t end = start + count;
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700433 for (size_t i = 0; i < size_t(rc); ++i) {
434 int32_t startRun = -1;
435 int32_t lengthRun = -1;
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700436 UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700437
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700438 if (startRun == -1 || lengthRun == -1) {
439 // Something went wrong when getting the visual run, need to clear
440 // already computed data before doing a single run pass
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800441 LOGW("Visual run is not valid");
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700442 outGlyphs->clear();
443 outAdvances->clear();
444 *outTotalAdvance = 0;
445 isRTL = (paraDir == 1);
446 useSingleRun = true;
447 break;
448 }
449
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700450 if (startRun >= end) {
451 continue;
452 }
453 int32_t endRun = startRun + lengthRun;
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700454 if (endRun <= int32_t(start)) {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700455 continue;
456 }
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700457 if (startRun < int32_t(start)) {
458 startRun = int32_t(start);
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700459 }
460 if (endRun > end) {
461 endRun = end;
462 }
463
464 lengthRun = endRun - startRun;
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700465 isRTL = (runDir == UBIDI_RTL);
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700466 jfloat runTotalAdvance = 0;
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700467#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000468 ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800469 i, startRun, lengthRun, isRTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700470#endif
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800471 computeRunValues(paint, chars + startRun, lengthRun, isRTL,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700472 outAdvances, &runTotalAdvance, outGlyphs);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700473
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700474 *outTotalAdvance += runTotalAdvance;
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700475 }
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700476 }
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700477 } else {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800478 LOGW("Cannot set Para");
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700479 useSingleRun = true;
480 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700481 }
482 ubidi_close(bidi);
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700483 } else {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800484 LOGW("Cannot ubidi_open()");
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700485 useSingleRun = true;
486 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700487 }
488 }
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700489
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700490 // Default single run case
491 if (useSingleRun){
492#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000493 ALOGD("Using a SINGLE BiDi Run "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800494 "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700495#endif
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800496 computeRunValues(paint, chars + start, count, isRTL,
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700497 outAdvances, outTotalAdvance, outGlyphs);
498 }
499
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700500#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000501 ALOGD(" -- Total returned glyphs-count = %d", outGlyphs->size());
502 ALOGD("******** ComputeValues -- end");
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700503#endif
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700504}
505
Fabrice Di Meglioabb0f292011-08-18 17:59:29 -0700506static void logGlyphs(HB_ShaperItem shaperItem) {
Steve Block5baa3a62011-12-20 16:23:08 +0000507 ALOGD(" -- glyphs count=%d", shaperItem.num_glyphs);
Fabrice Di Meglioabb0f292011-08-18 17:59:29 -0700508 for (size_t i = 0; i < shaperItem.num_glyphs; i++) {
Steve Block5baa3a62011-12-20 16:23:08 +0000509 ALOGD(" -- glyph[%d] = %d, offset.x = %0.2f, offset.y = %0.2f", i,
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800510 shaperItem.glyphs[i],
Fabrice Di Meglioabb0f292011-08-18 17:59:29 -0700511 HBFixedToFloat(shaperItem.offsets[i].x),
512 HBFixedToFloat(shaperItem.offsets[i].y));
513 }
514}
515
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800516void TextLayoutEngine::computeRunValues(SkPaint* paint, const UChar* chars,
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700517 size_t count, bool isRTL,
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700518 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700519 Vector<jchar>* const outGlyphs) {
Jeff Browna03bded2011-12-05 17:36:16 -0800520 if (!count) {
521 // We cannot shape an empty run.
522 *outTotalAdvance = 0;
523 return;
524 }
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700525
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800526 UErrorCode error = U_ZERO_ERROR;
527 bool useNormalizedString = false;
528 for (ssize_t i = count - 1; i >= 0; --i) {
529 UChar ch1 = chars[i];
530 if (::ublock_getCode(ch1) == UBLOCK_COMBINING_DIACRITICAL_MARKS) {
531 // So we have found a diacritic, let's get now the main code point which is paired
532 // with it. As we can have several diacritics in a row, we need to iterate back again
533#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000534 ALOGD("The BiDi run '%s' is containing a Diacritic at position %d",
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800535 String8(chars, count).string(), int(i));
536#endif
537 ssize_t j = i - 1;
538 for (; j >= 0; --j) {
539 UChar ch2 = chars[j];
540 if (::ublock_getCode(ch2) != UBLOCK_COMBINING_DIACRITICAL_MARKS) {
541 break;
542 }
543 }
544
545 // We could not found the main code point, so we will just use the initial chars
546 if (j < 0) {
547 break;
548 }
549
550#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000551 ALOGD("Found main code point at index %d", int(j));
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800552#endif
553 // We found the main code point, so we can normalize the "chunck" and fill
554 // the remaining with ZWSP so that the Paint.getTextWidth() APIs will still be able
555 // to get one advance per char
556 mBuffer.remove();
557 Normalizer::normalize(UnicodeString(chars + j, i - j + 1),
558 UNORM_NFC, 0 /* no options */, mBuffer, error);
559 if (U_SUCCESS(error)) {
560 if (!useNormalizedString) {
561 useNormalizedString = true;
562 mNormalizedString.setTo(false /* not terminated*/, chars, count);
563 }
564 // Set the normalized chars
565 for (ssize_t k = j; k < j + mBuffer.length(); ++k) {
566 mNormalizedString.setCharAt(k, mBuffer.charAt(k - j));
567 }
568 // Fill the remain part with ZWSP (ZWNJ and ZWJ would lead to weird results
569 // because some fonts are missing those glyphs)
570 for (ssize_t k = j + mBuffer.length(); k <= i; ++k) {
571 mNormalizedString.setCharAt(k, UNICODE_ZWSP);
572 }
573 }
574 i = j - 1;
575 }
576 }
577
578#if DEBUG_GLYPHS
579 if (useNormalizedString) {
Steve Block5baa3a62011-12-20 16:23:08 +0000580 ALOGD("Will use normalized string '%s', length = %d",
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800581 String8(mNormalizedString.getTerminatedBuffer(),
582 mNormalizedString.length()).string(),
583 mNormalizedString.length());
584 } else {
Steve Block5baa3a62011-12-20 16:23:08 +0000585 ALOGD("Normalization is not needed or cannot be done, using initial string");
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800586 }
587#endif
588
589 assert(mNormalizedString.length() == count);
590
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800591 // Set the string properties
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800592 mShaperItem.string = useNormalizedString ? mNormalizedString.getTerminatedBuffer() : chars;
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800593 mShaperItem.stringLength = count;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700594
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800595 // Define shaping paint properties
596 mShapingPaint.setTextSize(paint->getTextSize());
597 mShapingPaint.setTextSkewX(paint->getTextSkewX());
598 mShapingPaint.setTextScaleX(paint->getTextScaleX());
599 mShapingPaint.setFlags(paint->getFlags());
600 mShapingPaint.setHinting(paint->getHinting());
601
602 // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
603 // into the shaperItem
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800604 ssize_t indexFontRun = isRTL ? mShaperItem.stringLength - 1 : 0;
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800605 unsigned numCodePoints = 0;
Jeff Browna03bded2011-12-05 17:36:16 -0800606 jfloat totalAdvance = 0;
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800607 while ((isRTL) ?
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800608 hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, mShaperItem.string,
609 mShaperItem.stringLength, &indexFontRun):
610 hb_utf16_script_run_next(&numCodePoints, &mShaperItem.item, mShaperItem.string,
611 mShaperItem.stringLength, &indexFontRun)) {
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700612
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800613 ssize_t startScriptRun = mShaperItem.item.pos;
614 size_t countScriptRun = mShaperItem.item.length;
615 ssize_t endScriptRun = startScriptRun + countScriptRun;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700616
Fabrice Di Meglio9c418db2011-09-18 12:54:38 -0700617#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000618 ALOGD("-------- Start of Script Run --------");
619 ALOGD("Shaping Script Run with");
620 ALOGD(" -- isRTL = %d", isRTL);
621 ALOGD(" -- HB script = %d", mShaperItem.item.script);
622 ALOGD(" -- startFontRun = %d", int(startScriptRun));
623 ALOGD(" -- endFontRun = %d", int(endScriptRun));
624 ALOGD(" -- countFontRun = %d", countScriptRun);
625 ALOGD(" -- run = '%s'", String8(chars + startScriptRun, countScriptRun).string());
626 ALOGD(" -- string = '%s'", String8(chars, count).string());
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700627#endif
628
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800629 // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
630 // and shape the Font run
631 size_t glyphBaseCount = shapeFontRun(paint, isRTL);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700632
Fabrice Di Meglio78b868f2011-05-17 19:48:57 -0700633#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000634 ALOGD("Got from Harfbuzz");
635 ALOGD(" -- glyphBaseCount = %d", glyphBaseCount);
636 ALOGD(" -- num_glypth = %d", mShaperItem.num_glyphs);
637 ALOGD(" -- kerning_applied = %d", mShaperItem.kerning_applied);
638 ALOGD(" -- isDevKernText = %d", paint->isDevKernText());
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700639
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800640 logGlyphs(mShaperItem);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700641#endif
642 if (isRTL) {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800643 endScriptRun = startScriptRun;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700644#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000645 ALOGD("Updated endScriptRun = %d", int(endScriptRun));
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700646#endif
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700647 } else {
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800648 startScriptRun = endScriptRun;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700649#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000650 ALOGD("Updated startScriptRun = %d", int(startScriptRun));
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700651#endif
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700652 }
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700653
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800654 if (mShaperItem.advances == NULL || mShaperItem.num_glyphs == 0) {
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700655#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000656 ALOGD("Advances array is empty or num_glypth = 0");
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700657#endif
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800658 outAdvances->insertAt(0, outAdvances->size(), countScriptRun);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700659 continue;
660 }
661
662 // Get Advances and their total
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800663 jfloat currentAdvance = HBFixedToFloat(mShaperItem.advances[mShaperItem.log_clusters[0]]);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700664 jfloat totalFontRunAdvance = currentAdvance;
665 outAdvances->add(currentAdvance);
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800666 for (size_t i = 1; i < countScriptRun; i++) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800667 size_t clusterPrevious = mShaperItem.log_clusters[i - 1];
668 size_t cluster = mShaperItem.log_clusters[i];
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700669 if (cluster == clusterPrevious) {
670 outAdvances->add(0);
671 } else {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800672 currentAdvance = HBFixedToFloat(mShaperItem.advances[mShaperItem.log_clusters[i]]);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700673 totalFontRunAdvance += currentAdvance;
674 outAdvances->add(currentAdvance);
675 }
676 }
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800677
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700678 totalAdvance += totalFontRunAdvance;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700679
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700680#if DEBUG_ADVANCES
Steve Block5baa3a62011-12-20 16:23:08 +0000681 ALOGD("Returned advances");
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800682 for (size_t i = 0; i < countScriptRun; i++) {
Steve Block5baa3a62011-12-20 16:23:08 +0000683 ALOGD(" -- hb-adv[%d] = %0.2f, log_clusters = %d, total = %0.2f", i,
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800684 (*outAdvances)[i], mShaperItem.log_clusters[i], totalFontRunAdvance);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700685 }
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700686#endif
687
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700688 // Get Glyphs and reverse them in place if RTL
689 if (outGlyphs) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800690 size_t countGlyphs = mShaperItem.num_glyphs;
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800691#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000692 ALOGD("Returned script run glyphs -- count = %d", countGlyphs);
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800693#endif
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700694 for (size_t i = 0; i < countGlyphs; i++) {
695 jchar glyph = glyphBaseCount +
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800696 (jchar) mShaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700697#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000698 ALOGD(" -- glyph[%d] = %d", i, glyph);
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700699#endif
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700700 outGlyphs->add(glyph);
701 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700702 }
703 }
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800704
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700705 *outTotalAdvance = totalAdvance;
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800706
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800707#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000708 ALOGD("-------- End of Script Run --------");
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800709#endif
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700710}
711
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800712
713size_t TextLayoutEngine::shapeFontRun(SkPaint* paint, bool isRTL) {
714 // Reset kerning
715 mShaperItem.kerning_applied = false;
716
717 // Update Harfbuzz Shaper
718 mShaperItem.item.bidiLevel = isRTL;
719
720 SkTypeface* typeface = paint->getTypeface();
721
722 // Set the correct Typeface depending on the script
723 switch (mShaperItem.item.script) {
724 case HB_Script_Arabic:
725 typeface = getCachedTypeface(&mArabicTypeface, TYPEFACE_ARABIC);
726#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000727 ALOGD("Using Arabic Typeface");
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800728#endif
729 break;
730
731 case HB_Script_Hebrew:
732 if (typeface) {
733 switch (typeface->style()) {
734 case SkTypeface::kBold:
735 case SkTypeface::kBoldItalic:
736 typeface = getCachedTypeface(&mHebrewBoldTypeface, TYPE_FACE_HEBREW_BOLD);
737#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000738 ALOGD("Using Hebrew Bold/BoldItalic Typeface");
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800739#endif
740 break;
741
742 case SkTypeface::kNormal:
743 case SkTypeface::kItalic:
744 default:
745 typeface = getCachedTypeface(&mHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
746#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000747 ALOGD("Using Hebrew Regular/Italic Typeface");
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800748#endif
749 break;
750 }
751 } else {
752 typeface = getCachedTypeface(&mHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
753#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000754 ALOGD("Using Hebrew Regular Typeface");
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800755#endif
756 }
757 break;
758
Fabrice Di Meglio65194ad2011-12-13 16:26:08 -0800759 case HB_Script_Bengali:
760 typeface = getCachedTypeface(&mBengaliTypeface, TYPEFACE_BENGALI);
761#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000762 ALOGD("Using Bengali Typeface");
Fabrice Di Meglio65194ad2011-12-13 16:26:08 -0800763#endif
764 break;
765
Fabrice Di Meglioa4d07702011-12-13 18:52:13 -0800766 case HB_Script_Thai:
767 typeface = getCachedTypeface(&mThaiTypeface, TYPEFACE_THAI);
768#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000769 ALOGD("Using Thai Typeface");
Fabrice Di Meglioa4d07702011-12-13 18:52:13 -0800770#endif
771 break;
772
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800773 default:
774 if (!typeface) {
775 typeface = mDefaultTypeface;
776#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000777 ALOGD("Using Default Typeface");
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800778#endif
779 } else {
780#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000781 ALOGD("Using Paint Typeface");
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800782#endif
783 }
784 break;
785 }
786
787 mShapingPaint.setTypeface(typeface);
788 mShaperItem.face = getCachedHBFace(typeface);
789
790#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000791 ALOGD("Run typeface = %p, uniqueID = %d, hb_face = %p",
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800792 typeface, typeface->uniqueID(), mShaperItem.face);
793#endif
794
795 // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
796 // This is needed as the Typeface used for shaping can be not the default one
797 // when we are shaping any script that needs to use a fallback Font.
798 // If we are a "common" script we dont need to shift
799 size_t baseGlyphCount = 0;
800 switch (mShaperItem.item.script) {
801 case HB_Script_Arabic:
Fabrice Di Meglio65194ad2011-12-13 16:26:08 -0800802 case HB_Script_Hebrew:
Fabrice Di Meglioa4d07702011-12-13 18:52:13 -0800803 case HB_Script_Bengali:
804 case HB_Script_Thai:{
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800805 const uint16_t* text16 = (const uint16_t*)mShaperItem.string;
806 SkUnichar firstUnichar = SkUTF16_NextUnichar(&text16);
807 baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
808 break;
809 }
810 default:
811 break;
812 }
813
814 // Shape
Jeff Browna03bded2011-12-05 17:36:16 -0800815 assert(mShaperItem.item.length > 0); // Harfbuzz will overwrite other memory if length is 0.
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800816 ensureShaperItemGlyphArrays(mShaperItem.item.length * 3 / 2);
817 mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
818 while (!HB_ShapeItem(&mShaperItem)) {
819 // We overflowed our glyph arrays. Resize and retry.
820 // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
821 ensureShaperItemGlyphArrays(mShaperItem.num_glyphs * 2);
822 mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
823 }
824 return baseGlyphCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700825}
826
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800827void TextLayoutEngine::ensureShaperItemGlyphArrays(size_t size) {
828 if (size > mShaperItemGlyphArraySize) {
829 deleteShaperItemGlyphArrays();
830 createShaperItemGlyphArrays(size);
831 }
832}
833
834void TextLayoutEngine::createShaperItemGlyphArrays(size_t size) {
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700835#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000836 ALOGD("Creating Glyph Arrays with size = %d", size);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700837#endif
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800838 mShaperItemGlyphArraySize = size;
Jeff Brown738ef872011-12-05 17:19:49 -0800839
840 // These arrays are all indexed by glyph.
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800841 mShaperItem.glyphs = new HB_Glyph[size];
842 mShaperItem.attributes = new HB_GlyphAttributes[size];
843 mShaperItem.advances = new HB_Fixed[size];
844 mShaperItem.offsets = new HB_FixedPoint[size];
Jeff Brown738ef872011-12-05 17:19:49 -0800845
846 // Although the log_clusters array is indexed by character, Harfbuzz expects that
847 // it is big enough to hold one element per glyph. So we allocate log_clusters along
848 // with the other glyph arrays above.
849 mShaperItem.log_clusters = new unsigned short[size];
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800850}
851
852void TextLayoutEngine::deleteShaperItemGlyphArrays() {
853 delete[] mShaperItem.glyphs;
854 delete[] mShaperItem.attributes;
855 delete[] mShaperItem.advances;
856 delete[] mShaperItem.offsets;
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800857 delete[] mShaperItem.log_clusters;
858}
859
860SkTypeface* TextLayoutEngine::getCachedTypeface(SkTypeface** typeface, const char path[]) {
861 if (!*typeface) {
862 *typeface = SkTypeface::CreateFromFile(path);
Fabrice Di Meglio65194ad2011-12-13 16:26:08 -0800863 // CreateFromFile(path) can return NULL if the path is non existing
864 if (!*typeface) {
865#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000866 ALOGD("Font path '%s' is not valid, will use default font", path);
Fabrice Di Meglio65194ad2011-12-13 16:26:08 -0800867#endif
868 return mDefaultTypeface;
869 }
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800870 (*typeface)->ref();
871#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000872 ALOGD("Created SkTypeface from file '%s' with uniqueID = %d", path, (*typeface)->uniqueID());
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800873#endif
874 }
875 return *typeface;
876}
877
878HB_Face TextLayoutEngine::getCachedHBFace(SkTypeface* typeface) {
879 SkFontID fontId = typeface->uniqueID();
880 ssize_t index = mCachedHBFaces.indexOfKey(fontId);
881 if (index >= 0) {
882 return mCachedHBFaces.valueAt(index);
883 }
884 HB_Face face = HB_NewFace(typeface, harfbuzzSkiaGetTable);
885 if (face) {
886#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000887 ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800888#endif
889 mCachedHBFaces.add(fontId, face);
890 }
891 return face;
Fabrice Di Meglio79df5322011-09-19 15:17:56 -0700892}
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700893
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800894} // namespace android