blob: 73f3639b204025ebc5c7ece53b76f118cf487459 [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"
Billy Hewlettac1cbaf2012-07-18 09:51:45 -070023#include "SkTypeface_android.h"
Raph Levienaaedde52012-10-30 15:55:33 -070024#include "HarfBuzzNGFaceSkia.h"
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -080025#include <unicode/unistr.h>
Fabrice Di Meglio3632b7f2012-04-24 19:55:18 -070026#include <unicode/uchar.h>
Raph Levienaaedde52012-10-30 15:55:33 -070027#include <hb-icu.h>
Fabrice Di Meglio5de5b1a2011-08-09 14:37:22 -070028
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
Fabrice Di Meglioa731b082012-01-23 18:18:45 -080033ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
Fabrice Di Megliob02d0ca2011-12-08 14:05:44 -080034
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070035//--------------------------------------------------------------------------------------------------
36
Fabrice Di Meglioa731b082012-01-23 18:18:45 -080037TextLayoutCache::TextLayoutCache(TextLayoutShaper* shaper) :
38 mShaper(shaper),
Raph Leviend98efca2012-10-25 23:17:33 -070039 mCache(LruCache<TextLayoutCacheKey, sp<TextLayoutValue> >::kUnlimitedCapacity),
Fabrice Di Megliod313c662011-02-24 19:56:18 -080040 mSize(0), mMaxSize(MB(DEFAULT_TEXT_LAYOUT_CACHE_SIZE_IN_MB)),
41 mCacheHitCount(0), mNanosecondsSaved(0) {
42 init();
43}
44
Fabrice Di Megliod313c662011-02-24 19:56:18 -080045TextLayoutCache::~TextLayoutCache() {
46 mCache.clear();
47}
48
49void TextLayoutCache::init() {
50 mCache.setOnEntryRemovedListener(this);
51
52 mDebugLevel = readRtlDebugLevel();
53 mDebugEnabled = mDebugLevel & kRtlDebugCaches;
Steve Block5baa3a62011-12-20 16:23:08 +000054 ALOGD("Using debug level = %d - Debug Enabled = %d", mDebugLevel, mDebugEnabled);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080055
56 mCacheStartTime = systemTime(SYSTEM_TIME_MONOTONIC);
Fabrice Di Meglio9f82b582011-03-08 12:02:59 -080057
58 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +000059 ALOGD("Initialization is done - Start time = %lld", mCacheStartTime);
Fabrice Di Meglio9f82b582011-03-08 12:02:59 -080060 }
61
Fabrice Di Meglio163268b2011-09-07 18:12:11 -070062 mInitialized = true;
Fabrice Di Megliod313c662011-02-24 19:56:18 -080063}
64
Fabrice Di Megliod313c662011-02-24 19:56:18 -080065/**
66 * Callbacks
67 */
Fabrice Di Meglioa731b082012-01-23 18:18:45 -080068void TextLayoutCache::operator()(TextLayoutCacheKey& text, sp<TextLayoutValue>& desc) {
Jeff Brown06daa7b2011-11-11 15:14:56 -080069 size_t totalSizeToDelete = text.getSize() + desc->getSize();
70 mSize -= totalSizeToDelete;
71 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +000072 ALOGD("Cache value %p deleted, size = %d", desc.get(), totalSizeToDelete);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080073 }
74}
75
76/*
77 * Cache clearing
78 */
Raph Levien13ba4e42012-09-12 15:15:51 -070079void TextLayoutCache::purgeCaches() {
80 AutoMutex _l(mLock);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080081 mCache.clear();
Raph Levien13ba4e42012-09-12 15:15:51 -070082 mShaper->purgeCaches();
Fabrice Di Megliod313c662011-02-24 19:56:18 -080083}
84
85/*
86 * Caching
87 */
Fabrice Di Meglioa731b082012-01-23 18:18:45 -080088sp<TextLayoutValue> TextLayoutCache::getValue(const SkPaint* paint,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -070089 const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) {
Fabrice Di Megliod313c662011-02-24 19:56:18 -080090 AutoMutex _l(mLock);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080091 nsecs_t startTime = 0;
92 if (mDebugEnabled) {
93 startTime = systemTime(SYSTEM_TIME_MONOTONIC);
94 }
95
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -070096 // Create the key
Fabrice Di Meglioda12f382013-03-15 11:26:56 -070097 TextLayoutCacheKey key(paint, text, start, count, contextCount, dirFlags);
Fabrice Di Megliod313c662011-02-24 19:56:18 -080098
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -070099 // Get value from cache if possible
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800100 sp<TextLayoutValue> value = mCache.get(key);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800101
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700102 // Value not found for the key, we need to add a new value in the cache
103 if (value == NULL) {
Fabrice Di Meglio010d5c42011-04-21 15:33:50 -0700104 if (mDebugEnabled) {
105 startTime = systemTime(SYSTEM_TIME_MONOTONIC);
106 }
107
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800108 value = new TextLayoutValue(contextCount);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800109
110 // Compute advances and store them
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800111 mShaper->computeValues(value.get(), paint,
Raph Levien832815c2012-10-02 10:30:41 -0700112 reinterpret_cast<const UChar*>(key.getText()), start, count,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700113 size_t(contextCount), int(dirFlags));
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800114
Jeff Brown06daa7b2011-11-11 15:14:56 -0800115 if (mDebugEnabled) {
116 value->setElapsedTime(systemTime(SYSTEM_TIME_MONOTONIC) - startTime);
117 }
Fabrice Di Meglio010d5c42011-04-21 15:33:50 -0700118
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800119 // Don't bother to add in the cache if the entry is too big
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700120 size_t size = key.getSize() + value->getSize();
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800121 if (size <= mMaxSize) {
122 // Cleanup to make some room if needed
123 if (mSize + size > mMaxSize) {
124 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000125 ALOGD("Need to clean some entries for making some room for a new entry");
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800126 }
127 while (mSize + size > mMaxSize) {
128 // This will call the callback
Jeff Brownd9e688c2011-11-11 15:40:13 -0800129 bool removedOne = mCache.removeOldest();
Jeff Brown06daa7b2011-11-11 15:14:56 -0800130 LOG_ALWAYS_FATAL_IF(!removedOne, "The cache is non-empty but we "
131 "failed to remove the oldest entry. "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800132 "mSize = %u, size = %u, mMaxSize = %u, mCache.size() = %u",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800133 mSize, size, mMaxSize, mCache.size());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800134 }
135 }
136
137 // Update current cache size
138 mSize += size;
139
Jeff Brown06daa7b2011-11-11 15:14:56 -0800140 bool putOne = mCache.put(key, value);
141 LOG_ALWAYS_FATAL_IF(!putOne, "Failed to put an entry into the cache. "
142 "This indicates that the cache already has an entry with the "
143 "same key but it should not since we checked earlier!"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800144 " - start = %d, count = %d, contextCount = %d - Text = '%s'",
Raph Levien832815c2012-10-02 10:30:41 -0700145 start, count, contextCount, String8(key.getText() + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800146
147 if (mDebugEnabled) {
Jeff Brown06daa7b2011-11-11 15:14:56 -0800148 nsecs_t totalTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
Steve Block5baa3a62011-12-20 16:23:08 +0000149 ALOGD("CACHE MISS: Added entry %p "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800150 "with start = %d, count = %d, contextCount = %d, "
Jeff Brown06daa7b2011-11-11 15:14:56 -0800151 "entry size %d bytes, remaining space %d bytes"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800152 " - Compute time %0.6f ms - Put time %0.6f ms - Text = '%s'",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800153 value.get(), start, count, contextCount, size, mMaxSize - mSize,
154 value->getElapsedTime() * 0.000001f,
155 (totalTime - value->getElapsedTime()) * 0.000001f,
Raph Levien832815c2012-10-02 10:30:41 -0700156 String8(key.getText() + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800157 }
158 } else {
159 if (mDebugEnabled) {
Steve Block5baa3a62011-12-20 16:23:08 +0000160 ALOGD("CACHE MISS: Calculated but not storing entry because it is too big "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800161 "with start = %d, count = %d, contextCount = %d, "
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800162 "entry size %d bytes, remaining space %d bytes"
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800163 " - Compute time %0.6f ms - Text = '%s'",
Jeff Brown06daa7b2011-11-11 15:14:56 -0800164 start, count, contextCount, size, mMaxSize - mSize,
165 value->getElapsedTime() * 0.000001f,
Raph Levien832815c2012-10-02 10:30:41 -0700166 String8(key.getText() + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800167 }
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800168 }
169 } else {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700170 // This is a cache hit, just log timestamp and user infos
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800171 if (mDebugEnabled) {
172 nsecs_t elapsedTimeThruCacheGet = systemTime(SYSTEM_TIME_MONOTONIC) - startTime;
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700173 mNanosecondsSaved += (value->getElapsedTime() - elapsedTimeThruCacheGet);
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800174 ++mCacheHitCount;
175
Fabrice Di Meglio1de9e7a2011-04-05 13:43:18 -0700176 if (value->getElapsedTime() > 0) {
177 float deltaPercent = 100 * ((value->getElapsedTime() - elapsedTimeThruCacheGet)
178 / ((float)value->getElapsedTime()));
Steve Block5baa3a62011-12-20 16:23:08 +0000179 ALOGD("CACHE HIT #%d with start = %d, count = %d, contextCount = %d"
Jeff Brown06daa7b2011-11-11 15:14:56 -0800180 "- Compute time %0.6f ms - "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800181 "Cache get time %0.6f ms - Gain in percent: %2.2f - Text = '%s'",
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700182 mCacheHitCount, start, count, contextCount,
Jeff Brown06daa7b2011-11-11 15:14:56 -0800183 value->getElapsedTime() * 0.000001f,
184 elapsedTimeThruCacheGet * 0.000001f,
185 deltaPercent,
Raph Levien832815c2012-10-02 10:30:41 -0700186 String8(key.getText() + start, count).string());
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800187 }
188 if (mCacheHitCount % DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL == 0) {
189 dumpCacheStats();
190 }
191 }
192 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700193 return value;
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800194}
195
196void TextLayoutCache::dumpCacheStats() {
197 float remainingPercent = 100 * ((mMaxSize - mSize) / ((float)mMaxSize));
198 float timeRunningInSec = (systemTime(SYSTEM_TIME_MONOTONIC) - mCacheStartTime) / 1000000000;
Jeff Brown06daa7b2011-11-11 15:14:56 -0800199
Jeff Brown06daa7b2011-11-11 15:14:56 -0800200 size_t cacheSize = mCache.size();
Jeff Brown06daa7b2011-11-11 15:14:56 -0800201
Steve Block5baa3a62011-12-20 16:23:08 +0000202 ALOGD("------------------------------------------------");
203 ALOGD("Cache stats");
204 ALOGD("------------------------------------------------");
205 ALOGD("pid : %d", getpid());
206 ALOGD("running : %.0f seconds", timeRunningInSec);
207 ALOGD("entries : %d", cacheSize);
208 ALOGD("max size : %d bytes", mMaxSize);
Raph Leviend98efca2012-10-25 23:17:33 -0700209 ALOGD("used : %d bytes according to mSize", mSize);
Steve Block5baa3a62011-12-20 16:23:08 +0000210 ALOGD("remaining : %d bytes or %2.2f percent", mMaxSize - mSize, remainingPercent);
211 ALOGD("hits : %d", mCacheHitCount);
212 ALOGD("saved : %0.6f ms", mNanosecondsSaved * 0.000001f);
213 ALOGD("------------------------------------------------");
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800214}
215
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700216/**
217 * TextLayoutCacheKey
218 */
Raph Levien832815c2012-10-02 10:30:41 -0700219TextLayoutCacheKey::TextLayoutCacheKey(): start(0), count(0), contextCount(0),
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700220 dirFlags(0), typeface(NULL), textSize(0), textSkewX(0), textScaleX(0), flags(0),
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400221 hinting(SkPaint::kNo_Hinting) {
222 paintOpts.setUseFontFallbacks(true);
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 Meglioda12f382013-03-15 11:26:56 -0700226 size_t start, size_t count, size_t contextCount, int dirFlags) :
227 start(start), count(count), contextCount(contextCount),
228 dirFlags(dirFlags) {
Raph Levien832815c2012-10-02 10:30:41 -0700229 textCopy.setTo(text, contextCount);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700230 typeface = paint->getTypeface();
231 textSize = paint->getTextSize();
232 textSkewX = paint->getTextSkewX();
233 textScaleX = paint->getTextScaleX();
234 flags = paint->getFlags();
235 hinting = paint->getHinting();
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400236 paintOpts = paint->getPaintOptionsAndroid();
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 Meglioda12f382013-03-15 11:26:56 -0700244 dirFlags(other.dirFlags),
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700245 typeface(other.typeface),
246 textSize(other.textSize),
247 textSkewX(other.textSkewX),
248 textScaleX(other.textScaleX),
249 flags(other.flags),
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700250 hinting(other.hinting),
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400251 paintOpts(other.paintOpts) {
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
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700282 deltaInt = lhs.dirFlags - rhs.dirFlags;
283 if (deltaInt) return (deltaInt);
284
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400285 if (lhs.paintOpts != rhs.paintOpts)
286 return memcmp(&lhs.paintOpts, &rhs.paintOpts, sizeof(SkPaintOptionsAndroid));
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700287
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);
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400305 hash = JenkinsHashMix(hash, paintOpts.getFontVariant());
Raph Leviend98efca2012-10-25 23:17:33 -0700306 // 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) {
Chris Craik41541822013-05-03 16:35:54 -0700317 mBounds.setEmpty();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800318 // Give a hint for advances and glyphs vectors size
319 mAdvances.setCapacity(contextCount);
320 mGlyphs.setCapacity(contextCount);
Raph Levien2301d322012-07-17 16:39:49 -0700321 mPos.setCapacity(contextCount * 2);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800322}
323
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800324size_t TextLayoutValue::getSize() const {
325 return sizeof(TextLayoutValue) + sizeof(jfloat) * mAdvances.capacity() +
Raph Levien2301d322012-07-17 16:39:49 -0700326 sizeof(jchar) * mGlyphs.capacity() + sizeof(jfloat) * mPos.capacity();
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700327}
328
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800329void TextLayoutValue::setElapsedTime(uint32_t time) {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700330 mElapsedTime = time;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700331}
332
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800333uint32_t TextLayoutValue::getElapsedTime() {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700334 return mElapsedTime;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700335}
336
Raph Levienaaedde52012-10-30 15:55:33 -0700337TextLayoutShaper::TextLayoutShaper() {
Raph Levienaaedde52012-10-30 15:55:33 -0700338 mBuffer = hb_buffer_create();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800339}
340
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700341TextLayoutShaper::~TextLayoutShaper() {
Raph Levienaaedde52012-10-30 15:55:33 -0700342 hb_buffer_destroy(mBuffer);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800343}
344
Chris Craik41541822013-05-03 16:35:54 -0700345void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint,
346 const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) {
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700347 computeValues(paint, chars, start, count, contextCount, dirFlags,
Chris Craik41541822013-05-03 16:35:54 -0700348 &value->mAdvances, &value->mTotalAdvance, &value->mBounds,
349 &value->mGlyphs, &value->mPos);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700350#if DEBUG_ADVANCES
Steve Block5baa3a62011-12-20 16:23:08 +0000351 ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800352 contextCount, value->mTotalAdvance);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700353#endif
354}
355
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800356void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700357 size_t start, size_t count, size_t contextCount, int dirFlags,
Chris Craik41541822013-05-03 16:35:54 -0700358 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, SkRect* outBounds,
Raph Levien2301d322012-07-17 16:39:49 -0700359 Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
360 *outTotalAdvance = 0;
Jeff Browna03bded2011-12-05 17:36:16 -0800361 if (!count) {
Jeff Browna03bded2011-12-05 17:36:16 -0800362 return;
363 }
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700364
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700365 UBiDiLevel bidiReq = 0;
366 bool forceLTR = false;
367 bool forceRTL = false;
368
369 switch (dirFlags & kBidi_Mask) {
370 case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
371 case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
372 case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
373 case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
374 case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR
375 case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
376 }
377
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700378 bool useSingleRun = false;
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700379 bool isRTL = forceRTL;
380 if (forceLTR || forceRTL) {
381 useSingleRun = true;
382 } else {
383 UBiDi* bidi = ubidi_open();
384 if (bidi) {
385 UErrorCode status = U_ZERO_ERROR;
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700386#if DEBUG_GLYPHS
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700387 ALOGD("******** ComputeValues -- start");
388 ALOGD(" -- string = '%s'", String8(chars + start, count).string());
389 ALOGD(" -- start = %d", start);
390 ALOGD(" -- count = %d", count);
391 ALOGD(" -- contextCount = %d", contextCount);
392 ALOGD(" -- bidiReq = %d", bidiReq);
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700393#endif
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700394 ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
395 if (U_SUCCESS(status)) {
396 int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
397 ssize_t rc = ubidi_countRuns(bidi, &status);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700398#if DEBUG_GLYPHS
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700399 ALOGD(" -- dirFlags = %d", dirFlags);
400 ALOGD(" -- paraDir = %d", paraDir);
401 ALOGD(" -- run-count = %d", int(rc));
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700402#endif
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700403 if (U_SUCCESS(status) && rc == 1) {
404 // Normal case: one run, status is ok
405 isRTL = (paraDir == 1);
406 useSingleRun = true;
407 } else if (!U_SUCCESS(status) || rc < 1) {
408 ALOGW("Need to force to single run -- string = '%s',"
409 " status = %d, rc = %d",
410 String8(chars + start, count).string(), status, int(rc));
411 isRTL = (paraDir == 1);
412 useSingleRun = true;
413 } else {
414 int32_t end = start + count;
415 for (size_t i = 0; i < size_t(rc); ++i) {
416 int32_t startRun = -1;
417 int32_t lengthRun = -1;
418 UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800419
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700420 if (startRun == -1 || lengthRun == -1) {
421 // Something went wrong when getting the visual run, need to clear
422 // already computed data before doing a single run pass
423 ALOGW("Visual run is not valid");
424 outGlyphs->clear();
425 outAdvances->clear();
426 outPos->clear();
427 *outTotalAdvance = 0;
428 isRTL = (paraDir == 1);
429 useSingleRun = true;
430 break;
431 }
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800432
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700433 if (startRun >= end) {
434 continue;
435 }
436 int32_t endRun = startRun + lengthRun;
437 if (endRun <= int32_t(start)) {
438 continue;
439 }
440 if (startRun < int32_t(start)) {
441 startRun = int32_t(start);
442 }
443 if (endRun > end) {
444 endRun = end;
445 }
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800446
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700447 lengthRun = endRun - startRun;
448 isRTL = (runDir == UBIDI_RTL);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800449#if DEBUG_GLYPHS
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700450 ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
451 i, startRun, lengthRun, isRTL);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800452#endif
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700453 computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
Chris Craik41541822013-05-03 16:35:54 -0700454 outAdvances, outTotalAdvance, outBounds, outGlyphs, outPos);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800455
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700456 }
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800457 }
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700458 } else {
459 ALOGW("Cannot set Para");
460 useSingleRun = true;
461 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700462 }
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700463 ubidi_close(bidi);
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700464 } else {
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700465 ALOGW("Cannot ubidi_open()");
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700466 useSingleRun = true;
467 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700468 }
469 }
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700470
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700471 // Default single run case
472 if (useSingleRun){
473#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000474 ALOGD("Using a SINGLE BiDi Run "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800475 "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700476#endif
Raph Levienaaedde52012-10-30 15:55:33 -0700477 computeRunValues(paint, chars, start, count, contextCount, isRTL,
Chris Craik41541822013-05-03 16:35:54 -0700478 outAdvances, outTotalAdvance, outBounds, outGlyphs, outPos);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700479 }
480
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700481#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000482 ALOGD(" -- Total returned glyphs-count = %d", outGlyphs->size());
483 ALOGD("******** ComputeValues -- end");
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700484#endif
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700485}
486
Raph Levienaaedde52012-10-30 15:55:33 -0700487#define HB_IsHighSurrogate(ucs) \
488 (((ucs) & 0xfc00) == 0xd800)
489
490#define HB_IsLowSurrogate(ucs) \
491 (((ucs) & 0xfc00) == 0xdc00)
492
493#ifndef HB_SurrogateToUcs4
494#define HB_SurrogateToUcs4_(high, low) \
495 (((hb_codepoint_t)(high))<<10) + (low) - 0x35fdc00;
496#endif
497
498#define HB_InvalidCodePoint ~0u
499
500hb_codepoint_t
501utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) {
502 const uint16_t v = chars[(*iter)++];
503 if (HB_IsHighSurrogate(v)) {
504 // surrogate pair
505 if (size_t(*iter) >= len) {
506 // the surrogate is incomplete.
507 return HB_InvalidCodePoint;
508 }
509 const uint16_t v2 = chars[(*iter)++];
510 if (!HB_IsLowSurrogate(v2)) {
511 // invalidate surrogate pair.
512 (*iter)--;
513 return HB_InvalidCodePoint;
514 }
515
516 return HB_SurrogateToUcs4(v, v2);
517 }
518
519 if (HB_IsLowSurrogate(v)) {
520 // this isn't a valid code point
521 return HB_InvalidCodePoint;
522 }
523
524 return v;
525}
526
527hb_codepoint_t
528utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) {
529 const uint16_t v = chars[(*iter)--];
530 if (HB_IsLowSurrogate(v)) {
531 // surrogate pair
532 if (*iter < 0) {
533 // the surrogate is incomplete.
534 return HB_InvalidCodePoint;
535 }
536 const uint16_t v2 = chars[(*iter)--];
537 if (!HB_IsHighSurrogate(v2)) {
538 // invalidate surrogate pair.
539 (*iter)++;
540 return HB_InvalidCodePoint;
541 }
542
543 return HB_SurrogateToUcs4(v2, v);
544 }
545
546 if (HB_IsHighSurrogate(v)) {
547 // this isn't a valid code point
548 return HB_InvalidCodePoint;
549 }
550
551 return v;
552}
553
554struct ScriptRun {
555 hb_script_t script;
556 size_t pos;
557 size_t length;
558};
559
560hb_script_t code_point_to_script(hb_codepoint_t codepoint) {
561 static hb_unicode_funcs_t* u;
562 if (!u) {
563 u = hb_icu_get_unicode_funcs();
564 }
565 return hb_unicode_script(u, codepoint);
566}
567
568bool
569hb_utf16_script_run_next(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
570 if (size_t(*iter) == len)
571 return false;
572
573 run->pos = *iter;
574 const uint32_t init_cp = utf16_to_code_point(chars, len, iter);
575 const hb_script_t init_script = code_point_to_script(init_cp);
576 hb_script_t current_script = init_script;
577 run->script = init_script;
578
579 for (;;) {
580 if (size_t(*iter) == len)
581 break;
582 const ssize_t prev_iter = *iter;
583 const uint32_t cp = utf16_to_code_point(chars, len, iter);
584 const hb_script_t script = code_point_to_script(cp);
585
586 if (script != current_script) {
587 /* BEGIN android-changed
588 The condition was not correct by doing "a == b == constant"
589 END android-changed */
590 if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
591 // If we started off as inherited, we take whatever we can find.
592 run->script = script;
593 current_script = script;
594 continue;
595 } else if (script == HB_SCRIPT_INHERITED) {
596 continue;
597 } else {
598 *iter = prev_iter;
599 break;
600 }
601 }
602 }
603
604 if (run->script == HB_SCRIPT_INHERITED)
605 run->script = HB_SCRIPT_COMMON;
606
607 run->length = *iter - run->pos;
608 return true;
609}
610
611bool
612hb_utf16_script_run_prev(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
613 if (*iter == -1)
614 return false;
615
616 const size_t ending_index = *iter;
617 const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter);
618 const hb_script_t init_script = code_point_to_script(init_cp);
619 hb_script_t current_script = init_script;
620 run->script = init_script;
Raph Levien9d47db22013-04-23 12:42:16 -0700621 size_t break_iter = *iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700622
623 for (;;) {
624 if (*iter < 0)
625 break;
Raph Levienaaedde52012-10-30 15:55:33 -0700626 const uint32_t cp = utf16_to_code_point_prev(chars, len, iter);
627 const hb_script_t script = code_point_to_script(cp);
628
629 if (script != current_script) {
630 if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
631 // If we started off as inherited, we take whatever we can find.
632 run->script = script;
633 current_script = script;
Raph Levien9d47db22013-04-23 12:42:16 -0700634 // In cases of script1 + inherited + script2, always group the inherited
635 // with script1.
636 break_iter = *iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700637 continue;
638 } else if (script == HB_SCRIPT_INHERITED) {
Raph Levienaaedde52012-10-30 15:55:33 -0700639 continue;
640 } else {
Raph Levien9d47db22013-04-23 12:42:16 -0700641 *iter = break_iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700642 break;
643 }
Raph Levien9d47db22013-04-23 12:42:16 -0700644 } else {
645 break_iter = *iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700646 }
647 }
648
649 if (run->script == HB_SCRIPT_INHERITED)
650 run->script = HB_SCRIPT_COMMON;
651
652 run->pos = *iter + 1;
653 run->length = ending_index - *iter;
654 return true;
655}
656
657
658static void logGlyphs(hb_buffer_t* buffer) {
659 unsigned int numGlyphs;
660 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
661 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
662 ALOGD(" -- glyphs count=%d", numGlyphs);
663 for (size_t i = 0; i < numGlyphs; i++) {
664 ALOGD(" -- glyph[%d] = %d, cluster = %u, advance = %0.2f, offset.x = %0.2f, offset.y = %0.2f", i,
665 info[i].codepoint,
666 info[i].cluster,
667 HBFixedToFloat(positions[i].x_advance),
668 HBFixedToFloat(positions[i].x_offset),
669 HBFixedToFloat(positions[i].y_offset));
Fabrice Di Meglioabb0f292011-08-18 17:59:29 -0700670 }
671}
672
Raph Levienaaedde52012-10-30 15:55:33 -0700673void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars,
674 size_t start, size_t count, size_t contextCount, bool isRTL,
Chris Craik41541822013-05-03 16:35:54 -0700675 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, SkRect* outBounds,
Raph Levien2301d322012-07-17 16:39:49 -0700676 Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
Jeff Browna03bded2011-12-05 17:36:16 -0800677 if (!count) {
678 // We cannot shape an empty run.
Jeff Browna03bded2011-12-05 17:36:16 -0800679 return;
680 }
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700681
Raph Levien57e97232012-04-24 16:04:34 -0700682 // To be filled in later
683 for (size_t i = 0; i < count; i++) {
684 outAdvances->add(0);
685 }
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800686
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800687 // Set the string properties
Raph Levienaaedde52012-10-30 15:55:33 -0700688 const UChar* chars = contextChars + start;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700689
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800690 // Define shaping paint properties
691 mShapingPaint.setTextSize(paint->getTextSize());
Raph Levien2301d322012-07-17 16:39:49 -0700692 float skewX = paint->getTextSkewX();
693 mShapingPaint.setTextSkewX(skewX);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800694 mShapingPaint.setTextScaleX(paint->getTextScaleX());
695 mShapingPaint.setFlags(paint->getFlags());
696 mShapingPaint.setHinting(paint->getHinting());
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400697 mShapingPaint.setPaintOptionsAndroid(paint->getPaintOptionsAndroid());
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800698
699 // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
700 // into the shaperItem
Raph Levienaaedde52012-10-30 15:55:33 -0700701 ssize_t indexFontRun = isRTL ? count - 1 : 0;
Raph Levien2301d322012-07-17 16:39:49 -0700702 jfloat totalAdvance = *outTotalAdvance;
Raph Levienaaedde52012-10-30 15:55:33 -0700703 ScriptRun run; // relative to chars
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800704 while ((isRTL) ?
Raph Levienaaedde52012-10-30 15:55:33 -0700705 hb_utf16_script_run_prev(&run, chars, count, &indexFontRun):
706 hb_utf16_script_run_next(&run, chars, count, &indexFontRun)) {
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700707
Fabrice Di Meglio9c418db2011-09-18 12:54:38 -0700708#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000709 ALOGD("-------- Start of Script Run --------");
710 ALOGD("Shaping Script Run with");
711 ALOGD(" -- isRTL = %d", isRTL);
Raph Levienaaedde52012-10-30 15:55:33 -0700712 ALOGD(" -- HB script = %c%c%c%c", HB_UNTAG(run.script));
713 ALOGD(" -- run.pos = %d", int(run.pos));
714 ALOGD(" -- run.length = %d", int(run.length));
715 ALOGD(" -- run = '%s'", String8(chars + run.pos, run.length).string());
Steve Block5baa3a62011-12-20 16:23:08 +0000716 ALOGD(" -- string = '%s'", String8(chars, count).string());
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700717#endif
718
Raph Levienaaedde52012-10-30 15:55:33 -0700719 hb_buffer_reset(mBuffer);
720 // Note: if we want to set unicode functions, etc., this is the place.
721
722 hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
723 hb_buffer_set_script(mBuffer, run.script);
724 // Should set language here (for bug 7004056)
725 hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length);
726
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800727 // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
728 // and shape the Font run
Raph Levienaaedde52012-10-30 15:55:33 -0700729 size_t glyphBaseCount = shapeFontRun(paint);
730 unsigned int numGlyphs;
731 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
732 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(mBuffer, NULL);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700733
Fabrice Di Meglio78b868f2011-05-17 19:48:57 -0700734#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000735 ALOGD("Got from Harfbuzz");
736 ALOGD(" -- glyphBaseCount = %d", glyphBaseCount);
Raph Levienaaedde52012-10-30 15:55:33 -0700737 ALOGD(" -- num_glyph = %d", numGlyphs);
Steve Block5baa3a62011-12-20 16:23:08 +0000738 ALOGD(" -- isDevKernText = %d", paint->isDevKernText());
Raph Levienaaedde52012-10-30 15:55:33 -0700739 ALOGD(" -- initial totalAdvance = %f", totalAdvance);
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700740
Raph Levienaaedde52012-10-30 15:55:33 -0700741 logGlyphs(mBuffer);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700742#endif
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700743
Raph Levienaaedde52012-10-30 15:55:33 -0700744 for (size_t i = 0; i < numGlyphs; i++) {
745 size_t cluster = info[i].cluster - start;
746 float xAdvance = HBFixedToFloat(positions[i].x_advance);
747 outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster);
Chris Craik41541822013-05-03 16:35:54 -0700748 jchar glyphId = info[i].codepoint + glyphBaseCount;
749 outGlyphs->add(glyphId);
Raph Levienaaedde52012-10-30 15:55:33 -0700750 float xo = HBFixedToFloat(positions[i].x_offset);
751 float yo = -HBFixedToFloat(positions[i].y_offset);
Chris Craik41541822013-05-03 16:35:54 -0700752
753 float xpos = totalAdvance + xo + yo * skewX;
754 float ypos = yo;
755 outPos->add(xpos);
756 outPos->add(ypos);
Raph Levienaaedde52012-10-30 15:55:33 -0700757 totalAdvance += xAdvance;
Chris Craik41541822013-05-03 16:35:54 -0700758
759 // TODO: consider using glyph cache
760 const SkGlyph& metrics = mShapingPaint.getGlyphMetrics(glyphId, NULL);
761 outBounds->join(xpos + metrics.fLeft, ypos + metrics.fTop,
762 xpos + metrics.fLeft + metrics.fWidth, ypos + metrics.fTop + metrics.fHeight);
763
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700764 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700765 }
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800766
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700767 *outTotalAdvance = totalAdvance;
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800768
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800769#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700770 ALOGD(" -- final totalAdvance = %f", totalAdvance);
Steve Block5baa3a62011-12-20 16:23:08 +0000771 ALOGD("-------- End of Script Run --------");
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800772#endif
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700773}
774
Raph Levien1637dcd2012-05-02 10:41:14 -0700775/**
776 * Return the first typeface in the logical change, starting with this typeface,
777 * that contains the specified unichar, or NULL if none is found.
Raph Levien1637dcd2012-05-02 10:41:14 -0700778 */
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700779SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
Raph Levienaaedde52012-10-30 15:55:33 -0700780 hb_script_t script) {
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700781 SkTypeface::Style currentStyle = SkTypeface::kNormal;
782 if (typeface) {
783 currentStyle = typeface->style();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800784 }
Raph Levienaaedde52012-10-30 15:55:33 -0700785 typeface = SkCreateTypefaceForScriptNG(script, currentStyle);
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700786#if DEBUG_GLYPHS
Victoria Leaseded5ed92013-02-12 13:36:22 -0800787 ALOGD("Using Harfbuzz Script %c%c%c%c, Style %d", HB_UNTAG(script), currentStyle);
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700788#endif
Raph Levien1637dcd2012-05-02 10:41:14 -0700789 return typeface;
790}
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800791
Raph Levienaaedde52012-10-30 15:55:33 -0700792bool TextLayoutShaper::isComplexScript(hb_script_t script) {
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700793 switch (script) {
Raph Levienaaedde52012-10-30 15:55:33 -0700794 case HB_SCRIPT_COMMON:
795 case HB_SCRIPT_GREEK:
796 case HB_SCRIPT_CYRILLIC:
797 case HB_SCRIPT_HANGUL:
798 case HB_SCRIPT_INHERITED:
Junichi Monma6d191ed2013-01-17 16:13:22 +0900799 case HB_SCRIPT_HAN:
800 case HB_SCRIPT_KATAKANA:
801 case HB_SCRIPT_HIRAGANA:
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700802 return false;
803 default:
804 return true;
805 }
806}
807
Raph Levienaaedde52012-10-30 15:55:33 -0700808size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) {
Raph Levien1637dcd2012-05-02 10:41:14 -0700809 // Update Harfbuzz Shaper
Raph Levien1637dcd2012-05-02 10:41:14 -0700810
811 SkTypeface* typeface = paint->getTypeface();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800812
813 // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
814 // This is needed as the Typeface used for shaping can be not the default one
815 // when we are shaping any script that needs to use a fallback Font.
816 // If we are a "common" script we dont need to shift
817 size_t baseGlyphCount = 0;
Raph Levienaaedde52012-10-30 15:55:33 -0700818 hb_codepoint_t firstUnichar = 0;
819 if (isComplexScript(hb_buffer_get_script(mBuffer))) {
820 unsigned int numGlyphs;
821 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
822 for (size_t i = 0; i < numGlyphs; i++) {
823 firstUnichar = info[i].codepoint;
824 if (firstUnichar != ' ') {
825 break;
826 }
Raph Levienb2944352012-05-01 09:41:50 -0700827 }
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800828 baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800829 }
830
Raph Leviendd0a9122013-05-06 15:00:41 -0700831 SkTypeface* scriptTypeface = NULL;
Raph Levien1637dcd2012-05-02 10:41:14 -0700832 if (baseGlyphCount != 0) {
Raph Leviendd0a9122013-05-06 15:00:41 -0700833 scriptTypeface = typefaceForScript(paint, typeface,
834 hb_buffer_get_script(mBuffer));
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700835#if DEBUG_GLYPHS
Raph Leviendd0a9122013-05-06 15:00:41 -0700836 ALOGD("Using Default Typeface for script %c%c%c%c",
837 HB_UNTAG(hb_buffer_get_script(mBuffer)));
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700838#endif
Raph Leviendd0a9122013-05-06 15:00:41 -0700839 }
840 if (scriptTypeface) {
841 typeface = scriptTypeface;
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700842 } else {
Raph Leviendd0a9122013-05-06 15:00:41 -0700843 baseGlyphCount = 0;
844 if (typeface) {
845 SkSafeRef(typeface);
846 } else {
Victoria Leaseaf1653a2013-04-17 10:20:23 -0700847 typeface = SkTypeface::CreateFromName(NULL, SkTypeface::kNormal);
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700848#if DEBUG_GLYPHS
Victoria Leasecc0f9d82013-04-15 15:29:36 -0700849 ALOGD("Using Default Typeface (normal style)");
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700850#endif
851 }
Raph Levien1637dcd2012-05-02 10:41:14 -0700852 }
853
Raph Levien1637dcd2012-05-02 10:41:14 -0700854 mShapingPaint.setTypeface(typeface);
Raph Levienaaedde52012-10-30 15:55:33 -0700855 hb_face_t* face = referenceCachedHBFace(typeface);
Raph Levien1637dcd2012-05-02 10:41:14 -0700856
Raph Levienaaedde52012-10-30 15:55:33 -0700857 float sizeY = paint->getTextSize();
858 float sizeX = sizeY * paint->getTextScaleX();
859 hb_font_t* font = createFont(face, &mShapingPaint, sizeX, sizeY);
860 hb_face_destroy(face);
Raph Levien2301d322012-07-17 16:39:49 -0700861
Raph Levien1637dcd2012-05-02 10:41:14 -0700862#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700863 ALOGD("Run typeface = %p, uniqueID = %d, face = %p",
864 typeface, typeface->uniqueID(), face);
Raph Levien1637dcd2012-05-02 10:41:14 -0700865#endif
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700866 SkSafeUnref(typeface);
Raph Levien1637dcd2012-05-02 10:41:14 -0700867
Raph Levienaaedde52012-10-30 15:55:33 -0700868 hb_shape(font, mBuffer, NULL, 0);
869 hb_font_destroy(font);
870
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800871 return baseGlyphCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700872}
873
Raph Levienaaedde52012-10-30 15:55:33 -0700874hb_face_t* TextLayoutShaper::referenceCachedHBFace(SkTypeface* typeface) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800875 SkFontID fontId = typeface->uniqueID();
876 ssize_t index = mCachedHBFaces.indexOfKey(fontId);
877 if (index >= 0) {
Raph Levienaaedde52012-10-30 15:55:33 -0700878 return hb_face_reference(mCachedHBFaces.valueAt(index));
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800879 }
Raph Levienaaedde52012-10-30 15:55:33 -0700880 // TODO: destroy function
881 hb_face_t* face = hb_face_create_for_tables(harfbuzzSkiaReferenceTable, typeface, NULL);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800882#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700883 ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800884#endif
Raph Levienaaedde52012-10-30 15:55:33 -0700885 mCachedHBFaces.add(fontId, face);
886 return hb_face_reference(face);
Fabrice Di Meglio79df5322011-09-19 15:17:56 -0700887}
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700888
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700889void TextLayoutShaper::purgeCaches() {
Raph Levien75394d62012-06-02 15:47:29 -0700890 size_t cacheSize = mCachedHBFaces.size();
891 for (size_t i = 0; i < cacheSize; i++) {
Raph Levienaaedde52012-10-30 15:55:33 -0700892 hb_face_destroy(mCachedHBFaces.valueAt(i));
Raph Levien75394d62012-06-02 15:47:29 -0700893 }
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700894 mCachedHBFaces.clear();
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700895}
896
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800897TextLayoutEngine::TextLayoutEngine() {
898 mShaper = new TextLayoutShaper();
899#if USE_TEXT_LAYOUT_CACHE
900 mTextLayoutCache = new TextLayoutCache(mShaper);
901#else
902 mTextLayoutCache = NULL;
903#endif
904}
905
906TextLayoutEngine::~TextLayoutEngine() {
907 delete mTextLayoutCache;
908 delete mShaper;
909}
910
911sp<TextLayoutValue> TextLayoutEngine::getValue(const SkPaint* paint, const jchar* text,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700912 jint start, jint count, jint contextCount, jint dirFlags) {
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800913 sp<TextLayoutValue> value;
914#if USE_TEXT_LAYOUT_CACHE
915 value = mTextLayoutCache->getValue(paint, text, start, count,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700916 contextCount, dirFlags);
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800917 if (value == NULL) {
918 ALOGE("Cannot get TextLayoutCache value for text = '%s'",
919 String8(text + start, count).string());
920 }
921#else
922 value = new TextLayoutValue(count);
923 mShaper->computeValues(value.get(), paint,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700924 reinterpret_cast<const UChar*>(text), start, count, contextCount, dirFlags);
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800925#endif
926 return value;
927}
928
Fabrice Di Meglio30ca5cd2012-05-07 17:45:44 -0700929void TextLayoutEngine::purgeCaches() {
930#if USE_TEXT_LAYOUT_CACHE
Raph Levien13ba4e42012-09-12 15:15:51 -0700931 mTextLayoutCache->purgeCaches();
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700932#if DEBUG_GLYPHS
933 ALOGD("Purged TextLayoutEngine caches");
934#endif
Fabrice Di Meglio30ca5cd2012-05-07 17:45:44 -0700935#endif
936}
937
938
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800939} // namespace android