blob: 144ac3990cd2fcadfdd8f65123d3b41598ed92d3 [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"
Victoria Lease43b692d2013-12-03 15:02:28 -080023#include "SkGlyphCache.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),
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400222 hinting(SkPaint::kNo_Hinting) {
223 paintOpts.setUseFontFallbacks(true);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700224}
225
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700226TextLayoutCacheKey::TextLayoutCacheKey(const SkPaint* paint, const UChar* text,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700227 size_t start, size_t count, size_t contextCount, int dirFlags) :
228 start(start), count(count), contextCount(contextCount),
229 dirFlags(dirFlags) {
Raph Levien832815c2012-10-02 10:30:41 -0700230 textCopy.setTo(text, contextCount);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700231 typeface = paint->getTypeface();
232 textSize = paint->getTextSize();
233 textSkewX = paint->getTextSkewX();
234 textScaleX = paint->getTextScaleX();
235 flags = paint->getFlags();
236 hinting = paint->getHinting();
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400237 paintOpts = paint->getPaintOptionsAndroid();
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),
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400252 paintOpts(other.paintOpts) {
Fabrice Di Meglioe74fef32011-09-18 14:30:21 -0700253}
254
Fabrice Di Meglio717060b2011-09-27 15:53:42 -0700255int TextLayoutCacheKey::compare(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& rhs) {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700256 int deltaInt = lhs.start - rhs.start;
257 if (deltaInt != 0) return (deltaInt);
258
259 deltaInt = lhs.count - rhs.count;
260 if (deltaInt != 0) return (deltaInt);
261
262 deltaInt = lhs.contextCount - rhs.contextCount;
Fabrice Di Meglio717060b2011-09-27 15:53:42 -0700263 if (deltaInt != 0) return (deltaInt);
264
265 if (lhs.typeface < rhs.typeface) return -1;
266 if (lhs.typeface > rhs.typeface) return +1;
267
268 if (lhs.textSize < rhs.textSize) return -1;
269 if (lhs.textSize > rhs.textSize) return +1;
270
271 if (lhs.textSkewX < rhs.textSkewX) return -1;
272 if (lhs.textSkewX > rhs.textSkewX) return +1;
273
274 if (lhs.textScaleX < rhs.textScaleX) return -1;
275 if (lhs.textScaleX > rhs.textScaleX) return +1;
276
277 deltaInt = lhs.flags - rhs.flags;
278 if (deltaInt != 0) return (deltaInt);
279
280 deltaInt = lhs.hinting - rhs.hinting;
281 if (deltaInt != 0) return (deltaInt);
282
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700283 deltaInt = lhs.dirFlags - rhs.dirFlags;
284 if (deltaInt) return (deltaInt);
285
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400286 if (lhs.paintOpts != rhs.paintOpts)
287 return memcmp(&lhs.paintOpts, &rhs.paintOpts, sizeof(SkPaintOptionsAndroid));
Billy Hewlettac1cbaf2012-07-18 09:51:45 -0700288
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700289 return memcmp(lhs.getText(), rhs.getText(), lhs.contextCount * sizeof(UChar));
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700290}
291
Jeff Brown06daa7b2011-11-11 15:14:56 -0800292size_t TextLayoutCacheKey::getSize() const {
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700293 return sizeof(TextLayoutCacheKey) + sizeof(UChar) * contextCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700294}
295
Raph Leviend98efca2012-10-25 23:17:33 -0700296hash_t TextLayoutCacheKey::hash() const {
297 uint32_t hash = JenkinsHashMix(0, start);
298 hash = JenkinsHashMix(hash, count);
299 /* contextCount not needed because it's included in text, below */
300 hash = JenkinsHashMix(hash, hash_type(typeface));
301 hash = JenkinsHashMix(hash, hash_type(textSize));
302 hash = JenkinsHashMix(hash, hash_type(textSkewX));
303 hash = JenkinsHashMix(hash, hash_type(textScaleX));
304 hash = JenkinsHashMix(hash, flags);
305 hash = JenkinsHashMix(hash, hinting);
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400306 hash = JenkinsHashMix(hash, paintOpts.getFontVariant());
Raph Leviend98efca2012-10-25 23:17:33 -0700307 // Note: leaving out language is not problematic, as equality comparisons
308 // are still valid - the only bad thing that could happen is collisions.
309 hash = JenkinsHashMixShorts(hash, getText(), contextCount);
310 return JenkinsHashWhiten(hash);
311}
312
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700313/**
314 * TextLayoutCacheValue
315 */
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800316TextLayoutValue::TextLayoutValue(size_t contextCount) :
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700317 mTotalAdvance(0), mElapsedTime(0) {
Chris Craik41541822013-05-03 16:35:54 -0700318 mBounds.setEmpty();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800319 // Give a hint for advances and glyphs vectors size
320 mAdvances.setCapacity(contextCount);
321 mGlyphs.setCapacity(contextCount);
Raph Levien2301d322012-07-17 16:39:49 -0700322 mPos.setCapacity(contextCount * 2);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800323}
324
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800325size_t TextLayoutValue::getSize() const {
326 return sizeof(TextLayoutValue) + sizeof(jfloat) * mAdvances.capacity() +
Raph Levien2301d322012-07-17 16:39:49 -0700327 sizeof(jchar) * mGlyphs.capacity() + sizeof(jfloat) * mPos.capacity();
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700328}
329
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800330void TextLayoutValue::setElapsedTime(uint32_t time) {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700331 mElapsedTime = time;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700332}
333
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800334uint32_t TextLayoutValue::getElapsedTime() {
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700335 return mElapsedTime;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700336}
337
Raph Levienaaedde52012-10-30 15:55:33 -0700338TextLayoutShaper::TextLayoutShaper() {
Raph Levienaaedde52012-10-30 15:55:33 -0700339 mBuffer = hb_buffer_create();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800340}
341
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700342TextLayoutShaper::~TextLayoutShaper() {
Raph Levienaaedde52012-10-30 15:55:33 -0700343 hb_buffer_destroy(mBuffer);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800344}
345
Chris Craik41541822013-05-03 16:35:54 -0700346void TextLayoutShaper::computeValues(TextLayoutValue* value, const SkPaint* paint,
347 const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags) {
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700348 computeValues(paint, chars, start, count, contextCount, dirFlags,
Chris Craik41541822013-05-03 16:35:54 -0700349 &value->mAdvances, &value->mTotalAdvance, &value->mBounds,
350 &value->mGlyphs, &value->mPos);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700351#if DEBUG_ADVANCES
Steve Block5baa3a62011-12-20 16:23:08 +0000352 ALOGD("Advances - start = %d, count = %d, contextCount = %d, totalAdvance = %f", start, count,
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800353 contextCount, value->mTotalAdvance);
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700354#endif
355}
356
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800357void TextLayoutShaper::computeValues(const SkPaint* paint, const UChar* chars,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700358 size_t start, size_t count, size_t contextCount, int dirFlags,
Chris Craik41541822013-05-03 16:35:54 -0700359 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, SkRect* outBounds,
Raph Levien2301d322012-07-17 16:39:49 -0700360 Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
361 *outTotalAdvance = 0;
Jeff Browna03bded2011-12-05 17:36:16 -0800362 if (!count) {
Jeff Browna03bded2011-12-05 17:36:16 -0800363 return;
364 }
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700365
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700366 UBiDiLevel bidiReq = 0;
367 bool forceLTR = false;
368 bool forceRTL = false;
369
370 switch (dirFlags & kBidi_Mask) {
371 case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
372 case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
373 case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
374 case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
375 case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR
376 case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
377 }
378
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700379 bool useSingleRun = false;
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700380 bool isRTL = forceRTL;
381 if (forceLTR || forceRTL) {
382 useSingleRun = true;
383 } else {
384 UBiDi* bidi = ubidi_open();
385 if (bidi) {
386 UErrorCode status = U_ZERO_ERROR;
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700387#if DEBUG_GLYPHS
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700388 ALOGD("******** ComputeValues -- start");
389 ALOGD(" -- string = '%s'", String8(chars + start, count).string());
390 ALOGD(" -- start = %d", start);
391 ALOGD(" -- count = %d", count);
392 ALOGD(" -- contextCount = %d", contextCount);
393 ALOGD(" -- bidiReq = %d", bidiReq);
Fabrice Di Meglio06732fd2011-04-18 15:44:53 -0700394#endif
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700395 ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
396 if (U_SUCCESS(status)) {
397 int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
398 ssize_t rc = ubidi_countRuns(bidi, &status);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700399#if DEBUG_GLYPHS
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700400 ALOGD(" -- dirFlags = %d", dirFlags);
401 ALOGD(" -- paraDir = %d", paraDir);
402 ALOGD(" -- run-count = %d", int(rc));
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700403#endif
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700404 if (U_SUCCESS(status) && rc == 1) {
405 // Normal case: one run, status is ok
406 isRTL = (paraDir == 1);
407 useSingleRun = true;
408 } else if (!U_SUCCESS(status) || rc < 1) {
409 ALOGW("Need to force to single run -- string = '%s',"
410 " status = %d, rc = %d",
411 String8(chars + start, count).string(), status, int(rc));
412 isRTL = (paraDir == 1);
413 useSingleRun = true;
414 } else {
415 int32_t end = start + count;
416 for (size_t i = 0; i < size_t(rc); ++i) {
417 int32_t startRun = -1;
418 int32_t lengthRun = -1;
419 UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800420
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700421 if (startRun == -1 || lengthRun == -1) {
422 // Something went wrong when getting the visual run, need to clear
423 // already computed data before doing a single run pass
424 ALOGW("Visual run is not valid");
425 outGlyphs->clear();
426 outAdvances->clear();
427 outPos->clear();
428 *outTotalAdvance = 0;
429 isRTL = (paraDir == 1);
430 useSingleRun = true;
431 break;
432 }
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800433
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700434 if (startRun >= end) {
435 continue;
436 }
437 int32_t endRun = startRun + lengthRun;
438 if (endRun <= int32_t(start)) {
439 continue;
440 }
441 if (startRun < int32_t(start)) {
442 startRun = int32_t(start);
443 }
444 if (endRun > end) {
445 endRun = end;
446 }
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800447
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700448 lengthRun = endRun - startRun;
449 isRTL = (runDir == UBIDI_RTL);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800450#if DEBUG_GLYPHS
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700451 ALOGD("Processing Bidi Run = %d -- run-start = %d, run-len = %d, isRTL = %d",
452 i, startRun, lengthRun, isRTL);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800453#endif
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700454 computeRunValues(paint, chars, startRun, lengthRun, contextCount, isRTL,
Chris Craik41541822013-05-03 16:35:54 -0700455 outAdvances, outTotalAdvance, outBounds, outGlyphs, outPos);
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800456
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700457 }
Fabrice Di Meglio6d9fe5b2013-02-11 18:27:34 -0800458 }
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700459 } else {
460 ALOGW("Cannot set Para");
461 useSingleRun = true;
462 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700463 }
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700464 ubidi_close(bidi);
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700465 } else {
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700466 ALOGW("Cannot ubidi_open()");
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700467 useSingleRun = true;
468 isRTL = (bidiReq = 1) || (bidiReq = UBIDI_DEFAULT_RTL);
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700469 }
470 }
Fabrice Di Meglio5c863f72011-10-05 18:11:59 -0700471
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700472 // Default single run case
473 if (useSingleRun){
474#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000475 ALOGD("Using a SINGLE BiDi Run "
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800476 "-- run-start = %d, run-len = %d, isRTL = %d", start, count, isRTL);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700477#endif
Raph Levienaaedde52012-10-30 15:55:33 -0700478 computeRunValues(paint, chars, start, count, contextCount, isRTL,
Chris Craik41541822013-05-03 16:35:54 -0700479 outAdvances, outTotalAdvance, outBounds, outGlyphs, outPos);
Fabrice Di Meglio5beeda02011-10-24 17:05:55 -0700480 }
481
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700482#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000483 ALOGD(" -- Total returned glyphs-count = %d", outGlyphs->size());
484 ALOGD("******** ComputeValues -- end");
Fabrice Di Meglio589e4e22011-04-25 16:48:51 -0700485#endif
Fabrice Di Meglio689e5152011-04-13 16:07:37 -0700486}
487
Raph Levienaaedde52012-10-30 15:55:33 -0700488#define HB_IsHighSurrogate(ucs) \
489 (((ucs) & 0xfc00) == 0xd800)
490
491#define HB_IsLowSurrogate(ucs) \
492 (((ucs) & 0xfc00) == 0xdc00)
493
494#ifndef HB_SurrogateToUcs4
495#define HB_SurrogateToUcs4_(high, low) \
496 (((hb_codepoint_t)(high))<<10) + (low) - 0x35fdc00;
497#endif
498
499#define HB_InvalidCodePoint ~0u
500
501hb_codepoint_t
502utf16_to_code_point(const uint16_t *chars, size_t len, ssize_t *iter) {
503 const uint16_t v = chars[(*iter)++];
504 if (HB_IsHighSurrogate(v)) {
505 // surrogate pair
506 if (size_t(*iter) >= len) {
507 // the surrogate is incomplete.
508 return HB_InvalidCodePoint;
509 }
510 const uint16_t v2 = chars[(*iter)++];
511 if (!HB_IsLowSurrogate(v2)) {
512 // invalidate surrogate pair.
513 (*iter)--;
514 return HB_InvalidCodePoint;
515 }
516
517 return HB_SurrogateToUcs4(v, v2);
518 }
519
520 if (HB_IsLowSurrogate(v)) {
521 // this isn't a valid code point
522 return HB_InvalidCodePoint;
523 }
524
525 return v;
526}
527
528hb_codepoint_t
529utf16_to_code_point_prev(const uint16_t *chars, size_t len, ssize_t *iter) {
530 const uint16_t v = chars[(*iter)--];
531 if (HB_IsLowSurrogate(v)) {
532 // surrogate pair
533 if (*iter < 0) {
534 // the surrogate is incomplete.
535 return HB_InvalidCodePoint;
536 }
537 const uint16_t v2 = chars[(*iter)--];
538 if (!HB_IsHighSurrogate(v2)) {
539 // invalidate surrogate pair.
540 (*iter)++;
541 return HB_InvalidCodePoint;
542 }
543
544 return HB_SurrogateToUcs4(v2, v);
545 }
546
547 if (HB_IsHighSurrogate(v)) {
548 // this isn't a valid code point
549 return HB_InvalidCodePoint;
550 }
551
552 return v;
553}
554
555struct ScriptRun {
556 hb_script_t script;
557 size_t pos;
558 size_t length;
559};
560
561hb_script_t code_point_to_script(hb_codepoint_t codepoint) {
562 static hb_unicode_funcs_t* u;
563 if (!u) {
564 u = hb_icu_get_unicode_funcs();
565 }
566 return hb_unicode_script(u, codepoint);
567}
568
569bool
570hb_utf16_script_run_next(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
571 if (size_t(*iter) == len)
572 return false;
573
574 run->pos = *iter;
575 const uint32_t init_cp = utf16_to_code_point(chars, len, iter);
576 const hb_script_t init_script = code_point_to_script(init_cp);
577 hb_script_t current_script = init_script;
578 run->script = init_script;
579
580 for (;;) {
581 if (size_t(*iter) == len)
582 break;
583 const ssize_t prev_iter = *iter;
584 const uint32_t cp = utf16_to_code_point(chars, len, iter);
585 const hb_script_t script = code_point_to_script(cp);
586
587 if (script != current_script) {
588 /* BEGIN android-changed
589 The condition was not correct by doing "a == b == constant"
590 END android-changed */
591 if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
592 // If we started off as inherited, we take whatever we can find.
593 run->script = script;
594 current_script = script;
595 continue;
596 } else if (script == HB_SCRIPT_INHERITED) {
597 continue;
598 } else {
599 *iter = prev_iter;
600 break;
601 }
602 }
603 }
604
605 if (run->script == HB_SCRIPT_INHERITED)
606 run->script = HB_SCRIPT_COMMON;
607
608 run->length = *iter - run->pos;
609 return true;
610}
611
612bool
613hb_utf16_script_run_prev(ScriptRun* run, const uint16_t *chars, size_t len, ssize_t *iter) {
614 if (*iter == -1)
615 return false;
616
617 const size_t ending_index = *iter;
618 const uint32_t init_cp = utf16_to_code_point_prev(chars, len, iter);
619 const hb_script_t init_script = code_point_to_script(init_cp);
620 hb_script_t current_script = init_script;
621 run->script = init_script;
Raph Levien9d47db22013-04-23 12:42:16 -0700622 size_t break_iter = *iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700623
624 for (;;) {
625 if (*iter < 0)
626 break;
Raph Levienaaedde52012-10-30 15:55:33 -0700627 const uint32_t cp = utf16_to_code_point_prev(chars, len, iter);
628 const hb_script_t script = code_point_to_script(cp);
629
630 if (script != current_script) {
631 if (current_script == HB_SCRIPT_INHERITED && init_script == HB_SCRIPT_INHERITED) {
632 // If we started off as inherited, we take whatever we can find.
633 run->script = script;
634 current_script = script;
Raph Levien9d47db22013-04-23 12:42:16 -0700635 // In cases of script1 + inherited + script2, always group the inherited
636 // with script1.
637 break_iter = *iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700638 continue;
639 } else if (script == HB_SCRIPT_INHERITED) {
Raph Levienaaedde52012-10-30 15:55:33 -0700640 continue;
641 } else {
Raph Levien9d47db22013-04-23 12:42:16 -0700642 *iter = break_iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700643 break;
644 }
Raph Levien9d47db22013-04-23 12:42:16 -0700645 } else {
646 break_iter = *iter;
Raph Levienaaedde52012-10-30 15:55:33 -0700647 }
648 }
649
650 if (run->script == HB_SCRIPT_INHERITED)
651 run->script = HB_SCRIPT_COMMON;
652
653 run->pos = *iter + 1;
654 run->length = ending_index - *iter;
655 return true;
656}
657
658
659static void logGlyphs(hb_buffer_t* buffer) {
660 unsigned int numGlyphs;
661 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
662 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
663 ALOGD(" -- glyphs count=%d", numGlyphs);
664 for (size_t i = 0; i < numGlyphs; i++) {
665 ALOGD(" -- glyph[%d] = %d, cluster = %u, advance = %0.2f, offset.x = %0.2f, offset.y = %0.2f", i,
666 info[i].codepoint,
667 info[i].cluster,
668 HBFixedToFloat(positions[i].x_advance),
669 HBFixedToFloat(positions[i].x_offset),
670 HBFixedToFloat(positions[i].y_offset));
Fabrice Di Meglioabb0f292011-08-18 17:59:29 -0700671 }
672}
673
Raph Levienaaedde52012-10-30 15:55:33 -0700674void TextLayoutShaper::computeRunValues(const SkPaint* paint, const UChar* contextChars,
675 size_t start, size_t count, size_t contextCount, bool isRTL,
Chris Craik41541822013-05-03 16:35:54 -0700676 Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance, SkRect* outBounds,
Raph Levien2301d322012-07-17 16:39:49 -0700677 Vector<jchar>* const outGlyphs, Vector<jfloat>* const outPos) {
Jeff Browna03bded2011-12-05 17:36:16 -0800678 if (!count) {
679 // We cannot shape an empty run.
Jeff Browna03bded2011-12-05 17:36:16 -0800680 return;
681 }
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700682
Raph Levien57e97232012-04-24 16:04:34 -0700683 // To be filled in later
684 for (size_t i = 0; i < count; i++) {
685 outAdvances->add(0);
686 }
Fabrice Di Meglio902a5b32011-12-08 18:59:14 -0800687
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800688 // Set the string properties
Raph Levienaaedde52012-10-30 15:55:33 -0700689 const UChar* chars = contextChars + start;
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700690
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800691 // Define shaping paint properties
692 mShapingPaint.setTextSize(paint->getTextSize());
Raph Levien2301d322012-07-17 16:39:49 -0700693 float skewX = paint->getTextSkewX();
694 mShapingPaint.setTextSkewX(skewX);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800695 mShapingPaint.setTextScaleX(paint->getTextScaleX());
696 mShapingPaint.setFlags(paint->getFlags());
697 mShapingPaint.setHinting(paint->getHinting());
Derek Sollenbergerd7a80772013-05-28 10:44:26 -0400698 mShapingPaint.setPaintOptionsAndroid(paint->getPaintOptionsAndroid());
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800699
700 // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
701 // into the shaperItem
Raph Levienaaedde52012-10-30 15:55:33 -0700702 ssize_t indexFontRun = isRTL ? count - 1 : 0;
Raph Levien2301d322012-07-17 16:39:49 -0700703 jfloat totalAdvance = *outTotalAdvance;
Raph Levienaaedde52012-10-30 15:55:33 -0700704 ScriptRun run; // relative to chars
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800705 while ((isRTL) ?
Raph Levienaaedde52012-10-30 15:55:33 -0700706 hb_utf16_script_run_prev(&run, chars, count, &indexFontRun):
707 hb_utf16_script_run_next(&run, chars, count, &indexFontRun)) {
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700708
Fabrice Di Meglio9c418db2011-09-18 12:54:38 -0700709#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000710 ALOGD("-------- Start of Script Run --------");
711 ALOGD("Shaping Script Run with");
712 ALOGD(" -- isRTL = %d", isRTL);
Raph Levienaaedde52012-10-30 15:55:33 -0700713 ALOGD(" -- HB script = %c%c%c%c", HB_UNTAG(run.script));
714 ALOGD(" -- run.pos = %d", int(run.pos));
715 ALOGD(" -- run.length = %d", int(run.length));
716 ALOGD(" -- run = '%s'", String8(chars + run.pos, run.length).string());
Steve Block5baa3a62011-12-20 16:23:08 +0000717 ALOGD(" -- string = '%s'", String8(chars, count).string());
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700718#endif
719
Raph Levienaaedde52012-10-30 15:55:33 -0700720 hb_buffer_reset(mBuffer);
721 // Note: if we want to set unicode functions, etc., this is the place.
722
723 hb_buffer_set_direction(mBuffer, isRTL ? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
724 hb_buffer_set_script(mBuffer, run.script);
Raph Levien677726b2013-10-08 15:45:16 +0200725 SkString langString = paint->getPaintOptionsAndroid().getLanguage().getTag();
726 hb_buffer_set_language(mBuffer, hb_language_from_string(langString.c_str(), -1));
Raph Levienaaedde52012-10-30 15:55:33 -0700727 hb_buffer_add_utf16(mBuffer, contextChars, contextCount, start + run.pos, run.length);
728
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800729 // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
730 // and shape the Font run
Raph Levienaaedde52012-10-30 15:55:33 -0700731 size_t glyphBaseCount = shapeFontRun(paint);
732 unsigned int numGlyphs;
733 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
734 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(mBuffer, NULL);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700735
Fabrice Di Meglio78b868f2011-05-17 19:48:57 -0700736#if DEBUG_GLYPHS
Steve Block5baa3a62011-12-20 16:23:08 +0000737 ALOGD("Got from Harfbuzz");
738 ALOGD(" -- glyphBaseCount = %d", glyphBaseCount);
Raph Levienaaedde52012-10-30 15:55:33 -0700739 ALOGD(" -- num_glyph = %d", numGlyphs);
Steve Block5baa3a62011-12-20 16:23:08 +0000740 ALOGD(" -- isDevKernText = %d", paint->isDevKernText());
Raph Levienaaedde52012-10-30 15:55:33 -0700741 ALOGD(" -- initial totalAdvance = %f", totalAdvance);
Fabrice Di Meglio4dd99e52011-09-19 10:47:10 -0700742
Raph Levienaaedde52012-10-30 15:55:33 -0700743 logGlyphs(mBuffer);
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700744#endif
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700745
Raph Levienaaedde52012-10-30 15:55:33 -0700746 for (size_t i = 0; i < numGlyphs; i++) {
747 size_t cluster = info[i].cluster - start;
748 float xAdvance = HBFixedToFloat(positions[i].x_advance);
749 outAdvances->replaceAt(outAdvances->itemAt(cluster) + xAdvance, cluster);
Chris Craik41541822013-05-03 16:35:54 -0700750 jchar glyphId = info[i].codepoint + glyphBaseCount;
751 outGlyphs->add(glyphId);
Raph Levienaaedde52012-10-30 15:55:33 -0700752 float xo = HBFixedToFloat(positions[i].x_offset);
753 float yo = -HBFixedToFloat(positions[i].y_offset);
Chris Craik41541822013-05-03 16:35:54 -0700754
755 float xpos = totalAdvance + xo + yo * skewX;
756 float ypos = yo;
757 outPos->add(xpos);
758 outPos->add(ypos);
Raph Levienaaedde52012-10-30 15:55:33 -0700759 totalAdvance += xAdvance;
Chris Craik41541822013-05-03 16:35:54 -0700760
Victoria Lease43b692d2013-12-03 15:02:28 -0800761 SkAutoGlyphCache autoCache(mShapingPaint, NULL, NULL);
762 const SkGlyph& metrics = autoCache.getCache()->getGlyphIDMetrics(glyphId);
Chris Craik41541822013-05-03 16:35:54 -0700763 outBounds->join(xpos + metrics.fLeft, ypos + metrics.fTop,
764 xpos + metrics.fLeft + metrics.fWidth, ypos + metrics.fTop + metrics.fHeight);
765
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700766 }
Fabrice Di Megliofcf2be12011-04-05 17:02:36 -0700767 }
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800768
Fabrice Di Meglioef9bb3c2011-10-17 11:06:46 -0700769 *outTotalAdvance = totalAdvance;
Fabrice Di Meglio5448f032011-12-02 15:56:19 -0800770
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800771#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700772 ALOGD(" -- final totalAdvance = %f", totalAdvance);
Steve Block5baa3a62011-12-20 16:23:08 +0000773 ALOGD("-------- End of Script Run --------");
Fabrice Di Meglio56e6e542011-11-30 15:48:18 -0800774#endif
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700775}
776
Raph Levien1637dcd2012-05-02 10:41:14 -0700777/**
778 * Return the first typeface in the logical change, starting with this typeface,
779 * that contains the specified unichar, or NULL if none is found.
Raph Levien1637dcd2012-05-02 10:41:14 -0700780 */
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700781SkTypeface* TextLayoutShaper::typefaceForScript(const SkPaint* paint, SkTypeface* typeface,
Raph Levienaaedde52012-10-30 15:55:33 -0700782 hb_script_t script) {
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700783 SkTypeface::Style currentStyle = SkTypeface::kNormal;
784 if (typeface) {
785 currentStyle = typeface->style();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800786 }
Raph Levienaaedde52012-10-30 15:55:33 -0700787 typeface = SkCreateTypefaceForScriptNG(script, currentStyle);
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700788#if DEBUG_GLYPHS
Victoria Leaseded5ed92013-02-12 13:36:22 -0800789 ALOGD("Using Harfbuzz Script %c%c%c%c, Style %d", HB_UNTAG(script), currentStyle);
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700790#endif
Raph Levien1637dcd2012-05-02 10:41:14 -0700791 return typeface;
792}
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800793
Raph Levienaaedde52012-10-30 15:55:33 -0700794bool TextLayoutShaper::isComplexScript(hb_script_t script) {
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700795 switch (script) {
Raph Levienaaedde52012-10-30 15:55:33 -0700796 case HB_SCRIPT_COMMON:
797 case HB_SCRIPT_GREEK:
798 case HB_SCRIPT_CYRILLIC:
799 case HB_SCRIPT_HANGUL:
800 case HB_SCRIPT_INHERITED:
Junichi Monma6d191ed2013-01-17 16:13:22 +0900801 case HB_SCRIPT_HAN:
802 case HB_SCRIPT_KATAKANA:
803 case HB_SCRIPT_HIRAGANA:
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700804 return false;
805 default:
806 return true;
807 }
808}
809
Raph Levienaaedde52012-10-30 15:55:33 -0700810size_t TextLayoutShaper::shapeFontRun(const SkPaint* paint) {
Raph Levien1637dcd2012-05-02 10:41:14 -0700811 // Update Harfbuzz Shaper
Raph Levien1637dcd2012-05-02 10:41:14 -0700812
813 SkTypeface* typeface = paint->getTypeface();
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800814
815 // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
816 // This is needed as the Typeface used for shaping can be not the default one
817 // when we are shaping any script that needs to use a fallback Font.
818 // If we are a "common" script we dont need to shift
819 size_t baseGlyphCount = 0;
Raph Levienaaedde52012-10-30 15:55:33 -0700820 hb_codepoint_t firstUnichar = 0;
821 if (isComplexScript(hb_buffer_get_script(mBuffer))) {
822 unsigned int numGlyphs;
823 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(mBuffer, &numGlyphs);
824 for (size_t i = 0; i < numGlyphs; i++) {
825 firstUnichar = info[i].codepoint;
826 if (firstUnichar != ' ') {
827 break;
828 }
Raph Levienb2944352012-05-01 09:41:50 -0700829 }
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800830 baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800831 }
832
Raph Leviendd0a9122013-05-06 15:00:41 -0700833 SkTypeface* scriptTypeface = NULL;
Raph Levien1637dcd2012-05-02 10:41:14 -0700834 if (baseGlyphCount != 0) {
Raph Leviendd0a9122013-05-06 15:00:41 -0700835 scriptTypeface = typefaceForScript(paint, typeface,
836 hb_buffer_get_script(mBuffer));
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700837#if DEBUG_GLYPHS
Raph Leviendd0a9122013-05-06 15:00:41 -0700838 ALOGD("Using Default Typeface for script %c%c%c%c",
839 HB_UNTAG(hb_buffer_get_script(mBuffer)));
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700840#endif
Raph Leviendd0a9122013-05-06 15:00:41 -0700841 }
842 if (scriptTypeface) {
843 typeface = scriptTypeface;
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700844 } else {
Raph Leviendd0a9122013-05-06 15:00:41 -0700845 baseGlyphCount = 0;
846 if (typeface) {
847 SkSafeRef(typeface);
848 } else {
Victoria Leaseaf1653a2013-04-17 10:20:23 -0700849 typeface = SkTypeface::CreateFromName(NULL, SkTypeface::kNormal);
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700850#if DEBUG_GLYPHS
Victoria Leasecc0f9d82013-04-15 15:29:36 -0700851 ALOGD("Using Default Typeface (normal style)");
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700852#endif
853 }
Raph Levien1637dcd2012-05-02 10:41:14 -0700854 }
855
Raph Levien1637dcd2012-05-02 10:41:14 -0700856 mShapingPaint.setTypeface(typeface);
Raph Levienaaedde52012-10-30 15:55:33 -0700857 hb_face_t* face = referenceCachedHBFace(typeface);
Raph Levien1637dcd2012-05-02 10:41:14 -0700858
Raph Levienaaedde52012-10-30 15:55:33 -0700859 float sizeY = paint->getTextSize();
860 float sizeX = sizeY * paint->getTextScaleX();
861 hb_font_t* font = createFont(face, &mShapingPaint, sizeX, sizeY);
862 hb_face_destroy(face);
Raph Levien2301d322012-07-17 16:39:49 -0700863
Raph Levien1637dcd2012-05-02 10:41:14 -0700864#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700865 ALOGD("Run typeface = %p, uniqueID = %d, face = %p",
866 typeface, typeface->uniqueID(), face);
Raph Levien1637dcd2012-05-02 10:41:14 -0700867#endif
Billy Hewlettd6deccb2012-06-22 10:30:55 -0700868 SkSafeUnref(typeface);
Raph Levien1637dcd2012-05-02 10:41:14 -0700869
Raph Levienaaedde52012-10-30 15:55:33 -0700870 hb_shape(font, mBuffer, NULL, 0);
871 hb_font_destroy(font);
872
Victoria Lease8450a6e2013-10-04 17:33:35 -0700873 mShapingPaint.setTypeface(paint->getTypeface());
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800874 return baseGlyphCount;
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700875}
876
Raph Levienaaedde52012-10-30 15:55:33 -0700877hb_face_t* TextLayoutShaper::referenceCachedHBFace(SkTypeface* typeface) {
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800878 SkFontID fontId = typeface->uniqueID();
879 ssize_t index = mCachedHBFaces.indexOfKey(fontId);
880 if (index >= 0) {
Raph Levienaaedde52012-10-30 15:55:33 -0700881 return hb_face_reference(mCachedHBFaces.valueAt(index));
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800882 }
Raph Levienaaedde52012-10-30 15:55:33 -0700883 // TODO: destroy function
884 hb_face_t* face = hb_face_create_for_tables(harfbuzzSkiaReferenceTable, typeface, NULL);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800885#if DEBUG_GLYPHS
Raph Levienaaedde52012-10-30 15:55:33 -0700886 ALOGD("Created HB_NewFace %p from paint typeface = %p", face, typeface);
Fabrice Di Meglio0af10b52011-11-18 17:36:41 -0800887#endif
Raph Levienaaedde52012-10-30 15:55:33 -0700888 mCachedHBFaces.add(fontId, face);
889 return hb_face_reference(face);
Fabrice Di Meglio79df5322011-09-19 15:17:56 -0700890}
Fabrice Di Meglio48796a82011-04-05 15:22:41 -0700891
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700892void TextLayoutShaper::purgeCaches() {
Raph Levien75394d62012-06-02 15:47:29 -0700893 size_t cacheSize = mCachedHBFaces.size();
894 for (size_t i = 0; i < cacheSize; i++) {
Raph Levienaaedde52012-10-30 15:55:33 -0700895 hb_face_destroy(mCachedHBFaces.valueAt(i));
Raph Levien75394d62012-06-02 15:47:29 -0700896 }
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700897 mCachedHBFaces.clear();
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700898}
899
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800900TextLayoutEngine::TextLayoutEngine() {
901 mShaper = new TextLayoutShaper();
902#if USE_TEXT_LAYOUT_CACHE
903 mTextLayoutCache = new TextLayoutCache(mShaper);
904#else
905 mTextLayoutCache = NULL;
906#endif
907}
908
909TextLayoutEngine::~TextLayoutEngine() {
910 delete mTextLayoutCache;
911 delete mShaper;
912}
913
914sp<TextLayoutValue> TextLayoutEngine::getValue(const SkPaint* paint, const jchar* text,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700915 jint start, jint count, jint contextCount, jint dirFlags) {
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800916 sp<TextLayoutValue> value;
917#if USE_TEXT_LAYOUT_CACHE
918 value = mTextLayoutCache->getValue(paint, text, start, count,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700919 contextCount, dirFlags);
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800920 if (value == NULL) {
921 ALOGE("Cannot get TextLayoutCache value for text = '%s'",
922 String8(text + start, count).string());
923 }
924#else
925 value = new TextLayoutValue(count);
926 mShaper->computeValues(value.get(), paint,
Fabrice Di Meglioda12f382013-03-15 11:26:56 -0700927 reinterpret_cast<const UChar*>(text), start, count, contextCount, dirFlags);
Fabrice Di Meglioa731b082012-01-23 18:18:45 -0800928#endif
929 return value;
930}
931
Fabrice Di Meglio30ca5cd2012-05-07 17:45:44 -0700932void TextLayoutEngine::purgeCaches() {
933#if USE_TEXT_LAYOUT_CACHE
Raph Levien13ba4e42012-09-12 15:15:51 -0700934 mTextLayoutCache->purgeCaches();
Fabrice Di Meglio15cc68c2012-05-15 14:47:03 -0700935#if DEBUG_GLYPHS
936 ALOGD("Purged TextLayoutEngine caches");
937#endif
Fabrice Di Meglio30ca5cd2012-05-07 17:45:44 -0700938#endif
939}
940
941
Fabrice Di Megliod313c662011-02-24 19:56:18 -0800942} // namespace android