blob: 7cdd04d6536264447ad6d999a85f35cf9e51e0b5 [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
Raph Leviend98efca2012-10-25 23:17:33 -070019#include <utils/JenkinsHash.h>
20
Fabrice Di Megliod313c662011-02-24 19:56:18 -080021#include "TextLayoutCache.h"
Fabrice Di Meglio689e5152011-04-13 16:07:37 -070022#include "TextLayout.h"
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -070023#include "SkFontHost.h"
Billy Hewlettac1cbaf2012-07-18 09:51:45 -070024#include "SkTypeface_android.h"
Raph Levienaaedde52012-10-30 15:55:33 -070025#include "HarfBuzzNGFaceSkia.h"
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -080026#include <unicode/unistr.h>
Fabrice Di Meglio3632b7f2012-04-24 19:55:18 -070027#include <unicode/uchar.h>
Raph Levienaaedde52012-10-30 15:55:33 -070028#include <hb-icu.h>
Fabrice Di Meglio5de5b1a2011-08-09 14:37:22 -070029
Fabrice Di Megliod313c662011-02-24 19:56:18 -080030namespace android {
31
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070032//--------------------------------------------------------------------------------------------------
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -070033
Fabrice Di Meglioa731b082012-01-23 18:18:45 -080034ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
Fabrice Di Megliob02d0ca2011-12-08 14:05:44 -080035
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070036//--------------------------------------------------------------------------------------------------
37
Fabrice Di Meglioa731b082012-01-23 18:18:45 -080038TextLayoutCache::TextLayoutCache(TextLayoutShaper* shaper) :
39 mShaper(shaper),
Raph Leviend98efca2012-10-25 23:17:33 -070040 mCache(LruCache<TextLayoutCacheKey, sp<TextLayoutValue> >::kUnlimitedCapacity),
Fabrice Di Megliod313c662011-02-24 19:56:18 -080041 mSize(0), mMaxSize(MB(DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB)),
42 mCacheHitCount(0), mNanosecondsSaved(0) {
43 init();
44}
45
Fabrice Di Megliod313c662011-02-24 19:56:18 -080046TextLayoutCache::~TextLayoutCache() {
47 mCache.clear();
48}
49
50void TextLayoutCache::init() {
51 mCache.setOnEntryRemovedListener(this);
52
53 mDebugLevel = readRtlDebugLevel();
54 mDebugEnabled = mDebugLevel & kRtlDebugCaches;
Steve Block5baa3a62011-12-20 16:23:08 +000055 ALOGD("Using debug level = %d - Debug Enabled = %d", mDebugLevel, mDebugEnabled);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080056
57 mCacheStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
Fabrice Di Meglio9f82b582011-03-08 12:02:59 -080058
59 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +000060 ALOGD("Initialization is done - Start time = %lld", mCacheStartTime);
Fabrice Di Meglio9f82b582011-03-08 12:02:59 -080061 }
62
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070063 mInitialized = true;
Fabrice Di Megliod313c662011-02-24 19:56:18 -080064}
65
Fabrice Di Megliod313c662011-02-24 19:56:18 -080066/**
67 * Callbacks
68 */
Fabrice Di Meglioa731b082012-01-23 18:18:45 -080069void TextLayoutCache::operator()(TextLayoutCacheKey& text, sp<TextLayoutValue>& desc) {
Jeff Brown06daa7b2011-11-11 15:14:56 -080070 size_t totalSizeToDelete = text.getSize() + desc->getSize();
71 mSize -= totalSizeToDelete;
72 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +000073 ALOGD("Cache value %p deleted, size = %d", desc.get(), totalSizeToDelete);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080074 }
75}
76
77/*
78 * Cache clearing
79 */
Raph Levien13ba4e42012-09-12 15:15:51 -070080void TextLayoutCache::purgeCaches() {
81 AutoMutex _l(mLock);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080082 mCache.clear();
Raph Levien13ba4e42012-09-12 15:15:51 -070083 mShaper->purgeCaches();
Fabrice Di Megliod313c662011-02-24 19:56:18 -080084}
85
86/*
87 * Caching
88 */
Fabrice Di Meglioa731b082012-01-23 18:18:45 -080089sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -080090 const jchar* text, jint start, jint count, jint contextCount) {
Fabrice Di Megliod313c662011-02-24 19:56:18 -080091 AutoMutex _l(mLock);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080092 nsecs_t startTime = 0;
93 if (mDebugEnabled) {
94 startTime = systemTime(SYSTEM_TIME_MONOTONIC);
95 }
96
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -070097 // Create the key
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -080098 TextLayoutCacheKey key(paint, text, start, count, contextCount);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080099
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700100 // Get value from cache if possible
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800101 sp<TextLayoutValue> value = mCache.get(key);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800102
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700103 // Value not found for the key, we need to add a new value in the cache
104 if (value == NULL) {
Fabrice Di Meglio010d5c42011-04-21 15:33:50 -0700105 if (mDebugEnabled) {
106 startTime = systemTime(SYSTEM_TIME_MONOTONIC);
107 }
108
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800109 value = new TextLayoutValue(contextCount);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800110
111 // Compute advances and store them
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800112 mShaper->computeValues(value.get(), paint,
Raph Levien832815c2012-10-02 10:30:41 -0700113 reinterpret_cast<const UChar*>(key.getText()), start, count,
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800114 size_t(contextCount));
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800115
Jeff Brown06daa7b2011-11-11 15:14:56 -0800116 if (mDebugEnabled) {
117 value->setElapsedTime(systemTime(SYSTEM_TIME_MONOTONIC) - startTime);
118 }
Fabrice Di Meglio010d5c42011-04-21 15:33:50 -0700119
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800120 // Don't bother to add in the cache if the entry is too big
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700121 size_t size = key.getSize() + value->getSize();
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800122 if (size <= mMaxSize) {
123 // Cleanup to make some room if needed
124 if (mSize + size > mMaxSize) {
125 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000126 ALOGD("Need to clean some entries for making some room for a new entry");
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800127 }
128 while (mSize + size > mMaxSize) {
129 // This will call the callback
Jeff Brownd9e688c2011-11-11 15:40:13 -0800130 bool removedOne = mCache.removeOldest();
Jeff Brown06daa7b2011-11-11 15:14:56 -0800131 LOG_ALWAYS_FATAL_IF(!removedOne, "The cache is non-empty but we "
132 "failed to remove the oldest entry. "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800133 "mSize = %u, size = %u, mMaxSize = %u, mCache.size() = %u",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800134 mSize, size, mMaxSize, mCache.size());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800135 }
136 }
137
138 // Update current cache size
139 mSize += size;
140
Jeff Brown06daa7b2011-11-11 15:14:56 -0800141 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!"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800145 " - start = %d, count = %d, contextCount = %d - Text = '%s'",
Raph Levien832815c2012-10-02 10:30:41 -0700146 start, count, contextCount, String8(key.getText() + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800147
148 if (mDebugEnabled) {
Jeff Brown06daa7b2011-11-11 15:14:56 -0800149 nsecs_t totalTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
Steve Block5baa3a62011-12-20 16:23:08 +0000150 ALOGD("CACHE MISS: Added entry %p "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800151 "with start = %d, count = %d, contextCount = %d, "
Jeff Brown06daa7b2011-11-11 15:14:56 -0800152 "entry size %d bytes, remaining space %d bytes"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800153 " - Compute time %0.6f ms - Put time %0.6f ms - Text = '%s'",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800154 value.get(), start, count, contextCount, size, mMaxSize - mSize,
155 value->getElapsedTime() * 0.000001f,
156 (totalTime - value->getElapsedTime()) * 0.000001f,
Raph Levien832815c2012-10-02 10:30:41 -0700157 String8(key.getText() + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800158 }
159 } else {
160 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000161 ALOGD("CACHE MISS: Calculated but not storing entry because it is too big "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800162 "with start = %d, count = %d, contextCount = %d, "
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800163 "entry size %d bytes, remaining space %d bytes"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800164 " - Compute time %0.6f ms - Text = '%s'",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800165 start, count, contextCount, size, mMaxSize - mSize,
166 value->getElapsedTime() * 0.000001f,
Raph Levien832815c2012-10-02 10:30:41 -0700167 String8(key.getText() + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800168 }
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800169 }
170 } else {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700171 // This is a cache hit, just log timestamp and user infos
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800172 if (mDebugEnabled) {
173 nsecs_t elapsedTimeThruCacheGet = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700174 mNanosecondsSaved += (value->getElapsedTime() - elapsedTimeThruCacheGet);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800175 ++mCacheHitCount;
176
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700177 if (value->getElapsedTime() > 0) {
178 float deltaPercent = 100 * ((value->getElapsedTime() - elapsedTimeThruCacheGet)
179 / ((float)value->getElapsedTime()));
Steve Block5baa3a62011-12-20 16:23:08 +0000180 ALOGD("CACHE HIT #%d with start = %d, count = %d, contextCount = %d"
Jeff Brown06daa7b2011-11-11 15:14:56 -0800181 "- Compute time %0.6f ms - "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800182 "Cache get time %0.6f ms - Gain in percent: %2.2f - Text = '%s'",
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700183 mCacheHitCount, start, count, contextCount,
Jeff Brown06daa7b2011-11-11 15:14:56 -0800184 value->getElapsedTime() * 0.000001f,
185 elapsedTimeThruCacheGet * 0.000001f,
186 deltaPercent,
Raph Levien832815c2012-10-02 10:30:41 -0700187 String8(key.getText() + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800188 }
189 if (mCacheHitCount % DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL == 0) {
190 dumpCacheStats();
191 }
192 }
193 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700194 return value;
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800195}
196
197void TextLayoutCache::dumpCacheStats() {
198 float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize));
199 float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000;
Jeff Brown06daa7b2011-11-11 15:14:56 -0800200
Jeff Brown06daa7b2011-11-11 15:14:56 -0800201 size_t cacheSize = mCache.size();
Jeff Brown06daa7b2011-11-11 15:14:56 -0800202
Steve Block5baa3a62011-12-20 16:23:08 +0000203 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);
Raph Leviend98efca2012-10-25 23:17:33 -0700210 ALOGD("used : %d bytes according to mSize", mSize);
Steve Block5baa3a62011-12-20 16:23:08 +0000211 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("------------------------------------------------");
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800215}
216
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700217/**
218 * TextLayoutCacheKey
219 */
Raph Levien832815c2012-10-02 10:30:41 -0700220TextLayoutCacheKey::TextLayoutCacheKey(): start(0), count(0), contextCount(0),
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800221 typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700222 hinting(SkPaint::kNo_Hinting), variant(SkPaint::kDefault_Variant), language() {
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700223}
224
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700225TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text,
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800226 size_t start, size_t count, size_t contextCount) :
227 start(start), count(count), contextCount(contextCount) {
Raph Levien832815c2012-10-02 10:30:41 -0700228 textCopy.setTo(text, contextCount);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700229 typeface = paint->getTypeface();
230 textSize = paint->getTextSize();
231 textSkewX = paint->getTextSkewX();
232 textScaleX = paint->getTextScaleX();
233 flags = paint->getFlags();
234 hinting = paint->getHinting();
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700235 variant = paint->getFontVariant();
236 language = paint->getLanguage();
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700237}
238
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700239TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) :
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700240 textCopy(other.textCopy),
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700241 start(other.start),
Jeff Brown7aac2972011-09-19 16:17:58 -0700242 count(other.count),
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700243 contextCount(other.contextCount),
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700244 typeface(other.typeface),
245 textSize(other.textSize),
246 textSkewX(other.textSkewX),
247 textScaleX(other.textScaleX),
248 flags(other.flags),
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700249 hinting(other.hinting),
250 variant(other.variant),
251 language(other.language) {
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700252}
253
Fabrice Di Meglio717060b2011-09-27 15:53:42 -0700254int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700255 int deltaInt = lhs.start - rhs.start;
256 if (deltaInt != 0) return (deltaInt);
257
258 deltaInt = lhs.count - rhs.count;
259 if (deltaInt != 0) return (deltaInt);
260
261 deltaInt = lhs.contextCount - rhs.contextCount;
Fabrice Di Meglio717060b2011-09-27 15:53:42 -0700262 if (deltaInt != 0) return (deltaInt);
263
264 if (lhs.typeface < rhs.typeface) return -1;
265 if (lhs.typeface > rhs.typeface) return +1;
266
267 if (lhs.textSize < rhs.textSize) return -1;
268 if (lhs.textSize > rhs.textSize) return +1;
269
270 if (lhs.textSkewX < rhs.textSkewX) return -1;
271 if (lhs.textSkewX > rhs.textSkewX) return +1;
272
273 if (lhs.textScaleX < rhs.textScaleX) return -1;
274 if (lhs.textScaleX > rhs.textScaleX) return +1;
275
276 deltaInt = lhs.flags - rhs.flags;
277 if (deltaInt != 0) return (deltaInt);
278
279 deltaInt = lhs.hinting - rhs.hinting;
280 if (deltaInt != 0) return (deltaInt);
281
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700282 deltaInt = lhs.variant - rhs.variant;
283 if (deltaInt) return (deltaInt);
284
285 if (lhs.language < rhs.language) return -1;
286 if (lhs.language > rhs.language) return +1;
287
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700288 return memcmp(lhs.getText(), rhs.getText(), lhs.contextCount * sizeof(UChar));
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700289}
290
Jeff Brown06daa7b2011-11-11 15:14:56 -0800291size_t TextLayoutCacheKey::getSize() const {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700292 return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700293}
294
Raph Leviend98efca2012-10-25 23:17:33 -0700295hash_t TextLayoutCacheKey::hash() const {
296 uint32_t hash = JenkinsHashMix(0, start);
297 hash = JenkinsHashMix(hash, count);
298 /* contextCount not needed because it's included in text, below */
299 hash = JenkinsHashMix(hash, hash_type(typeface));
300 hash = JenkinsHashMix(hash, hash_type(textSize));
301 hash = JenkinsHashMix(hash, hash_type(textSkewX));
302 hash = JenkinsHashMix(hash, hash_type(textScaleX));
303 hash = JenkinsHashMix(hash, flags);
304 hash = JenkinsHashMix(hash, hinting);
305 hash = JenkinsHashMix(hash, variant);
306 // Note: leaving out language is not problematic, as equality comparisons
307 // are still valid - the only bad thing that could happen is collisions.
308 hash = JenkinsHashMixShorts(hash, getText(), contextCount);
309 return JenkinsHashWhiten(hash);
310}
311
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700312/**
313 * TextLayoutCacheValue
314 */
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800315TextLayoutValue::TextLayoutValue(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);
Raph Levien2301d322012-07-17 16:39:49 -0700320 mPos.setCapacity(contextCount * 2);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800321}
322
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800323size_t TextLayoutValue::getSize() const {
324 return sizeof(TextLayoutValue) + sizeof(jfloat) * mAdvances.capacity() +
Raph Levien2301d322012-07-17 16:39:49 -0700325 sizeof(jchar) * mGlyphs.capacity() + sizeof(jfloat) * mPos.capacity();
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700326}
327
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800328void TextLayoutValue::setElapsedTime(uint32_t time) {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700329 mElapsedTime = time;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700330}
331
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800332uint32_t TextLayoutValue::getElapsedTime() {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700333 return mElapsedTime;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700334}
335
Raph Levienaaedde52012-10-30 15:55:33 -0700336TextLayoutShaper::TextLayoutShaper() {
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700337 init();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800338
Raph Levienaaedde52012-10-30 15:55:33 -0700339 mBuffer = hb_buffer_create();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800340}
341
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700342void TextLayoutShaper::init() {
Derek Sollenbergerca79cf62012-08-14 16:44:52 -0400343 mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700344}
345
346void TextLayoutShaper::unrefTypefaces() {
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800347 SkSafeUnref(mDefaultTypeface);
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700348}
349
350TextLayoutShaper::~TextLayoutShaper() {
Raph Levienaaedde52012-10-30 15:55:33 -0700351 hb_buffer_destroy(mBuffer);
352
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700353 unrefTypefaces();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800354}
355
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800356void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800357 size_t start, size_t count, size_t contextCount) {
Fabrice Di Meglio79df5322011-09-19 15:17:56 -0700358
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800359 computeValues(paint, chars, start, count, contextCount,
Raph Levien2301d322012-07-17 16:39:49 -0700360 &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs, &value->mPos);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700361#if DEBUG_ADVANCES
Steve Block5baa3a62011-12-20 16:23:08 +0000362 ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800363 contextCount, value->mTotalAdvance);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700364#endif
365}
366
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800367void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800368 size_t start, size_t count, size_t contextCount,
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700369 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Raph Levien2301d322012-07-17 16:39:49 -0700370 Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
371 *outTotalAdvance = 0;
Jeff Browna03bded2011-12-05 17:36:16 -0800372 if (!count) {
Jeff Browna03bded2011-12-05 17:36:16 -0800373 return;
374 }
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700375
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800376 UBiDiLevel bidiReq = UBIDI_DEFAULT_LTR;
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700377 bool useSingleRun = false;
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800378 bool isRTL = false;
379
380 UBiDi* bidi = ubidi_open();
381 if (bidi) {
382 UErrorCode status = U_ZERO_ERROR;
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700383#if DEBUG_GLYPHS
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800384 ALOGD("******** ComputeValues -- start");
385 ALOGD(" -- string = '%s'", String8(chars + start, count).string());
386 ALOGD(" -- start = %d", start);
387 ALOGD(" -- count = %d", count);
388 ALOGD(" -- contextCount = %d", contextCount);
389 ALOGD(" -- bidiReq = %d", bidiReq);
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700390#endif
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800391 ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
392 if (U_SUCCESS(status)) {
393 int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
394 ssize_t rc = ubidi_countRuns(bidi, &status);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700395#if DEBUG_GLYPHS
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800396 ALOGD(" -- paraDir = %d", paraDir);
397 ALOGD(" -- run-count = %d", int(rc));
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700398#endif
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800399 if (U_SUCCESS(status) && rc == 1) {
400 // Normal case: one run, status is ok
401 isRTL = (paraDir == 1);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700402 useSingleRun = true;
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800403 } else if (!U_SUCCESS(status) || rc < 1) {
404 ALOGW("Need to force to single run -- string = '%s',"
405 " status = %d, rc = %d",
406 String8(chars + start, count).string(), status, int(rc));
407 isRTL = (paraDir == 1);
408 useSingleRun = true;
409 } else {
410 int32_t end = start + count;
411 for (size_t i = 0; i < size_t(rc); ++i) {
412 int32_t startRun = -1;
413 int32_t lengthRun = -1;
414 UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
415
416 if (startRun == -1 || lengthRun == -1) {
417 // Something went wrong when getting the visual run, need to clear
418 // already computed data before doing a single run pass
419 ALOGW("Visual run is not valid");
420 outGlyphs->clear();
421 outAdvances->clear();
422 outPos->clear();
423 *outTotalAdvance = 0;
424 isRTL = (paraDir == 1);
425 useSingleRun = true;
426 break;
427 }
428
429 if (startRun >= end) {
430 continue;
431 }
432 int32_t endRun = startRun + lengthRun;
433 if (endRun <= int32_t(start)) {
434 continue;
435 }
436 if (startRun < int32_t(start)) {
437 startRun = int32_t(start);
438 }
439 if (endRun > end) {
440 endRun = end;
441 }
442
443 lengthRun = endRun - startRun;
444 isRTL = (runDir == UBIDI_RTL);
445#if DEBUG_GLYPHS
446 ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
447 i, startRun, lengthRun, isRTL);
448#endif
449 computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
450 outAdvances, outTotalAdvance, outGlyphs, outPos);
451
452 }
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700453 }
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700454 } else {
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800455 ALOGW("Cannot set Para");
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700456 useSingleRun = true;
457 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700458 }
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800459 ubidi_close(bidi);
460 } else {
461 ALOGW("Cannot ubidi_open()");
462 useSingleRun = true;
463 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700464 }
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700465
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700466 // Default single run case
467 if (useSingleRun){
468#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000469 ALOGD("Using a SINGLE BiDi Run "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800470 "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700471#endif
Raph Levienaaedde52012-10-30 15:55:33 -0700472 computeRunValues(paint, chars, start, count, contextCount, isRTL,
Raph Levien2301d322012-07-17 16:39:49 -0700473 outAdvances, outTotalAdvance, outGlyphs, outPos);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700474 }
475
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700476#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000477 ALOGD(" -- Total returned glyphs-count = %d", outGlyphs->size());
478 ALOGD("******** ComputeValues -- end");
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700479#endif
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700480}
481
Raph Levienaaedde52012-10-30 15:55:33 -0700482#define HB_IsHighSurrogate(ucs) \
483 (((ucs) & 0xfc00) == 0xd800)
484
485#define HB_IsLowSurrogate(ucs) \
486 (((ucs) & 0xfc00) == 0xdc00)
487
488#ifndef HB_SurrogateToUcs4
489#define HB_SurrogateToUcs4_(high, low) \
490 (((hb_codepoint_t)(high))<<10) + (low) - 0x35fdc00;
491#endif
492
493#define HB_InvalidCodePoint ~0u
494
495hb_codepoint_t
496utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) {
497 const uint16_t v = chars[(*iter)++];
498 if (HB_IsHighSurrogate(v)) {
499 // surrogate pair
500 if (size_t(*iter) >= len) {
501 // the surrogate is incomplete.
502 return HB_InvalidCodePoint;
503 }
504 const uint16_t v2 = chars[(*iter)++];
505 if (!HB_IsLowSurrogate(v2)) {
506 // invalidate surrogate pair.
507 (*iter)--;
508 return HB_InvalidCodePoint;
509 }
510
511 return HB_SurrogateToUcs4(v, v2);
512 }
513
514 if (HB_IsLowSurrogate(v)) {
515 // this isn't a valid code point
516 return HB_InvalidCodePoint;
517 }
518
519 return v;
520}
521
522hb_codepoint_t
523utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) {
524 const uint16_t v = chars[(*iter)--];
525 if (HB_IsLowSurrogate(v)) {
526 // surrogate pair
527 if (*iter < 0) {
528 // the surrogate is incomplete.
529 return HB_InvalidCodePoint;
530 }
531 const uint16_t v2 = chars[(*iter)--];
532 if (!HB_IsHighSurrogate(v2)) {
533 // invalidate surrogate pair.
534 (*iter)++;
535 return HB_InvalidCodePoint;
536 }
537
538 return HB_SurrogateToUcs4(v2, v);
539 }
540
541 if (HB_IsHighSurrogate(v)) {
542 // this isn't a valid code point
543 return HB_InvalidCodePoint;
544 }
545
546 return v;
547}
548
549struct ScriptRun {
550 hb_script_t script;
551 size_t pos;
552 size_t length;
553};
554
555hb_script_t code_point_to_script(hb_codepoint_t codepoint) {
556 static hb_unicode_funcs_t* u;
557 if (!u) {
558 u = hb_icu_get_unicode_funcs();
559 }
560 return hb_unicode_script(u, codepoint);
561}
562
563bool
564hb_utf16_script_run_next(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
565 if (size_t(*iter) == len)
566 return false;
567
568 run->pos = *iter;
569 const uint32_t init_cp = utf16_to_code_point(chars, len, iter);
570 const hb_script_t init_script = code_point_to_script(init_cp);
571 hb_script_t current_script = init_script;
572 run->script = init_script;
573
574 for (;;) {
575 if (size_t(*iter) == len)
576 break;
577 const ssize_t prev_iter = *iter;
578 const uint32_t cp = utf16_to_code_point(chars, len, iter);
579 const hb_script_t script = code_point_to_script(cp);
580
581 if (script != current_script) {
582 /* BEGIN android-changed
583 The condition was not correct by doing "a == b == constant"
584 END android-changed */
585 if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
586 // If we started off as inherited, we take whatever we can find.
587 run->script = script;
588 current_script = script;
589 continue;
590 } else if (script == HB_SCRIPT_INHERITED) {
591 continue;
592 } else {
593 *iter = prev_iter;
594 break;
595 }
596 }
597 }
598
599 if (run->script == HB_SCRIPT_INHERITED)
600 run->script = HB_SCRIPT_COMMON;
601
602 run->length = *iter - run->pos;
603 return true;
604}
605
606bool
607hb_utf16_script_run_prev(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
608 if (*iter == -1)
609 return false;
610
611 const size_t ending_index = *iter;
612 const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter);
613 const hb_script_t init_script = code_point_to_script(init_cp);
614 hb_script_t current_script = init_script;
615 run->script = init_script;
616
617 for (;;) {
618 if (*iter < 0)
619 break;
620 const ssize_t prev_iter = *iter;
621 const uint32_t cp = utf16_to_code_point_prev(chars, len, iter);
622 const hb_script_t script = code_point_to_script(cp);
623
624 if (script != current_script) {
625 if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
626 // If we started off as inherited, we take whatever we can find.
627 run->script = script;
628 current_script = script;
629 continue;
630 } else if (script == HB_SCRIPT_INHERITED) {
631 /* BEGIN android-changed
632 We apply the same fix for Chrome to Android.
633 Chrome team will talk with upsteam about it.
634 Just assume that whatever follows this combining character is within
635 the same script. This is incorrect if you had language1 + combining
636 char + language 2, but that is rare and this code is suspicious
637 anyway.
638 END android-changed */
639 continue;
640 } else {
641 *iter = prev_iter;
642 break;
643 }
644 }
645 }
646
647 if (run->script == HB_SCRIPT_INHERITED)
648 run->script = HB_SCRIPT_COMMON;
649
650 run->pos = *iter + 1;
651 run->length = ending_index - *iter;
652 return true;
653}
654
655
656static void logGlyphs(hb_buffer_t* buffer) {
657 unsigned int numGlyphs;
658 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
659 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
660 ALOGD(" -- glyphs count=%d", numGlyphs);
661 for (size_t i = 0; i < numGlyphs; i++) {
662 ALOGD(" -- glyph[%d] = %d, cluster = %u, advance = %0.2f, offset.x = %0.2f, offset.y = %0.2f", i,
663 info[i].codepoint,
664 info[i].cluster,
665 HBFixedToFloat(positions[i].x_advance),
666 HBFixedToFloat(positions[i].x_offset),
667 HBFixedToFloat(positions[i].y_offset));
Fabrice Di Meglioabb0f292011-08-18 17:59:29 -0700668 }
669}
670
Raph Levienaaedde52012-10-30 15:55:33 -0700671void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars,
672 size_t start, size_t count, size_t contextCount, bool isRTL,
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700673 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Raph Levien2301d322012-07-17 16:39:49 -0700674 Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
Jeff Browna03bded2011-12-05 17:36:16 -0800675 if (!count) {
676 // We cannot shape an empty run.
Jeff Browna03bded2011-12-05 17:36:16 -0800677 return;
678 }
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700679
Raph Levien57e97232012-04-24 16:04:34 -0700680 // To be filled in later
681 for (size_t i = 0; i < count; i++) {
682 outAdvances->add(0);
683 }
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800684
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800685 // Set the string properties
Raph Levienaaedde52012-10-30 15:55:33 -0700686 const UChar* chars = contextChars + start;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700687
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800688 // Define shaping paint properties
689 mShapingPaint.setTextSize(paint->getTextSize());
Raph Levien2301d322012-07-17 16:39:49 -0700690 float skewX = paint->getTextSkewX();
691 mShapingPaint.setTextSkewX(skewX);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800692 mShapingPaint.setTextScaleX(paint->getTextScaleX());
693 mShapingPaint.setFlags(paint->getFlags());
694 mShapingPaint.setHinting(paint->getHinting());
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700695 mShapingPaint.setFontVariant(paint->getFontVariant());
696 mShapingPaint.setLanguage(paint->getLanguage());
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800697
698 // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
699 // into the shaperItem
Raph Levienaaedde52012-10-30 15:55:33 -0700700 ssize_t indexFontRun = isRTL ? count - 1 : 0;
Raph Levien2301d322012-07-17 16:39:49 -0700701 jfloat totalAdvance = *outTotalAdvance;
Raph Levienaaedde52012-10-30 15:55:33 -0700702 ScriptRun run; // relative to chars
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800703 while ((isRTL) ?
Raph Levienaaedde52012-10-30 15:55:33 -0700704 hb_utf16_script_run_prev(&run, chars, count, &indexFontRun):
705 hb_utf16_script_run_next(&run, chars, count, &indexFontRun)) {
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700706
Fabrice Di Meglio9c418db2011-09-18 12:54:38 -0700707#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000708 ALOGD("-------- Start of Script Run --------");
709 ALOGD("Shaping Script Run with");
710 ALOGD(" -- isRTL = %d", isRTL);
Raph Levienaaedde52012-10-30 15:55:33 -0700711 ALOGD(" -- HB script = %c%c%c%c", HB_UNTAG(run.script));
712 ALOGD(" -- run.pos = %d", int(run.pos));
713 ALOGD(" -- run.length = %d", int(run.length));
714 ALOGD(" -- run = '%s'", String8(chars + run.pos, run.length).string());
Steve Block5baa3a62011-12-20 16:23:08 +0000715 ALOGD(" -- string = '%s'", String8(chars, count).string());
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700716#endif
717
Raph Levienaaedde52012-10-30 15:55:33 -0700718 hb_buffer_reset(mBuffer);
719 // Note: if we want to set unicode functions, etc., this is the place.
720
721 hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
722 hb_buffer_set_script(mBuffer, run.script);
723 // Should set language here (for bug 7004056)
724 hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length);
725
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800726 // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
727 // and shape the Font run
Raph Levienaaedde52012-10-30 15:55:33 -0700728 size_t glyphBaseCount = shapeFontRun(paint);
729 unsigned int numGlyphs;
730 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
731 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(mBuffer, NULL);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700732
Fabrice Di Meglio78b868f2011-05-17 19:48:57 -0700733#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000734 ALOGD("Got from Harfbuzz");
735 ALOGD(" -- glyphBaseCount = %d", glyphBaseCount);
Raph Levienaaedde52012-10-30 15:55:33 -0700736 ALOGD(" -- num_glyph = %d", numGlyphs);
Steve Block5baa3a62011-12-20 16:23:08 +0000737 ALOGD(" -- isDevKernText = %d", paint->isDevKernText());
Raph Levienaaedde52012-10-30 15:55:33 -0700738 ALOGD(" -- initial totalAdvance = %f", totalAdvance);
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700739
Raph Levienaaedde52012-10-30 15:55:33 -0700740 logGlyphs(mBuffer);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700741#endif
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700742
Raph Levienaaedde52012-10-30 15:55:33 -0700743 for (size_t i = 0; i < numGlyphs; i++) {
744 size_t cluster = info[i].cluster - start;
745 float xAdvance = HBFixedToFloat(positions[i].x_advance);
746 outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster);
747 outGlyphs->add(info[i].codepoint + glyphBaseCount);
748 float xo = HBFixedToFloat(positions[i].x_offset);
749 float yo = -HBFixedToFloat(positions[i].y_offset);
750 outPos->add(totalAdvance + xo + yo * skewX);
751 outPos->add(yo);
752 totalAdvance += xAdvance;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700753 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700754 }
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800755
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700756 *outTotalAdvance = totalAdvance;
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800757
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800758#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700759 ALOGD(" -- final totalAdvance = %f", totalAdvance);
Steve Block5baa3a62011-12-20 16:23:08 +0000760 ALOGD("-------- End of Script Run --------");
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800761#endif
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700762}
763
Raph Levien1637dcd2012-05-02 10:41:14 -0700764/**
765 * Return the first typeface in the logical change, starting with this typeface,
766 * that contains the specified unichar, or NULL if none is found.
767 *
768 * Note that this function does _not_ increment the reference count on the typeface, as the
769 * assumption is that its lifetime is managed elsewhere - in particular, the fallback typefaces
770 * for the default font live in a global cache.
771 */
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700772SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
Raph Levienaaedde52012-10-30 15:55:33 -0700773 hb_script_t script) {
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700774 SkTypeface::Style currentStyle = SkTypeface::kNormal;
775 if (typeface) {
776 currentStyle = typeface->style();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800777 }
Raph Levienaaedde52012-10-30 15:55:33 -0700778 typeface = SkCreateTypefaceForScriptNG(script, currentStyle);
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700779#if DEBUG_GLYPHS
780 ALOGD("Using Harfbuzz Script %d, Style %d", script, currentStyle);
781#endif
Raph Levien1637dcd2012-05-02 10:41:14 -0700782 return typeface;
783}
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800784
Raph Levienaaedde52012-10-30 15:55:33 -0700785bool TextLayoutShaper::isComplexScript(hb_script_t script) {
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700786 switch (script) {
Raph Levienaaedde52012-10-30 15:55:33 -0700787 case HB_SCRIPT_COMMON:
788 case HB_SCRIPT_GREEK:
789 case HB_SCRIPT_CYRILLIC:
790 case HB_SCRIPT_HANGUL:
791 case HB_SCRIPT_INHERITED:
Junichi Monma6d191ed2013-01-17 16:13:22 +0900792 case HB_SCRIPT_HAN:
793 case HB_SCRIPT_KATAKANA:
794 case HB_SCRIPT_HIRAGANA:
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700795 return false;
796 default:
797 return true;
798 }
799}
800
Raph Levienaaedde52012-10-30 15:55:33 -0700801size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) {
Raph Levien1637dcd2012-05-02 10:41:14 -0700802 // Update Harfbuzz Shaper
Raph Levien1637dcd2012-05-02 10:41:14 -0700803
804 SkTypeface* typeface = paint->getTypeface();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800805
806 // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
807 // This is needed as the Typeface used for shaping can be not the default one
808 // when we are shaping any script that needs to use a fallback Font.
809 // If we are a "common" script we dont need to shift
810 size_t baseGlyphCount = 0;
Raph Levienaaedde52012-10-30 15:55:33 -0700811 hb_codepoint_t firstUnichar = 0;
812 if (isComplexScript(hb_buffer_get_script(mBuffer))) {
813 unsigned int numGlyphs;
814 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
815 for (size_t i = 0; i < numGlyphs; i++) {
816 firstUnichar = info[i].codepoint;
817 if (firstUnichar != ' ') {
818 break;
819 }
Raph Levienb2944352012-05-01 09:41:50 -0700820 }
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800821 baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800822 }
823
Raph Levien1637dcd2012-05-02 10:41:14 -0700824 if (baseGlyphCount != 0) {
Raph Levienaaedde52012-10-30 15:55:33 -0700825 typeface = typefaceForScript(paint, typeface, hb_buffer_get_script(mBuffer));
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700826 if (!typeface) {
827 typeface = mDefaultTypeface;
828 SkSafeRef(typeface);
829#if DEBUG_GLYPHS
830 ALOGD("Using Default Typeface");
831#endif
832 }
833 } else {
834 if (!typeface) {
835 typeface = mDefaultTypeface;
836#if DEBUG_GLYPHS
837 ALOGD("Using Default Typeface");
838#endif
839 }
840 SkSafeRef(typeface);
Raph Levien1637dcd2012-05-02 10:41:14 -0700841 }
842
Raph Levien1637dcd2012-05-02 10:41:14 -0700843 mShapingPaint.setTypeface(typeface);
Raph Levienaaedde52012-10-30 15:55:33 -0700844 hb_face_t* face = referenceCachedHBFace(typeface);
Raph Levien1637dcd2012-05-02 10:41:14 -0700845
Raph Levienaaedde52012-10-30 15:55:33 -0700846 float sizeY = paint->getTextSize();
847 float sizeX = sizeY * paint->getTextScaleX();
848 hb_font_t* font = createFont(face, &mShapingPaint, sizeX, sizeY);
849 hb_face_destroy(face);
Raph Levien2301d322012-07-17 16:39:49 -0700850
Raph Levien1637dcd2012-05-02 10:41:14 -0700851#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700852 ALOGD("Run typeface = %p, uniqueID = %d, face = %p",
853 typeface, typeface->uniqueID(), face);
Raph Levien1637dcd2012-05-02 10:41:14 -0700854#endif
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700855 SkSafeUnref(typeface);
Raph Levien1637dcd2012-05-02 10:41:14 -0700856
Raph Levienaaedde52012-10-30 15:55:33 -0700857 hb_shape(font, mBuffer, NULL, 0);
858 hb_font_destroy(font);
859
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800860 return baseGlyphCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700861}
862
Raph Levienaaedde52012-10-30 15:55:33 -0700863hb_face_t* TextLayoutShaper::referenceCachedHBFace(SkTypeface* typeface) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800864 SkFontID fontId = typeface->uniqueID();
865 ssize_t index = mCachedHBFaces.indexOfKey(fontId);
866 if (index >= 0) {
Raph Levienaaedde52012-10-30 15:55:33 -0700867 return hb_face_reference(mCachedHBFaces.valueAt(index));
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800868 }
Raph Levienaaedde52012-10-30 15:55:33 -0700869 // TODO: destroy function
870 hb_face_t* face = hb_face_create_for_tables(harfbuzzSkiaReferenceTable, typeface, NULL);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800871#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700872 ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800873#endif
Raph Levienaaedde52012-10-30 15:55:33 -0700874 mCachedHBFaces.add(fontId, face);
875 return hb_face_reference(face);
Fabrice Di Meglio79df5322011-09-19 15:17:56 -0700876}
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700877
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700878void TextLayoutShaper::purgeCaches() {
Raph Levien75394d62012-06-02 15:47:29 -0700879 size_t cacheSize = mCachedHBFaces.size();
880 for (size_t i = 0; i < cacheSize; i++) {
Raph Levienaaedde52012-10-30 15:55:33 -0700881 hb_face_destroy(mCachedHBFaces.valueAt(i));
Raph Levien75394d62012-06-02 15:47:29 -0700882 }
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700883 mCachedHBFaces.clear();
884 unrefTypefaces();
885 init();
886}
887
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800888TextLayoutEngine::TextLayoutEngine() {
889 mShaper = new TextLayoutShaper();
890#if USE_TEXT_LAYOUT_CACHE
891 mTextLayoutCache = new TextLayoutCache(mShaper);
892#else
893 mTextLayoutCache = NULL;
894#endif
895}
896
897TextLayoutEngine::~TextLayoutEngine() {
898 delete mTextLayoutCache;
899 delete mShaper;
900}
901
902sp<TextLayoutValue> TextLayoutEngine::getValue(const SkPaint* paint, const jchar* text,
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800903 jint start, jint count, jint contextCount) {
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800904 sp<TextLayoutValue> value;
905#if USE_TEXT_LAYOUT_CACHE
906 value = mTextLayoutCache->getValue(paint, text, start, count,
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800907 contextCount);
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800908 if (value == NULL) {
909 ALOGE("Cannot get TextLayoutCache value for text = '%s'",
910 String8(text + start, count).string());
911 }
912#else
913 value = new TextLayoutValue(count);
914 mShaper->computeValues(value.get(), paint,
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800915 reinterpret_cast<const UChar*>(text), start, count, contextCount);
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800916#endif
917 return value;
918}
919
Fabrice Di Meglio30ca5cd2012-05-07 17:45:44 -0700920void TextLayoutEngine::purgeCaches() {
921#if USE_TEXT_LAYOUT_CACHE
Raph Levien13ba4e42012-09-12 15:15:51 -0700922 mTextLayoutCache->purgeCaches();
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700923#if DEBUG_GLYPHS
924 ALOGD("Purged TextLayoutEngine caches");
925#endif
Fabrice Di Meglio30ca5cd2012-05-07 17:45:44 -0700926#endif
927}
928
929
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800930} // namespace android