blob: b2cf9c1674f126ca75497682bcfcd2564a63d7dd [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 Meglioda12f382013-03-15 11:26:56 -070090 const jchar* text, jint start, jint count, jint contextCount, jint dirFlags) {
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 Meglioda12f382013-03-15 11:26:56 -070098 TextLayoutCacheKey key(paint, text, start, count, contextCount, dirFlags);
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 Meglioda12f382013-03-15 11:26:56 -0700114 size_t(contextCount), int(dirFlags));
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 Meglioda12f382013-03-15 11:26:56 -0700221 dirFlags(0), 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 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();
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700236 variant = paint->getFontVariant();
237 language = paint->getLanguage();
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700238}
239
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700240TextLayoutCacheKey::TextLayoutCacheKey(const TextLayoutCacheKey& other) :
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700241 textCopy(other.textCopy),
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700242 start(other.start),
Jeff Brown7aac2972011-09-19 16:17:58 -0700243 count(other.count),
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700244 contextCount(other.contextCount),
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700245 dirFlags(other.dirFlags),
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700246 typeface(other.typeface),
247 textSize(other.textSize),
248 textSkewX(other.textSkewX),
249 textScaleX(other.textScaleX),
250 flags(other.flags),
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700251 hinting(other.hinting),
252 variant(other.variant),
253 language(other.language) {
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700254}
255
Fabrice Di Meglio717060b2011-09-27 15:53:42 -0700256int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700257 int deltaInt = lhs.start - rhs.start;
258 if (deltaInt != 0) return (deltaInt);
259
260 deltaInt = lhs.count - rhs.count;
261 if (deltaInt != 0) return (deltaInt);
262
263 deltaInt = lhs.contextCount - rhs.contextCount;
Fabrice Di Meglio717060b2011-09-27 15:53:42 -0700264 if (deltaInt != 0) return (deltaInt);
265
266 if (lhs.typeface < rhs.typeface) return -1;
267 if (lhs.typeface > rhs.typeface) return +1;
268
269 if (lhs.textSize < rhs.textSize) return -1;
270 if (lhs.textSize > rhs.textSize) return +1;
271
272 if (lhs.textSkewX < rhs.textSkewX) return -1;
273 if (lhs.textSkewX > rhs.textSkewX) return +1;
274
275 if (lhs.textScaleX < rhs.textScaleX) return -1;
276 if (lhs.textScaleX > rhs.textScaleX) return +1;
277
278 deltaInt = lhs.flags - rhs.flags;
279 if (deltaInt != 0) return (deltaInt);
280
281 deltaInt = lhs.hinting - rhs.hinting;
282 if (deltaInt != 0) return (deltaInt);
283
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700284 deltaInt = lhs.dirFlags - rhs.dirFlags;
285 if (deltaInt) return (deltaInt);
286
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700287 deltaInt = lhs.variant - rhs.variant;
288 if (deltaInt) return (deltaInt);
289
290 if (lhs.language < rhs.language) return -1;
291 if (lhs.language > rhs.language) return +1;
292
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700293 return memcmp(lhs.getText(), rhs.getText(), lhs.contextCount * sizeof(UChar));
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700294}
295
Jeff Brown06daa7b2011-11-11 15:14:56 -0800296size_t TextLayoutCacheKey::getSize() const {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700297 return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700298}
299
Raph Leviend98efca2012-10-25 23:17:33 -0700300hash_t TextLayoutCacheKey::hash() const {
301 uint32_t hash = JenkinsHashMix(0, start);
302 hash = JenkinsHashMix(hash, count);
303 /* contextCount not needed because it's included in text, below */
304 hash = JenkinsHashMix(hash, hash_type(typeface));
305 hash = JenkinsHashMix(hash, hash_type(textSize));
306 hash = JenkinsHashMix(hash, hash_type(textSkewX));
307 hash = JenkinsHashMix(hash, hash_type(textScaleX));
308 hash = JenkinsHashMix(hash, flags);
309 hash = JenkinsHashMix(hash, hinting);
310 hash = JenkinsHashMix(hash, variant);
311 // Note: leaving out language is not problematic, as equality comparisons
312 // are still valid - the only bad thing that could happen is collisions.
313 hash = JenkinsHashMixShorts(hash, getText(), contextCount);
314 return JenkinsHashWhiten(hash);
315}
316
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700317/**
318 * TextLayoutCacheValue
319 */
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800320TextLayoutValue::TextLayoutValue(size_t contextCount) :
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700321 mTotalAdvance(0), mElapsedTime(0) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800322 // Give a hint for advances and glyphs vectors size
323 mAdvances.setCapacity(contextCount);
324 mGlyphs.setCapacity(contextCount);
Raph Levien2301d322012-07-17 16:39:49 -0700325 mPos.setCapacity(contextCount * 2);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800326}
327
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800328size_t TextLayoutValue::getSize() const {
329 return sizeof(TextLayoutValue) + sizeof(jfloat) * mAdvances.capacity() +
Raph Levien2301d322012-07-17 16:39:49 -0700330 sizeof(jchar) * mGlyphs.capacity() + sizeof(jfloat) * mPos.capacity();
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700331}
332
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800333void TextLayoutValue::setElapsedTime(uint32_t time) {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700334 mElapsedTime = time;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700335}
336
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800337uint32_t TextLayoutValue::getElapsedTime() {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700338 return mElapsedTime;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700339}
340
Raph Levienaaedde52012-10-30 15:55:33 -0700341TextLayoutShaper::TextLayoutShaper() {
Raph Levienaaedde52012-10-30 15:55:33 -0700342 mBuffer = hb_buffer_create();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800343}
344
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700345TextLayoutShaper::~TextLayoutShaper() {
Raph Levienaaedde52012-10-30 15:55:33 -0700346 hb_buffer_destroy(mBuffer);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800347}
348
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800349void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint, const UChar* chars,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700350 size_t start, size_t count, size_t contextCount, int dirFlags) {
Fabrice Di Meglio79df5322011-09-19 15:17:56 -0700351
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700352 computeValues(paint, chars, start, count, contextCount, dirFlags,
Raph Levien2301d322012-07-17 16:39:49 -0700353 &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs, &value->mPos);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700354#if DEBUG_ADVANCES
Steve Block5baa3a62011-12-20 16:23:08 +0000355 ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800356 contextCount, value->mTotalAdvance);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700357#endif
358}
359
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800360void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700361 size_t start, size_t count, size_t contextCount, int dirFlags,
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700362 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Raph Levien2301d322012-07-17 16:39:49 -0700363 Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
364 *outTotalAdvance = 0;
Jeff Browna03bded2011-12-05 17:36:16 -0800365 if (!count) {
Jeff Browna03bded2011-12-05 17:36:16 -0800366 return;
367 }
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700368
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700369 UBiDiLevel bidiReq = 0;
370 bool forceLTR = false;
371 bool forceRTL = false;
372
373 switch (dirFlags & kBidi_Mask) {
374 case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
375 case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
376 case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
377 case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
378 case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR
379 case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
380 }
381
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700382 bool useSingleRun = false;
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700383 bool isRTL = forceRTL;
384 if (forceLTR || forceRTL) {
385 useSingleRun = true;
386 } else {
387 UBiDi* bidi = ubidi_open();
388 if (bidi) {
389 UErrorCode status = U_ZERO_ERROR;
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700390#if DEBUG_GLYPHS
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700391 ALOGD("******** ComputeValues -- start");
392 ALOGD(" -- string = '%s'", String8(chars + start, count).string());
393 ALOGD(" -- start = %d", start);
394 ALOGD(" -- count = %d", count);
395 ALOGD(" -- contextCount = %d", contextCount);
396 ALOGD(" -- bidiReq = %d", bidiReq);
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700397#endif
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700398 ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
399 if (U_SUCCESS(status)) {
400 int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
401 ssize_t rc = ubidi_countRuns(bidi, &status);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700402#if DEBUG_GLYPHS
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700403 ALOGD(" -- dirFlags = %d", dirFlags);
404 ALOGD(" -- paraDir = %d", paraDir);
405 ALOGD(" -- run-count = %d", int(rc));
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700406#endif
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700407 if (U_SUCCESS(status) && rc == 1) {
408 // Normal case: one run, status is ok
409 isRTL = (paraDir == 1);
410 useSingleRun = true;
411 } else if (!U_SUCCESS(status) || rc < 1) {
412 ALOGW("Need to force to single run -- string = '%s',"
413 " status = %d, rc = %d",
414 String8(chars + start, count).string(), status, int(rc));
415 isRTL = (paraDir == 1);
416 useSingleRun = true;
417 } else {
418 int32_t end = start + count;
419 for (size_t i = 0; i < size_t(rc); ++i) {
420 int32_t startRun = -1;
421 int32_t lengthRun = -1;
422 UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800423
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700424 if (startRun == -1 || lengthRun == -1) {
425 // Something went wrong when getting the visual run, need to clear
426 // already computed data before doing a single run pass
427 ALOGW("Visual run is not valid");
428 outGlyphs->clear();
429 outAdvances->clear();
430 outPos->clear();
431 *outTotalAdvance = 0;
432 isRTL = (paraDir == 1);
433 useSingleRun = true;
434 break;
435 }
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800436
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700437 if (startRun >= end) {
438 continue;
439 }
440 int32_t endRun = startRun + lengthRun;
441 if (endRun <= int32_t(start)) {
442 continue;
443 }
444 if (startRun < int32_t(start)) {
445 startRun = int32_t(start);
446 }
447 if (endRun > end) {
448 endRun = end;
449 }
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800450
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700451 lengthRun = endRun - startRun;
452 isRTL = (runDir == UBIDI_RTL);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800453#if DEBUG_GLYPHS
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700454 ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
455 i, startRun, lengthRun, isRTL);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800456#endif
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700457 computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
458 outAdvances, outTotalAdvance, outGlyphs, outPos);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800459
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700460 }
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800461 }
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700462 } else {
463 ALOGW("Cannot set Para");
464 useSingleRun = true;
465 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700466 }
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700467 ubidi_close(bidi);
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700468 } else {
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700469 ALOGW("Cannot ubidi_open()");
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700470 useSingleRun = true;
471 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700472 }
473 }
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700474
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700475 // Default single run case
476 if (useSingleRun){
477#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000478 ALOGD("Using a SINGLE BiDi Run "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800479 "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700480#endif
Raph Levienaaedde52012-10-30 15:55:33 -0700481 computeRunValues(paint, chars, start, count, contextCount, isRTL,
Raph Levien2301d322012-07-17 16:39:49 -0700482 outAdvances, outTotalAdvance, outGlyphs, outPos);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700483 }
484
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700485#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000486 ALOGD(" -- Total returned glyphs-count = %d", outGlyphs->size());
487 ALOGD("******** ComputeValues -- end");
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700488#endif
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700489}
490
Raph Levienaaedde52012-10-30 15:55:33 -0700491#define HB_IsHighSurrogate(ucs) \
492 (((ucs) & 0xfc00) == 0xd800)
493
494#define HB_IsLowSurrogate(ucs) \
495 (((ucs) & 0xfc00) == 0xdc00)
496
497#ifndef HB_SurrogateToUcs4
498#define HB_SurrogateToUcs4_(high, low) \
499 (((hb_codepoint_t)(high))<<10) + (low) - 0x35fdc00;
500#endif
501
502#define HB_InvalidCodePoint ~0u
503
504hb_codepoint_t
505utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) {
506 const uint16_t v = chars[(*iter)++];
507 if (HB_IsHighSurrogate(v)) {
508 // surrogate pair
509 if (size_t(*iter) >= len) {
510 // the surrogate is incomplete.
511 return HB_InvalidCodePoint;
512 }
513 const uint16_t v2 = chars[(*iter)++];
514 if (!HB_IsLowSurrogate(v2)) {
515 // invalidate surrogate pair.
516 (*iter)--;
517 return HB_InvalidCodePoint;
518 }
519
520 return HB_SurrogateToUcs4(v, v2);
521 }
522
523 if (HB_IsLowSurrogate(v)) {
524 // this isn't a valid code point
525 return HB_InvalidCodePoint;
526 }
527
528 return v;
529}
530
531hb_codepoint_t
532utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) {
533 const uint16_t v = chars[(*iter)--];
534 if (HB_IsLowSurrogate(v)) {
535 // surrogate pair
536 if (*iter < 0) {
537 // the surrogate is incomplete.
538 return HB_InvalidCodePoint;
539 }
540 const uint16_t v2 = chars[(*iter)--];
541 if (!HB_IsHighSurrogate(v2)) {
542 // invalidate surrogate pair.
543 (*iter)++;
544 return HB_InvalidCodePoint;
545 }
546
547 return HB_SurrogateToUcs4(v2, v);
548 }
549
550 if (HB_IsHighSurrogate(v)) {
551 // this isn't a valid code point
552 return HB_InvalidCodePoint;
553 }
554
555 return v;
556}
557
558struct ScriptRun {
559 hb_script_t script;
560 size_t pos;
561 size_t length;
562};
563
564hb_script_t code_point_to_script(hb_codepoint_t codepoint) {
565 static hb_unicode_funcs_t* u;
566 if (!u) {
567 u = hb_icu_get_unicode_funcs();
568 }
569 return hb_unicode_script(u, codepoint);
570}
571
572bool
573hb_utf16_script_run_next(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
574 if (size_t(*iter) == len)
575 return false;
576
577 run->pos = *iter;
578 const uint32_t init_cp = utf16_to_code_point(chars, len, iter);
579 const hb_script_t init_script = code_point_to_script(init_cp);
580 hb_script_t current_script = init_script;
581 run->script = init_script;
582
583 for (;;) {
584 if (size_t(*iter) == len)
585 break;
586 const ssize_t prev_iter = *iter;
587 const uint32_t cp = utf16_to_code_point(chars, len, iter);
588 const hb_script_t script = code_point_to_script(cp);
589
590 if (script != current_script) {
591 /* BEGIN android-changed
592 The condition was not correct by doing "a == b == constant"
593 END android-changed */
594 if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
595 // If we started off as inherited, we take whatever we can find.
596 run->script = script;
597 current_script = script;
598 continue;
599 } else if (script == HB_SCRIPT_INHERITED) {
600 continue;
601 } else {
602 *iter = prev_iter;
603 break;
604 }
605 }
606 }
607
608 if (run->script == HB_SCRIPT_INHERITED)
609 run->script = HB_SCRIPT_COMMON;
610
611 run->length = *iter - run->pos;
612 return true;
613}
614
615bool
616hb_utf16_script_run_prev(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
617 if (*iter == -1)
618 return false;
619
620 const size_t ending_index = *iter;
621 const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter);
622 const hb_script_t init_script = code_point_to_script(init_cp);
623 hb_script_t current_script = init_script;
624 run->script = init_script;
Raph Levien9d47db22013-04-23 12:42:16 -0700625 size_t break_iter = *iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700626
627 for (;;) {
628 if (*iter < 0)
629 break;
Raph Levienaaedde52012-10-30 15:55:33 -0700630 const uint32_t cp = utf16_to_code_point_prev(chars, len, iter);
631 const hb_script_t script = code_point_to_script(cp);
632
633 if (script != current_script) {
634 if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
635 // If we started off as inherited, we take whatever we can find.
636 run->script = script;
637 current_script = script;
Raph Levien9d47db22013-04-23 12:42:16 -0700638 // In cases of script1 + inherited + script2, always group the inherited
639 // with script1.
640 break_iter = *iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700641 continue;
642 } else if (script == HB_SCRIPT_INHERITED) {
Raph Levienaaedde52012-10-30 15:55:33 -0700643 continue;
644 } else {
Raph Levien9d47db22013-04-23 12:42:16 -0700645 *iter = break_iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700646 break;
647 }
Raph Levien9d47db22013-04-23 12:42:16 -0700648 } else {
649 break_iter = *iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700650 }
651 }
652
653 if (run->script == HB_SCRIPT_INHERITED)
654 run->script = HB_SCRIPT_COMMON;
655
656 run->pos = *iter + 1;
657 run->length = ending_index - *iter;
658 return true;
659}
660
661
662static void logGlyphs(hb_buffer_t* buffer) {
663 unsigned int numGlyphs;
664 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
665 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
666 ALOGD(" -- glyphs count=%d", numGlyphs);
667 for (size_t i = 0; i < numGlyphs; i++) {
668 ALOGD(" -- glyph[%d] = %d, cluster = %u, advance = %0.2f, offset.x = %0.2f, offset.y = %0.2f", i,
669 info[i].codepoint,
670 info[i].cluster,
671 HBFixedToFloat(positions[i].x_advance),
672 HBFixedToFloat(positions[i].x_offset),
673 HBFixedToFloat(positions[i].y_offset));
Fabrice Di Meglioabb0f292011-08-18 17:59:29 -0700674 }
675}
676
Raph Levienaaedde52012-10-30 15:55:33 -0700677void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars,
678 size_t start, size_t count, size_t contextCount, bool isRTL,
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700679 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Raph Levien2301d322012-07-17 16:39:49 -0700680 Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
Jeff Browna03bded2011-12-05 17:36:16 -0800681 if (!count) {
682 // We cannot shape an empty run.
Jeff Browna03bded2011-12-05 17:36:16 -0800683 return;
684 }
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700685
Raph Levien57e97232012-04-24 16:04:34 -0700686 // To be filled in later
687 for (size_t i = 0; i < count; i++) {
688 outAdvances->add(0);
689 }
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800690
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800691 // Set the string properties
Raph Levienaaedde52012-10-30 15:55:33 -0700692 const UChar* chars = contextChars + start;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700693
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800694 // Define shaping paint properties
695 mShapingPaint.setTextSize(paint->getTextSize());
Raph Levien2301d322012-07-17 16:39:49 -0700696 float skewX = paint->getTextSkewX();
697 mShapingPaint.setTextSkewX(skewX);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800698 mShapingPaint.setTextScaleX(paint->getTextScaleX());
699 mShapingPaint.setFlags(paint->getFlags());
700 mShapingPaint.setHinting(paint->getHinting());
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700701 mShapingPaint.setFontVariant(paint->getFontVariant());
702 mShapingPaint.setLanguage(paint->getLanguage());
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800703
704 // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
705 // into the shaperItem
Raph Levienaaedde52012-10-30 15:55:33 -0700706 ssize_t indexFontRun = isRTL ? count - 1 : 0;
Raph Levien2301d322012-07-17 16:39:49 -0700707 jfloat totalAdvance = *outTotalAdvance;
Raph Levienaaedde52012-10-30 15:55:33 -0700708 ScriptRun run; // relative to chars
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800709 while ((isRTL) ?
Raph Levienaaedde52012-10-30 15:55:33 -0700710 hb_utf16_script_run_prev(&run, chars, count, &indexFontRun):
711 hb_utf16_script_run_next(&run, chars, count, &indexFontRun)) {
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700712
Fabrice Di Meglio9c418db2011-09-18 12:54:38 -0700713#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000714 ALOGD("-------- Start of Script Run --------");
715 ALOGD("Shaping Script Run with");
716 ALOGD(" -- isRTL = %d", isRTL);
Raph Levienaaedde52012-10-30 15:55:33 -0700717 ALOGD(" -- HB script = %c%c%c%c", HB_UNTAG(run.script));
718 ALOGD(" -- run.pos = %d", int(run.pos));
719 ALOGD(" -- run.length = %d", int(run.length));
720 ALOGD(" -- run = '%s'", String8(chars + run.pos, run.length).string());
Steve Block5baa3a62011-12-20 16:23:08 +0000721 ALOGD(" -- string = '%s'", String8(chars, count).string());
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700722#endif
723
Raph Levienaaedde52012-10-30 15:55:33 -0700724 hb_buffer_reset(mBuffer);
725 // Note: if we want to set unicode functions, etc., this is the place.
726
727 hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
728 hb_buffer_set_script(mBuffer, run.script);
729 // Should set language here (for bug 7004056)
730 hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length);
731
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800732 // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
733 // and shape the Font run
Raph Levienaaedde52012-10-30 15:55:33 -0700734 size_t glyphBaseCount = shapeFontRun(paint);
735 unsigned int numGlyphs;
736 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
737 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(mBuffer, NULL);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700738
Fabrice Di Meglio78b868f2011-05-17 19:48:57 -0700739#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000740 ALOGD("Got from Harfbuzz");
741 ALOGD(" -- glyphBaseCount = %d", glyphBaseCount);
Raph Levienaaedde52012-10-30 15:55:33 -0700742 ALOGD(" -- num_glyph = %d", numGlyphs);
Steve Block5baa3a62011-12-20 16:23:08 +0000743 ALOGD(" -- isDevKernText = %d", paint->isDevKernText());
Raph Levienaaedde52012-10-30 15:55:33 -0700744 ALOGD(" -- initial totalAdvance = %f", totalAdvance);
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700745
Raph Levienaaedde52012-10-30 15:55:33 -0700746 logGlyphs(mBuffer);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700747#endif
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700748
Raph Levienaaedde52012-10-30 15:55:33 -0700749 for (size_t i = 0; i < numGlyphs; i++) {
750 size_t cluster = info[i].cluster - start;
751 float xAdvance = HBFixedToFloat(positions[i].x_advance);
752 outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster);
753 outGlyphs->add(info[i].codepoint + glyphBaseCount);
754 float xo = HBFixedToFloat(positions[i].x_offset);
755 float yo = -HBFixedToFloat(positions[i].y_offset);
756 outPos->add(totalAdvance + xo + yo * skewX);
757 outPos->add(yo);
758 totalAdvance += xAdvance;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700759 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700760 }
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800761
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700762 *outTotalAdvance = totalAdvance;
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800763
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800764#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700765 ALOGD(" -- final totalAdvance = %f", totalAdvance);
Steve Block5baa3a62011-12-20 16:23:08 +0000766 ALOGD("-------- End of Script Run --------");
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800767#endif
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700768}
769
Raph Levien1637dcd2012-05-02 10:41:14 -0700770/**
771 * Return the first typeface in the logical change, starting with this typeface,
772 * that contains the specified unichar, or NULL if none is found.
Raph Levien1637dcd2012-05-02 10:41:14 -0700773 */
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700774SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
Raph Levienaaedde52012-10-30 15:55:33 -0700775 hb_script_t script) {
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700776 SkTypeface::Style currentStyle = SkTypeface::kNormal;
777 if (typeface) {
778 currentStyle = typeface->style();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800779 }
Raph Levienaaedde52012-10-30 15:55:33 -0700780 typeface = SkCreateTypefaceForScriptNG(script, currentStyle);
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700781#if DEBUG_GLYPHS
Victoria Leaseded5ed92013-02-12 13:36:22 -0800782 ALOGD("Using Harfbuzz Script %c%c%c%c, Style %d", HB_UNTAG(script), currentStyle);
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700783#endif
Raph Levien1637dcd2012-05-02 10:41:14 -0700784 return typeface;
785}
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800786
Raph Levienaaedde52012-10-30 15:55:33 -0700787bool TextLayoutShaper::isComplexScript(hb_script_t script) {
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700788 switch (script) {
Raph Levienaaedde52012-10-30 15:55:33 -0700789 case HB_SCRIPT_COMMON:
790 case HB_SCRIPT_GREEK:
791 case HB_SCRIPT_CYRILLIC:
792 case HB_SCRIPT_HANGUL:
793 case HB_SCRIPT_INHERITED:
Junichi Monma6d191ed2013-01-17 16:13:22 +0900794 case HB_SCRIPT_HAN:
795 case HB_SCRIPT_KATAKANA:
796 case HB_SCRIPT_HIRAGANA:
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700797 return false;
798 default:
799 return true;
800 }
801}
802
Raph Levienaaedde52012-10-30 15:55:33 -0700803size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) {
Raph Levien1637dcd2012-05-02 10:41:14 -0700804 // Update Harfbuzz Shaper
Raph Levien1637dcd2012-05-02 10:41:14 -0700805
806 SkTypeface* typeface = paint->getTypeface();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800807
808 // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
809 // This is needed as the Typeface used for shaping can be not the default one
810 // when we are shaping any script that needs to use a fallback Font.
811 // If we are a "common" script we dont need to shift
812 size_t baseGlyphCount = 0;
Raph Levienaaedde52012-10-30 15:55:33 -0700813 hb_codepoint_t firstUnichar = 0;
814 if (isComplexScript(hb_buffer_get_script(mBuffer))) {
815 unsigned int numGlyphs;
816 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
817 for (size_t i = 0; i < numGlyphs; i++) {
818 firstUnichar = info[i].codepoint;
819 if (firstUnichar != ' ') {
820 break;
821 }
Raph Levienb2944352012-05-01 09:41:50 -0700822 }
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800823 baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800824 }
825
Raph Leviendd0a9122013-05-06 15:00:41 -0700826 SkTypeface* scriptTypeface = NULL;
Raph Levien1637dcd2012-05-02 10:41:14 -0700827 if (baseGlyphCount != 0) {
Raph Leviendd0a9122013-05-06 15:00:41 -0700828 scriptTypeface = typefaceForScript(paint, typeface,
829 hb_buffer_get_script(mBuffer));
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700830#if DEBUG_GLYPHS
Raph Leviendd0a9122013-05-06 15:00:41 -0700831 ALOGD("Using Default Typeface for script %c%c%c%c",
832 HB_UNTAG(hb_buffer_get_script(mBuffer)));
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700833#endif
Raph Leviendd0a9122013-05-06 15:00:41 -0700834 }
835 if (scriptTypeface) {
836 typeface = scriptTypeface;
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700837 } else {
Raph Leviendd0a9122013-05-06 15:00:41 -0700838 baseGlyphCount = 0;
839 if (typeface) {
840 SkSafeRef(typeface);
841 } else {
Victoria Leasecc0f9d82013-04-15 15:29:36 -0700842 typeface = SkFontHost::CreateTypeface(NULL, NULL, SkTypeface::kNormal);
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700843#if DEBUG_GLYPHS
Victoria Leasecc0f9d82013-04-15 15:29:36 -0700844 ALOGD("Using Default Typeface (normal style)");
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700845#endif
846 }
Raph Levien1637dcd2012-05-02 10:41:14 -0700847 }
848
Raph Levien1637dcd2012-05-02 10:41:14 -0700849 mShapingPaint.setTypeface(typeface);
Raph Levienaaedde52012-10-30 15:55:33 -0700850 hb_face_t* face = referenceCachedHBFace(typeface);
Raph Levien1637dcd2012-05-02 10:41:14 -0700851
Raph Levienaaedde52012-10-30 15:55:33 -0700852 float sizeY = paint->getTextSize();
853 float sizeX = sizeY * paint->getTextScaleX();
854 hb_font_t* font = createFont(face, &mShapingPaint, sizeX, sizeY);
855 hb_face_destroy(face);
Raph Levien2301d322012-07-17 16:39:49 -0700856
Raph Levien1637dcd2012-05-02 10:41:14 -0700857#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700858 ALOGD("Run typeface = %p, uniqueID = %d, face = %p",
859 typeface, typeface->uniqueID(), face);
Raph Levien1637dcd2012-05-02 10:41:14 -0700860#endif
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700861 SkSafeUnref(typeface);
Raph Levien1637dcd2012-05-02 10:41:14 -0700862
Raph Levienaaedde52012-10-30 15:55:33 -0700863 hb_shape(font, mBuffer, NULL, 0);
864 hb_font_destroy(font);
865
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800866 return baseGlyphCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700867}
868
Raph Levienaaedde52012-10-30 15:55:33 -0700869hb_face_t* TextLayoutShaper::referenceCachedHBFace(SkTypeface* typeface) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800870 SkFontID fontId = typeface->uniqueID();
871 ssize_t index = mCachedHBFaces.indexOfKey(fontId);
872 if (index >= 0) {
Raph Levienaaedde52012-10-30 15:55:33 -0700873 return hb_face_reference(mCachedHBFaces.valueAt(index));
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800874 }
Raph Levienaaedde52012-10-30 15:55:33 -0700875 // TODO: destroy function
876 hb_face_t* face = hb_face_create_for_tables(harfbuzzSkiaReferenceTable, typeface, NULL);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800877#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700878 ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800879#endif
Raph Levienaaedde52012-10-30 15:55:33 -0700880 mCachedHBFaces.add(fontId, face);
881 return hb_face_reference(face);
Fabrice Di Meglio79df5322011-09-19 15:17:56 -0700882}
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700883
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700884void TextLayoutShaper::purgeCaches() {
Raph Levien75394d62012-06-02 15:47:29 -0700885 size_t cacheSize = mCachedHBFaces.size();
886 for (size_t i = 0; i < cacheSize; i++) {
Raph Levienaaedde52012-10-30 15:55:33 -0700887 hb_face_destroy(mCachedHBFaces.valueAt(i));
Raph Levien75394d62012-06-02 15:47:29 -0700888 }
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700889 mCachedHBFaces.clear();
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700890}
891
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800892TextLayoutEngine::TextLayoutEngine() {
893 mShaper = new TextLayoutShaper();
894#if USE_TEXT_LAYOUT_CACHE
895 mTextLayoutCache = new TextLayoutCache(mShaper);
896#else
897 mTextLayoutCache = NULL;
898#endif
899}
900
901TextLayoutEngine::~TextLayoutEngine() {
902 delete mTextLayoutCache;
903 delete mShaper;
904}
905
906sp<TextLayoutValue> TextLayoutEngine::getValue(const SkPaint* paint, const jchar* text,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700907 jint start, jint count, jint contextCount, jint dirFlags) {
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800908 sp<TextLayoutValue> value;
909#if USE_TEXT_LAYOUT_CACHE
910 value = mTextLayoutCache->getValue(paint, text, start, count,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700911 contextCount, dirFlags);
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800912 if (value == NULL) {
913 ALOGE("Cannot get TextLayoutCache value for text = '%s'",
914 String8(text + start, count).string());
915 }
916#else
917 value = new TextLayoutValue(count);
918 mShaper->computeValues(value.get(), paint,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700919 reinterpret_cast<const UChar*>(text), start, count, contextCount, dirFlags);
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800920#endif
921 return value;
922}
923
Fabrice Di Meglio30ca5cd2012-05-07 17:45:44 -0700924void TextLayoutEngine::purgeCaches() {
925#if USE_TEXT_LAYOUT_CACHE
Raph Levien13ba4e42012-09-12 15:15:51 -0700926 mTextLayoutCache->purgeCaches();
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700927#if DEBUG_GLYPHS
928 ALOGD("Purged TextLayoutEngine caches");
929#endif
Fabrice Di Meglio30ca5cd2012-05-07 17:45:44 -0700930#endif
931}
932
933
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800934} // namespace android