1 /*
2 * Copyright (C) 2013 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
17 #define LOG_TAG "Minikin"
18 #include <cutils/log.h>
19
20 #include <string>
21 #include <vector>
22 #include <algorithm>
23 #include <fstream>
24 #include <iostream> // for debugging
25 #include <stdio.h> // ditto
26
27 #include <utils/JenkinsHash.h>
28 #include <utils/LruCache.h>
29 #include <utils/Singleton.h>
30 #include <utils/String16.h>
31
32 #include <unicode/ubidi.h>
33 #include <hb-icu.h>
34
35 #include "MinikinInternal.h"
36 #include <minikin/MinikinFontFreeType.h>
37 #include <minikin/Layout.h>
38
39 using std::string;
40 using std::vector;
41
42 namespace android {
43
44 // TODO: these should move into the header file, but for now we don't want
45 // to cause namespace collisions with TextLayout.h
46 enum {
47 kBidi_LTR = 0,
48 kBidi_RTL = 1,
49 kBidi_Default_LTR = 2,
50 kBidi_Default_RTL = 3,
51 kBidi_Force_LTR = 4,
52 kBidi_Force_RTL = 5,
53
54 kBidi_Mask = 0x7
55 };
56
57 const int kDirection_Mask = 0x1;
58
59 struct LayoutContext {
60 MinikinPaint paint;
61 FontStyle style;
62 std::vector<hb_font_t*> hbFonts; // parallel to mFaces
63
clearHbFontsandroid::LayoutContext64 void clearHbFonts() {
65 for (size_t i = 0; i < hbFonts.size(); i++) {
66 hb_font_destroy(hbFonts[i]);
67 }
68 hbFonts.clear();
69 }
70 };
71
72 // Layout cache datatypes
73
74 class LayoutCacheKey {
75 public:
LayoutCacheKey(const FontCollection * collection,const MinikinPaint & paint,FontStyle style,const uint16_t * chars,size_t start,size_t count,size_t nchars,bool dir)76 LayoutCacheKey(const FontCollection* collection, const MinikinPaint& paint, FontStyle style,
77 const uint16_t* chars, size_t start, size_t count, size_t nchars, bool dir)
78 : mStart(start), mCount(count), mId(collection->getId()), mStyle(style),
79 mSize(paint.size), mScaleX(paint.scaleX), mSkewX(paint.skewX),
80 mLetterSpacing(paint.letterSpacing),
81 mPaintFlags(paint.paintFlags), mIsRtl(dir),
82 mChars(chars), mNchars(nchars) {
83 }
84 bool operator==(const LayoutCacheKey &other) const;
85 hash_t hash() const;
86
copyText()87 void copyText() {
88 uint16_t* charsCopy = new uint16_t[mNchars];
89 memcpy(charsCopy, mChars, mNchars * sizeof(uint16_t));
90 mChars = charsCopy;
91 }
freeText()92 void freeText() {
93 delete[] mChars;
94 mChars = NULL;
95 }
96
doLayout(Layout * layout,LayoutContext * ctx,const FontCollection * collection) const97 void doLayout(Layout* layout, LayoutContext* ctx, const FontCollection* collection) const {
98 layout->setFontCollection(collection);
99 layout->mAdvances.resize(mCount, 0);
100 ctx->clearHbFonts();
101 layout->doLayoutRun(mChars, mStart, mCount, mNchars, mIsRtl, ctx);
102 }
103
104 private:
105 const uint16_t* mChars;
106 size_t mNchars;
107 size_t mStart;
108 size_t mCount;
109 uint32_t mId; // for the font collection
110 FontStyle mStyle;
111 float mSize;
112 float mScaleX;
113 float mSkewX;
114 float mLetterSpacing;
115 int32_t mPaintFlags;
116 bool mIsRtl;
117 // Note: any fields added to MinikinPaint must also be reflected here.
118 // TODO: language matching (possibly integrate into style)
119 };
120
121 class LayoutCache : private OnEntryRemoved<LayoutCacheKey, Layout*> {
122 public:
LayoutCache()123 LayoutCache() : mCache(kMaxEntries) {
124 mCache.setOnEntryRemovedListener(this);
125 }
126
clear()127 void clear() {
128 mCache.clear();
129 }
130
get(LayoutCacheKey & key,LayoutContext * ctx,const FontCollection * collection)131 Layout* get(LayoutCacheKey& key, LayoutContext* ctx, const FontCollection* collection) {
132 Layout* layout = mCache.get(key);
133 if (layout == NULL) {
134 key.copyText();
135 layout = new Layout();
136 key.doLayout(layout, ctx, collection);
137 mCache.put(key, layout);
138 }
139 return layout;
140 }
141
142 private:
143 // callback for OnEntryRemoved
operator ()(LayoutCacheKey & key,Layout * & value)144 void operator()(LayoutCacheKey& key, Layout*& value) {
145 key.freeText();
146 delete value;
147 }
148
149 LruCache<LayoutCacheKey, Layout*> mCache;
150
151 //static const size_t kMaxEntries = LruCache<LayoutCacheKey, Layout*>::kUnlimitedCapacity;
152
153 // TODO: eviction based on memory footprint; for now, we just use a constant
154 // number of strings
155 static const size_t kMaxEntries = 5000;
156 };
157
158 class HbFaceCache : private OnEntryRemoved<int32_t, hb_face_t*> {
159 public:
HbFaceCache()160 HbFaceCache() : mCache(kMaxEntries) {
161 mCache.setOnEntryRemovedListener(this);
162 }
163
164 // callback for OnEntryRemoved
operator ()(int32_t & key,hb_face_t * & value)165 void operator()(int32_t& key, hb_face_t*& value) {
166 hb_face_destroy(value);
167 }
168
169 LruCache<int32_t, hb_face_t*> mCache;
170 private:
171 static const size_t kMaxEntries = 100;
172 };
173
174 class LayoutEngine : public Singleton<LayoutEngine> {
175 public:
LayoutEngine()176 LayoutEngine() {
177 hbBuffer = hb_buffer_create();
178 }
179
180 hb_buffer_t* hbBuffer;
181 LayoutCache layoutCache;
182 HbFaceCache hbFaceCache;
183 };
184
185 ANDROID_SINGLETON_STATIC_INSTANCE(LayoutEngine);
186
operator ==(const LayoutCacheKey & other) const187 bool LayoutCacheKey::operator==(const LayoutCacheKey& other) const {
188 return mId == other.mId
189 && mStart == other.mStart
190 && mCount == other.mCount
191 && mStyle == other.mStyle
192 && mSize == other.mSize
193 && mScaleX == other.mScaleX
194 && mSkewX == other.mSkewX
195 && mLetterSpacing == other.mLetterSpacing
196 && mPaintFlags == other.mPaintFlags
197 && mIsRtl == other.mIsRtl
198 && mNchars == other.mNchars
199 && !memcmp(mChars, other.mChars, mNchars * sizeof(uint16_t));
200 }
201
hash() const202 hash_t LayoutCacheKey::hash() const {
203 uint32_t hash = JenkinsHashMix(0, mId);
204 hash = JenkinsHashMix(hash, mStart);
205 hash = JenkinsHashMix(hash, mCount);
206 hash = JenkinsHashMix(hash, hash_type(mStyle));
207 hash = JenkinsHashMix(hash, hash_type(mSize));
208 hash = JenkinsHashMix(hash, hash_type(mScaleX));
209 hash = JenkinsHashMix(hash, hash_type(mSkewX));
210 hash = JenkinsHashMix(hash, hash_type(mLetterSpacing));
211 hash = JenkinsHashMix(hash, hash_type(mPaintFlags));
212 hash = JenkinsHashMix(hash, hash_type(mIsRtl));
213 hash = JenkinsHashMixShorts(hash, mChars, mNchars);
214 return JenkinsHashWhiten(hash);
215 }
216
hash_type(const LayoutCacheKey & key)217 hash_t hash_type(const LayoutCacheKey& key) {
218 return key.hash();
219 }
220
Bitmap(int width,int height)221 Bitmap::Bitmap(int width, int height) : width(width), height(height) {
222 buf = new uint8_t[width * height]();
223 }
224
~Bitmap()225 Bitmap::~Bitmap() {
226 delete[] buf;
227 }
228
writePnm(std::ofstream & o) const229 void Bitmap::writePnm(std::ofstream &o) const {
230 o << "P5" << std::endl;
231 o << width << " " << height << std::endl;
232 o << "255" << std::endl;
233 o.write((const char *)buf, width * height);
234 o.close();
235 }
236
drawGlyph(const GlyphBitmap & bitmap,int x,int y)237 void Bitmap::drawGlyph(const GlyphBitmap& bitmap, int x, int y) {
238 int bmw = bitmap.width;
239 int bmh = bitmap.height;
240 x += bitmap.left;
241 y -= bitmap.top;
242 int x0 = std::max(0, x);
243 int x1 = std::min(width, x + bmw);
244 int y0 = std::max(0, y);
245 int y1 = std::min(height, y + bmh);
246 const unsigned char* src = bitmap.buffer + (y0 - y) * bmw + (x0 - x);
247 uint8_t* dst = buf + y0 * width;
248 for (int yy = y0; yy < y1; yy++) {
249 for (int xx = x0; xx < x1; xx++) {
250 int pixel = (int)dst[xx] + (int)src[xx - x];
251 pixel = pixel > 0xff ? 0xff : pixel;
252 dst[xx] = pixel;
253 }
254 src += bmw;
255 dst += width;
256 }
257 }
258
join(const MinikinRect & r)259 void MinikinRect::join(const MinikinRect& r) {
260 if (isEmpty()) {
261 set(r);
262 } else if (!r.isEmpty()) {
263 mLeft = std::min(mLeft, r.mLeft);
264 mTop = std::min(mTop, r.mTop);
265 mRight = std::max(mRight, r.mRight);
266 mBottom = std::max(mBottom, r.mBottom);
267 }
268 }
269
270 // Deprecated. Remove when callers are removed.
init()271 void Layout::init() {
272 }
273
reset()274 void Layout::reset() {
275 mGlyphs.clear();
276 mFaces.clear();
277 mBounds.setEmpty();
278 mAdvances.clear();
279 mAdvance = 0;
280 }
281
setFontCollection(const FontCollection * collection)282 void Layout::setFontCollection(const FontCollection* collection) {
283 mCollection = collection;
284 }
285
referenceTable(hb_face_t * face,hb_tag_t tag,void * userData)286 hb_blob_t* referenceTable(hb_face_t* face, hb_tag_t tag, void* userData) {
287 MinikinFont* font = reinterpret_cast<MinikinFont*>(userData);
288 size_t length = 0;
289 bool ok = font->GetTable(tag, NULL, &length);
290 if (!ok) {
291 return 0;
292 }
293 char* buffer = reinterpret_cast<char*>(malloc(length));
294 if (!buffer) {
295 return 0;
296 }
297 ok = font->GetTable(tag, reinterpret_cast<uint8_t*>(buffer), &length);
298 printf("referenceTable %c%c%c%c length=%d %d\n",
299 (tag >>24) & 0xff, (tag>>16)&0xff, (tag>>8)&0xff, tag&0xff, length, ok);
300 if (!ok) {
301 free(buffer);
302 return 0;
303 }
304 return hb_blob_create(const_cast<char*>(buffer), length,
305 HB_MEMORY_MODE_WRITABLE, buffer, free);
306 }
307
harfbuzzGetGlyph(hb_font_t * hbFont,void * fontData,hb_codepoint_t unicode,hb_codepoint_t variationSelector,hb_codepoint_t * glyph,void * userData)308 static hb_bool_t harfbuzzGetGlyph(hb_font_t* hbFont, void* fontData, hb_codepoint_t unicode, hb_codepoint_t variationSelector, hb_codepoint_t* glyph, void* userData)
309 {
310 MinikinPaint* paint = reinterpret_cast<MinikinPaint*>(fontData);
311 MinikinFont* font = paint->font;
312 uint32_t glyph_id;
313 /* HarfBuzz replaces broken input codepoints with (unsigned int) -1.
314 * Skia expects valid Unicode.
315 * Replace invalid codepoints with U+FFFD REPLACEMENT CHARACTER.
316 */
317 if (unicode > 0x10FFFF)
318 unicode = 0xFFFD;
319 bool ok = font->GetGlyph(unicode, &glyph_id);
320 if (ok) {
321 *glyph = glyph_id;
322 }
323 return ok;
324 }
325
harfbuzzGetGlyphHorizontalAdvance(hb_font_t * hbFont,void * fontData,hb_codepoint_t glyph,void * userData)326 static hb_position_t harfbuzzGetGlyphHorizontalAdvance(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, void* userData)
327 {
328 MinikinPaint* paint = reinterpret_cast<MinikinPaint*>(fontData);
329 MinikinFont* font = paint->font;
330 float advance = font->GetHorizontalAdvance(glyph, *paint);
331 return 256 * advance + 0.5;
332 }
333
harfbuzzGetGlyphHorizontalOrigin(hb_font_t * hbFont,void * fontData,hb_codepoint_t glyph,hb_position_t * x,hb_position_t * y,void * userData)334 static hb_bool_t harfbuzzGetGlyphHorizontalOrigin(hb_font_t* hbFont, void* fontData, hb_codepoint_t glyph, hb_position_t* x, hb_position_t* y, void* userData)
335 {
336 // Just return true, following the way that Harfbuzz-FreeType
337 // implementation does.
338 return true;
339 }
340
getHbFontFuncs()341 hb_font_funcs_t* getHbFontFuncs() {
342 static hb_font_funcs_t* hbFontFuncs = 0;
343
344 if (hbFontFuncs == 0) {
345 hbFontFuncs = hb_font_funcs_create();
346 hb_font_funcs_set_glyph_func(hbFontFuncs, harfbuzzGetGlyph, 0, 0);
347 hb_font_funcs_set_glyph_h_advance_func(hbFontFuncs, harfbuzzGetGlyphHorizontalAdvance, 0, 0);
348 hb_font_funcs_set_glyph_h_origin_func(hbFontFuncs, harfbuzzGetGlyphHorizontalOrigin, 0, 0);
349 hb_font_funcs_make_immutable(hbFontFuncs);
350 }
351 return hbFontFuncs;
352 }
353
getHbFace(MinikinFont * minikinFont)354 static hb_face_t* getHbFace(MinikinFont* minikinFont) {
355 HbFaceCache& cache = LayoutEngine::getInstance().hbFaceCache;
356 int32_t fontId = minikinFont->GetUniqueId();
357 hb_face_t* face = cache.mCache.get(fontId);
358 if (face == NULL) {
359 face = hb_face_create_for_tables(referenceTable, minikinFont, NULL);
360 cache.mCache.put(fontId, face);
361 }
362 return face;
363 }
364
create_hb_font(MinikinFont * minikinFont,MinikinPaint * minikinPaint)365 static hb_font_t* create_hb_font(MinikinFont* minikinFont, MinikinPaint* minikinPaint) {
366 hb_face_t* face = getHbFace(minikinFont);
367 hb_font_t* font = hb_font_create(face);
368 hb_font_set_funcs(font, getHbFontFuncs(), minikinPaint, 0);
369 return font;
370 }
371
HBFixedToFloat(hb_position_t v)372 static float HBFixedToFloat(hb_position_t v)
373 {
374 return scalbnf (v, -8);
375 }
376
HBFloatToFixed(float v)377 static hb_position_t HBFloatToFixed(float v)
378 {
379 return scalbnf (v, +8);
380 }
381
dump() const382 void Layout::dump() const {
383 for (size_t i = 0; i < mGlyphs.size(); i++) {
384 const LayoutGlyph& glyph = mGlyphs[i];
385 std::cout << glyph.glyph_id << ": " << glyph.x << ", " << glyph.y << std::endl;
386 }
387 }
388
findFace(FakedFont face,LayoutContext * ctx)389 int Layout::findFace(FakedFont face, LayoutContext* ctx) {
390 unsigned int ix;
391 for (ix = 0; ix < mFaces.size(); ix++) {
392 if (mFaces[ix].font == face.font) {
393 return ix;
394 }
395 }
396 mFaces.push_back(face);
397 // Note: ctx == NULL means we're copying from the cache, no need to create
398 // corresponding hb_font object.
399 if (ctx != NULL) {
400 hb_font_t* font = create_hb_font(face.font, &ctx->paint);
401 ctx->hbFonts.push_back(font);
402 }
403 return ix;
404 }
405
codePointToScript(hb_codepoint_t codepoint)406 static hb_script_t codePointToScript(hb_codepoint_t codepoint) {
407 static hb_unicode_funcs_t* u = 0;
408 if (!u) {
409 u = hb_icu_get_unicode_funcs();
410 }
411 return hb_unicode_script(u, codepoint);
412 }
413
decodeUtf16(const uint16_t * chars,size_t len,ssize_t * iter)414 static hb_codepoint_t decodeUtf16(const uint16_t* chars, size_t len, ssize_t* iter) {
415 const uint16_t v = chars[(*iter)++];
416 // test whether v in (0xd800..0xdfff), lead or trail surrogate
417 if ((v & 0xf800) == 0xd800) {
418 // test whether v in (0xd800..0xdbff), lead surrogate
419 if (size_t(*iter) < len && (v & 0xfc00) == 0xd800) {
420 const uint16_t v2 = chars[(*iter)++];
421 // test whether v2 in (0xdc00..0xdfff), trail surrogate
422 if ((v2 & 0xfc00) == 0xdc00) {
423 // (0xd800 0xdc00) in utf-16 maps to 0x10000 in ucs-32
424 const hb_codepoint_t delta = (0xd800 << 10) + 0xdc00 - 0x10000;
425 return (((hb_codepoint_t)v) << 10) + v2 - delta;
426 }
427 (*iter) -= 1;
428 return 0xFFFDu;
429 } else {
430 return 0xFFFDu;
431 }
432 } else {
433 return v;
434 }
435 }
436
getScriptRun(const uint16_t * chars,size_t len,ssize_t * iter)437 static hb_script_t getScriptRun(const uint16_t* chars, size_t len, ssize_t* iter) {
438 if (size_t(*iter) == len) {
439 return HB_SCRIPT_UNKNOWN;
440 }
441 uint32_t cp = decodeUtf16(chars, len, iter);
442 hb_script_t current_script = codePointToScript(cp);
443 for (;;) {
444 if (size_t(*iter) == len)
445 break;
446 const ssize_t prev_iter = *iter;
447 cp = decodeUtf16(chars, len, iter);
448 const hb_script_t script = codePointToScript(cp);
449 if (script != current_script) {
450 if (current_script == HB_SCRIPT_INHERITED ||
451 current_script == HB_SCRIPT_COMMON) {
452 current_script = script;
453 } else if (script == HB_SCRIPT_INHERITED ||
454 script == HB_SCRIPT_COMMON) {
455 continue;
456 } else {
457 *iter = prev_iter;
458 break;
459 }
460 }
461 }
462 if (current_script == HB_SCRIPT_INHERITED) {
463 current_script = HB_SCRIPT_COMMON;
464 }
465
466 return current_script;
467 }
468
469 /**
470 * For the purpose of layout, a word break is a boundary with no
471 * kerning or complex script processing. This is necessarily a
472 * heuristic, but should be accurate most of the time.
473 */
isWordBreak(int c)474 static bool isWordBreak(int c) {
475 if (c == ' ' || (c >= 0x2000 && c <= 0x200a) || c == 0x3000) {
476 // spaces
477 return true;
478 }
479 if ((c >= 0x3400 && c <= 0x9fff)) {
480 // CJK ideographs (and yijing hexagram symbols)
481 return true;
482 }
483 // Note: kana is not included, as sophisticated fonts may kern kana
484 return false;
485 }
486
487 /**
488 * Return offset of previous word break. It is either < offset or == 0.
489 */
getPrevWordBreak(const uint16_t * chars,size_t offset)490 static size_t getPrevWordBreak(const uint16_t* chars, size_t offset) {
491 if (offset == 0) return 0;
492 if (isWordBreak(chars[offset - 1])) {
493 return offset - 1;
494 }
495 for (size_t i = offset - 1; i > 0; i--) {
496 if (isWordBreak(chars[i - 1])) {
497 return i;
498 }
499 }
500 return 0;
501 }
502
503 /**
504 * Return offset of next word break. It is either > offset or == len.
505 */
getNextWordBreak(const uint16_t * chars,size_t offset,size_t len)506 static size_t getNextWordBreak(const uint16_t* chars, size_t offset, size_t len) {
507 if (offset >= len) return len;
508 if (isWordBreak(chars[offset])) {
509 return offset + 1;
510 }
511 for (size_t i = offset + 1; i < len; i++) {
512 if (isWordBreak(chars[i])) {
513 return i;
514 }
515 }
516 return len;
517 }
518
doLayout(const uint16_t * buf,size_t start,size_t count,size_t bufSize,int bidiFlags,const FontStyle & style,const MinikinPaint & paint)519 void Layout::doLayout(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
520 int bidiFlags, const FontStyle &style, const MinikinPaint &paint) {
521 AutoMutex _l(gMinikinLock);
522
523 LayoutContext ctx;
524 ctx.style = style;
525 ctx.paint = paint;
526
527 bool isRtl = (bidiFlags & kDirection_Mask) != 0;
528 bool doSingleRun = true;
529
530 reset();
531 mAdvances.resize(count, 0);
532
533 if (!(bidiFlags == kBidi_Force_LTR || bidiFlags == kBidi_Force_RTL)) {
534 UBiDi* bidi = ubidi_open();
535 if (bidi) {
536 UErrorCode status = U_ZERO_ERROR;
537 UBiDiLevel bidiReq = bidiFlags;
538 if (bidiFlags == kBidi_Default_LTR) {
539 bidiReq = UBIDI_DEFAULT_LTR;
540 } else if (bidiFlags == kBidi_Default_RTL) {
541 bidiReq = UBIDI_DEFAULT_RTL;
542 }
543 ubidi_setPara(bidi, buf, bufSize, bidiReq, NULL, &status);
544 if (U_SUCCESS(status)) {
545 int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask;
546 ssize_t rc = ubidi_countRuns(bidi, &status);
547 if (!U_SUCCESS(status) || rc < 0) {
548 ALOGW("error counting bidi runs, status = %d", status);
549 }
550 if (!U_SUCCESS(status) || rc <= 1) {
551 isRtl = (paraDir == kBidi_RTL);
552 } else {
553 doSingleRun = false;
554 // iterate through runs
555 for (ssize_t i = 0; i < (ssize_t)rc; i++) {
556 int32_t startRun = -1;
557 int32_t lengthRun = -1;
558 UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
559 if (startRun == -1 || lengthRun == -1) {
560 ALOGE("invalid visual run");
561 // skip the invalid run
562 continue;
563 }
564 int32_t endRun = std::min(startRun + lengthRun, int32_t(start + count));
565 startRun = std::max(startRun, int32_t(start));
566 lengthRun = endRun - startRun;
567 if (lengthRun > 0) {
568 isRtl = (runDir == UBIDI_RTL);
569 doLayoutRunCached(buf, startRun, lengthRun, bufSize, isRtl, &ctx,
570 start);
571 }
572 }
573 }
574 } else {
575 ALOGE("error calling ubidi_setPara, status = %d", status);
576 }
577 ubidi_close(bidi);
578 } else {
579 ALOGE("error creating bidi object");
580 }
581 }
582 if (doSingleRun) {
583 doLayoutRunCached(buf, start, count, bufSize, isRtl, &ctx, start);
584 }
585 ctx.clearHbFonts();
586 }
587
doLayoutRunCached(const uint16_t * buf,size_t start,size_t count,size_t bufSize,bool isRtl,LayoutContext * ctx,size_t dstStart)588 void Layout::doLayoutRunCached(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
589 bool isRtl, LayoutContext* ctx, size_t dstStart) {
590 if (!isRtl) {
591 // left to right
592 size_t wordstart = start == bufSize ? start : getPrevWordBreak(buf, start + 1);
593 size_t wordend;
594 for (size_t iter = start; iter < start + count; iter = wordend) {
595 wordend = getNextWordBreak(buf, iter, bufSize);
596 size_t wordcount = std::min(start + count, wordend) - iter;
597 doLayoutWord(buf + wordstart, iter - wordstart, wordcount, wordend - wordstart,
598 isRtl, ctx, iter - dstStart);
599 wordstart = wordend;
600 }
601 } else {
602 // right to left
603 size_t wordstart;
604 size_t end = start + count;
605 size_t wordend = end == 0 ? 0 : getNextWordBreak(buf, end - 1, bufSize);
606 for (size_t iter = end; iter > start; iter = wordstart) {
607 wordstart = getPrevWordBreak(buf, iter);
608 size_t bufStart = std::max(start, wordstart);
609 doLayoutWord(buf + wordstart, bufStart - wordstart, iter - bufStart,
610 wordend - wordstart, isRtl, ctx, bufStart - dstStart);
611 wordend = wordstart;
612 }
613 }
614 }
615
doLayoutWord(const uint16_t * buf,size_t start,size_t count,size_t bufSize,bool isRtl,LayoutContext * ctx,size_t bufStart)616 void Layout::doLayoutWord(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
617 bool isRtl, LayoutContext* ctx, size_t bufStart) {
618 LayoutCache& cache = LayoutEngine::getInstance().layoutCache;
619 LayoutCacheKey key(mCollection, ctx->paint, ctx->style, buf, start, count, bufSize, isRtl);
620 bool skipCache = ctx->paint.skipCache();
621 if (skipCache) {
622 Layout layout;
623 key.doLayout(&layout, ctx, mCollection);
624 appendLayout(&layout, bufStart);
625 } else {
626 Layout* layout = cache.get(key, ctx, mCollection);
627 appendLayout(layout, bufStart);
628 }
629 }
630
addFeatures(const string & str,vector<hb_feature_t> * features)631 static void addFeatures(const string &str, vector<hb_feature_t>* features) {
632 if (!str.size())
633 return;
634
635 const char* start = str.c_str();
636 const char* end = start + str.size();
637
638 while (start < end) {
639 static hb_feature_t feature;
640 const char* p = strchr(start, ',');
641 if (!p)
642 p = end;
643 /* We do not allow setting features on ranges. As such, reject any
644 * setting that has non-universal range. */
645 if (hb_feature_from_string (start, p - start, &feature)
646 && feature.start == 0 && feature.end == (unsigned int) -1)
647 features->push_back(feature);
648 start = p + 1;
649 }
650 }
651
doLayoutRun(const uint16_t * buf,size_t start,size_t count,size_t bufSize,bool isRtl,LayoutContext * ctx)652 void Layout::doLayoutRun(const uint16_t* buf, size_t start, size_t count, size_t bufSize,
653 bool isRtl, LayoutContext* ctx) {
654 hb_buffer_t* buffer = LayoutEngine::getInstance().hbBuffer;
655 vector<FontCollection::Run> items;
656 mCollection->itemize(buf + start, count, ctx->style, &items);
657 if (isRtl) {
658 std::reverse(items.begin(), items.end());
659 }
660
661 vector<hb_feature_t> features;
662 // Disable default-on non-required ligature features if letter-spacing
663 // See http://dev.w3.org/csswg/css-text-3/#letter-spacing-property
664 // "When the effective spacing between two characters is not zero (due to
665 // either justification or a non-zero value of letter-spacing), user agents
666 // should not apply optional ligatures."
667 if (fabs(ctx->paint.letterSpacing) > 0.03)
668 {
669 static const hb_feature_t no_liga = { HB_TAG('l', 'i', 'g', 'a'), 0, 0, ~0u };
670 static const hb_feature_t no_clig = { HB_TAG('c', 'l', 'i', 'g'), 0, 0, ~0u };
671 features.push_back(no_liga);
672 features.push_back(no_clig);
673 }
674 addFeatures(ctx->paint.fontFeatureSettings, &features);
675
676 double size = ctx->paint.size;
677 double scaleX = ctx->paint.scaleX;
678 double letterSpace = ctx->paint.letterSpacing * size * scaleX;
679 double letterSpaceHalfLeft;
680 if ((ctx->paint.paintFlags & LinearTextFlag) == 0) {
681 letterSpace = round(letterSpace);
682 letterSpaceHalfLeft = floor(letterSpace * 0.5);
683 } else {
684 letterSpaceHalfLeft = letterSpace * 0.5;
685 }
686 double letterSpaceHalfRight = letterSpace - letterSpaceHalfLeft;
687
688 float x = mAdvance;
689 float y = 0;
690 for (size_t run_ix = 0; run_ix < items.size(); run_ix++) {
691 FontCollection::Run &run = items[run_ix];
692 if (run.fakedFont.font == NULL) {
693 ALOGE("no font for run starting u+%04x length %d", buf[run.start], run.end - run.start);
694 continue;
695 }
696 int font_ix = findFace(run.fakedFont, ctx);
697 ctx->paint.font = mFaces[font_ix].font;
698 ctx->paint.fakery = mFaces[font_ix].fakery;
699 hb_font_t* hbFont = ctx->hbFonts[font_ix];
700 #ifdef VERBOSE
701 std::cout << "Run " << run_ix << ", font " << font_ix <<
702 " [" << run.start << ":" << run.end << "]" << std::endl;
703 #endif
704
705 hb_font_set_ppem(hbFont, size * scaleX, size);
706 hb_font_set_scale(hbFont, HBFloatToFixed(size * scaleX), HBFloatToFixed(size));
707
708 // TODO: if there are multiple scripts within a font in an RTL run,
709 // we need to reorder those runs. This is unlikely with our current
710 // font stack, but should be done for correctness.
711 ssize_t srunend;
712 for (ssize_t srunstart = run.start; srunstart < run.end; srunstart = srunend) {
713 srunend = srunstart;
714 hb_script_t script = getScriptRun(buf + start, run.end, &srunend);
715
716 hb_buffer_reset(buffer);
717 hb_buffer_set_script(buffer, script);
718 hb_buffer_set_direction(buffer, isRtl? HB_DIRECTION_RTL : HB_DIRECTION_LTR);
719 FontLanguage language = ctx->style.getLanguage();
720 if (language) {
721 string lang = language.getString();
722 hb_buffer_set_language(buffer, hb_language_from_string(lang.c_str(), -1));
723 }
724 hb_buffer_add_utf16(buffer, buf, bufSize, srunstart + start, srunend - srunstart);
725 hb_shape(hbFont, buffer, features.empty() ? NULL : &features[0], features.size());
726 unsigned int numGlyphs;
727 hb_glyph_info_t* info = hb_buffer_get_glyph_infos(buffer, &numGlyphs);
728 hb_glyph_position_t* positions = hb_buffer_get_glyph_positions(buffer, NULL);
729 if (numGlyphs)
730 {
731 mAdvances[info[0].cluster - start] += letterSpaceHalfLeft;
732 x += letterSpaceHalfLeft;
733 }
734 for (unsigned int i = 0; i < numGlyphs; i++) {
735 #ifdef VERBOSE
736 std::cout << positions[i].x_advance << " " << positions[i].y_advance << " " << positions[i].x_offset << " " << positions[i].y_offset << std::endl; std::cout << "DoLayout " << info[i].codepoint <<
737 ": " << HBFixedToFloat(positions[i].x_advance) << "; " << positions[i].x_offset << ", " << positions[i].y_offset << std::endl;
738 #endif
739 if (i > 0 && info[i - 1].cluster != info[i].cluster) {
740 mAdvances[info[i - 1].cluster - start] += letterSpaceHalfRight;
741 mAdvances[info[i].cluster - start] += letterSpaceHalfLeft;
742 x += letterSpace;
743 }
744
745 hb_codepoint_t glyph_ix = info[i].codepoint;
746 float xoff = HBFixedToFloat(positions[i].x_offset);
747 float yoff = -HBFixedToFloat(positions[i].y_offset);
748 xoff += yoff * ctx->paint.skewX;
749 LayoutGlyph glyph = {font_ix, glyph_ix, x + xoff, y + yoff};
750 mGlyphs.push_back(glyph);
751 float xAdvance = HBFixedToFloat(positions[i].x_advance);
752 if ((ctx->paint.paintFlags & LinearTextFlag) == 0) {
753 xAdvance = roundf(xAdvance);
754 }
755 MinikinRect glyphBounds;
756 ctx->paint.font->GetBounds(&glyphBounds, glyph_ix, ctx->paint);
757 glyphBounds.offset(x + xoff, y + yoff);
758 mBounds.join(glyphBounds);
759 mAdvances[info[i].cluster - start] += xAdvance;
760 x += xAdvance;
761 }
762 if (numGlyphs)
763 {
764 mAdvances[info[numGlyphs - 1].cluster - start] += letterSpaceHalfRight;
765 x += letterSpaceHalfRight;
766 }
767 }
768 }
769 mAdvance = x;
770 }
771
appendLayout(Layout * src,size_t start)772 void Layout::appendLayout(Layout* src, size_t start) {
773 int fontMapStack[16];
774 int* fontMap;
775 if (src->mFaces.size() < sizeof(fontMapStack) / sizeof(fontMapStack[0])) {
776 fontMap = fontMapStack;
777 } else {
778 fontMap = new int[src->mFaces.size()];
779 }
780 for (size_t i = 0; i < src->mFaces.size(); i++) {
781 int font_ix = findFace(src->mFaces[i], NULL);
782 fontMap[i] = font_ix;
783 }
784 int x0 = mAdvance;
785 for (size_t i = 0; i < src->mGlyphs.size(); i++) {
786 LayoutGlyph& srcGlyph = src->mGlyphs[i];
787 int font_ix = fontMap[srcGlyph.font_ix];
788 unsigned int glyph_id = srcGlyph.glyph_id;
789 float x = x0 + srcGlyph.x;
790 float y = srcGlyph.y;
791 LayoutGlyph glyph = {font_ix, glyph_id, x, y};
792 mGlyphs.push_back(glyph);
793 }
794 for (size_t i = 0; i < src->mAdvances.size(); i++) {
795 mAdvances[i + start] = src->mAdvances[i];
796 }
797 MinikinRect srcBounds(src->mBounds);
798 srcBounds.offset(x0, 0);
799 mBounds.join(srcBounds);
800 mAdvance += src->mAdvance;
801
802 if (fontMap != fontMapStack) {
803 delete[] fontMap;
804 }
805 }
806
draw(Bitmap * surface,int x0,int y0,float size) const807 void Layout::draw(Bitmap* surface, int x0, int y0, float size) const {
808 /*
809 TODO: redo as MinikinPaint settings
810 if (mProps.hasTag(minikinHinting)) {
811 int hintflags = mProps.value(minikinHinting).getIntValue();
812 if (hintflags & 1) load_flags |= FT_LOAD_NO_HINTING;
813 if (hintflags & 2) load_flags |= FT_LOAD_NO_AUTOHINT;
814 }
815 */
816 for (size_t i = 0; i < mGlyphs.size(); i++) {
817 const LayoutGlyph& glyph = mGlyphs[i];
818 MinikinFont* mf = mFaces[glyph.font_ix].font;
819 MinikinFontFreeType* face = static_cast<MinikinFontFreeType*>(mf);
820 GlyphBitmap glyphBitmap;
821 MinikinPaint paint;
822 paint.size = size;
823 bool ok = face->Render(glyph.glyph_id, paint, &glyphBitmap);
824 printf("glyphBitmap.width=%d, glyphBitmap.height=%d (%d, %d) x=%f, y=%f, ok=%d\n",
825 glyphBitmap.width, glyphBitmap.height, glyphBitmap.left, glyphBitmap.top, glyph.x, glyph.y, ok);
826 if (ok) {
827 surface->drawGlyph(glyphBitmap,
828 x0 + int(floor(glyph.x + 0.5)), y0 + int(floor(glyph.y + 0.5)));
829 }
830 }
831 }
832
nGlyphs() const833 size_t Layout::nGlyphs() const {
834 return mGlyphs.size();
835 }
836
getFont(int i) const837 MinikinFont* Layout::getFont(int i) const {
838 const LayoutGlyph& glyph = mGlyphs[i];
839 return mFaces[glyph.font_ix].font;
840 }
841
getFakery(int i) const842 FontFakery Layout::getFakery(int i) const {
843 const LayoutGlyph& glyph = mGlyphs[i];
844 return mFaces[glyph.font_ix].fakery;
845 }
846
getGlyphId(int i) const847 unsigned int Layout::getGlyphId(int i) const {
848 const LayoutGlyph& glyph = mGlyphs[i];
849 return glyph.glyph_id;
850 }
851
getX(int i) const852 float Layout::getX(int i) const {
853 const LayoutGlyph& glyph = mGlyphs[i];
854 return glyph.x;
855 }
856
getY(int i) const857 float Layout::getY(int i) const {
858 const LayoutGlyph& glyph = mGlyphs[i];
859 return glyph.y;
860 }
861
getAdvance() const862 float Layout::getAdvance() const {
863 return mAdvance;
864 }
865
getAdvances(float * advances)866 void Layout::getAdvances(float* advances) {
867 memcpy(advances, &mAdvances[0], mAdvances.size() * sizeof(float));
868 }
869
getBounds(MinikinRect * bounds)870 void Layout::getBounds(MinikinRect* bounds) {
871 bounds->set(mBounds);
872 }
873
purgeCaches()874 void Layout::purgeCaches() {
875 AutoMutex _l(gMinikinLock);
876 LayoutCache& layoutCache = LayoutEngine::getInstance().layoutCache;
877 layoutCache.clear();
878 HbFaceCache& hbCache = LayoutEngine::getInstance().hbFaceCache;
879 hbCache.mCache.clear();
880 }
881
882 } // namespace android
883