• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 "TextLayout"
18 
19 #include "TextLayout.h"
20 #include "TextLayoutCache.h"
21 
22 #include <android_runtime/AndroidRuntime.h>
23 
24 #include "SkTemplates.h"
25 #include "unicode/ubidi.h"
26 #include "unicode/ushape.h"
27 #include <utils/Log.h>
28 
29 namespace android {
30 
31 // Returns true if we might need layout.  If bidiFlags force LTR, assume no layout, if
32 // bidiFlags indicate there probably is RTL, assume we do, otherwise scan the text
33 // looking for a character >= the first RTL character in unicode and assume we do if
34 // we find one.
needsLayout(const jchar * text,jint len,jint bidiFlags)35 bool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) {
36     if (bidiFlags == kBidi_Force_LTR) {
37         return false;
38     }
39     if ((bidiFlags == kBidi_RTL) || (bidiFlags == kBidi_Default_RTL) ||
40             bidiFlags == kBidi_Force_RTL) {
41         return true;
42     }
43     for (int i = 0; i < len; ++i) {
44         if (text[i] >= UNICODE_FIRST_RTL_CHAR) {
45             return true;
46         }
47     }
48     return false;
49 }
50 
51 // Draws or gets the path of a paragraph of text on a single line, running bidi and shaping.
52 // This will draw if canvas is not null, otherwise path must be non-null and it will create
53 // a path representing the text that would have been drawn.
handleText(SkPaint * paint,const jchar * text,jsize len,jint bidiFlags,jfloat x,jfloat y,SkPath * path)54 void TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len,
55                             jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
56     sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
57             text, 0, len, len, bidiFlags);
58     if (value == NULL) {
59         return ;
60     }
61     SkScalar x_ = SkFloatToScalar(x);
62     SkScalar y_ = SkFloatToScalar(y);
63     // Beware: this needs Glyph encoding (already done on the Paint constructor)
64     paint->getTextPath(value->getGlyphs(), value->getGlyphsCount() * 2, x_, y_, path);
65 }
66 
getTextRunAdvances(SkPaint * paint,const jchar * chars,jint start,jint count,jint contextCount,jint dirFlags,jfloat * resultAdvances,jfloat * resultTotalAdvance)67 void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start,
68                                     jint count, jint contextCount, jint dirFlags,
69                                     jfloat* resultAdvances, jfloat* resultTotalAdvance) {
70     sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
71             chars, start, count, contextCount, dirFlags);
72     if (value == NULL) {
73         return ;
74     }
75     if (resultAdvances) {
76         memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat));
77     }
78     if (resultTotalAdvance) {
79         *resultTotalAdvance = value->getTotalAdvance();
80     }
81 }
82 
getTextRunAdvancesICU(SkPaint * paint,const jchar * chars,jint start,jint count,jint contextCount,jint dirFlags,jfloat * resultAdvances,jfloat & resultTotalAdvance)83 void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start,
84                                     jint count, jint contextCount, jint dirFlags,
85                                     jfloat* resultAdvances, jfloat& resultTotalAdvance) {
86     // Compute advances and return them
87     computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags,
88             resultAdvances, &resultTotalAdvance);
89 }
90 
getTextPath(SkPaint * paint,const jchar * text,jsize len,jint bidiFlags,jfloat x,jfloat y,SkPath * path)91 void TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len,
92                              jint bidiFlags, jfloat x, jfloat y, SkPath *path) {
93     handleText(paint, text, len, bidiFlags, x, y, path);
94 }
95 
96 
drawTextOnPath(SkPaint * paint,const jchar * text,int count,int bidiFlags,jfloat hOffset,jfloat vOffset,SkPath * path,SkCanvas * canvas)97 void TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count,
98                                 int bidiFlags, jfloat hOffset, jfloat vOffset,
99                                 SkPath* path, SkCanvas* canvas) {
100 
101     SkScalar h_ = SkFloatToScalar(hOffset);
102     SkScalar v_ = SkFloatToScalar(vOffset);
103 
104     sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
105             text, 0, count, count, bidiFlags);
106     if (value == NULL) {
107         return;
108     }
109 
110     // Beware: this needs Glyph encoding (already done on the Paint constructor)
111     canvas->drawTextOnPathHV(value->getGlyphs(), value->getGlyphsCount() * 2, *path, h_, v_, *paint);
112 }
113 
computeAdvancesWithICU(SkPaint * paint,const UChar * chars,size_t start,size_t count,size_t contextCount,int dirFlags,jfloat * outAdvances,jfloat * outTotalAdvance)114 void TextLayout::computeAdvancesWithICU(SkPaint* paint, const UChar* chars,
115         size_t start, size_t count, size_t contextCount, int dirFlags,
116         jfloat* outAdvances, jfloat* outTotalAdvance) {
117     SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount);
118     jchar* buffer = tempBuffer.get();
119     SkScalar* scalarArray = (SkScalar*)outAdvances;
120 
121     // this is where we'd call harfbuzz
122     // for now we just use ushape.c
123     size_t widths;
124     const jchar* text;
125     if (dirFlags & 0x1) { // rtl, call arabic shaping in case
126         UErrorCode status = U_ZERO_ERROR;
127         // Use fixed length since we need to keep start and count valid
128         u_shapeArabic(chars, contextCount, buffer, contextCount,
129                 U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
130                 U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
131                 U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
132         // we shouldn't fail unless there's an out of memory condition,
133         // in which case we're hosed anyway
134         for (int i = start, e = i + count; i < e; ++i) {
135             if (buffer[i] == UNICODE_NOT_A_CHAR) {
136                 buffer[i] = UNICODE_ZWSP; // zero-width-space for skia
137             }
138         }
139         text = buffer + start;
140         widths = paint->getTextWidths(text, count << 1, scalarArray);
141     } else {
142         text = chars + start;
143         widths = paint->getTextWidths(text, count << 1, scalarArray);
144     }
145 
146     jfloat totalAdvance = 0;
147     if (widths < count) {
148 #if DEBUG_ADVANCES
149     ALOGD("ICU -- count=%d", widths);
150 #endif
151         // Skia operates on code points, not code units, so surrogate pairs return only
152         // one value. Expand the result so we have one value per UTF-16 code unit.
153 
154         // Note, skia's getTextWidth gets confused if it encounters a surrogate pair,
155         // leaving the remaining widths zero.  Not nice.
156         for (size_t i = 0, p = 0; i < widths; ++i) {
157             totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]);
158             if (p < count &&
159                     text[p] >= UNICODE_FIRST_LOW_SURROGATE &&
160                     text[p] < UNICODE_FIRST_PRIVATE_USE &&
161                     text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE &&
162                     text[p-1] < UNICODE_FIRST_LOW_SURROGATE) {
163                 outAdvances[p++] = 0;
164             }
165 #if DEBUG_ADVANCES
166             ALOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
167 #endif
168         }
169     } else {
170 #if DEBUG_ADVANCES
171     ALOGD("ICU -- count=%d", count);
172 #endif
173         for (size_t i = 0; i < count; i++) {
174             totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]);
175 #if DEBUG_ADVANCES
176             ALOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance);
177 #endif
178         }
179     }
180     *outTotalAdvance = totalAdvance;
181 }
182 
183 }
184