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. 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. 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 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 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 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 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 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