1f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt/* 2f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * Copyright (C) 2010 The Android Open Source Project 3f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * 4f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * Licensed under the Apache License, Version 2.0 (the "License"); 5f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * you may not use this file except in compliance with the License. 6f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * You may obtain a copy of the License at 7f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * 8f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * http://www.apache.org/licenses/LICENSE-2.0 9f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * 10f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * Unless required by applicable law or agreed to in writing, software 11f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * distributed under the License is distributed on an "AS IS" BASIS, 12f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * See the License for the specific language governing permissions and 14f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt * limitations under the License. 15f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt */ 16f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 17b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio#define LOG_TAG "TextLayout" 18b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio 19f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt#include "TextLayout.h" 20d313c665e618af3194f504064bcd284fe5368682Fabrice Di Meglio#include "TextLayoutCache.h" 21f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 22f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt#include <android_runtime/AndroidRuntime.h> 23f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 24f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt#include "SkTemplates.h" 25f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt#include "unicode/ubidi.h" 26f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt#include "unicode/ushape.h" 27f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt#include <utils/Log.h> 28f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 29f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Feltnamespace android { 30d313c665e618af3194f504064bcd284fe5368682Fabrice Di Meglio 31f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt// Returns true if we might need layout. If bidiFlags force LTR, assume no layout, if 32f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt// bidiFlags indicate there probably is RTL, assume we do, otherwise scan the text 33f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt// looking for a character >= the first RTL character in unicode and assume we do if 34f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt// we find one. 35f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Feltbool TextLayout::needsLayout(const jchar* text, jint len, jint bidiFlags) { 36f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt if (bidiFlags == kBidi_Force_LTR) { 37f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt return false; 38f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt } 39f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt if ((bidiFlags == kBidi_RTL) || (bidiFlags == kBidi_Default_RTL) || 40f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt bidiFlags == kBidi_Force_RTL) { 41f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt return true; 42f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt } 43f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt for (int i = 0; i < len; ++i) { 44dd347df9f85e04d8b08a5249b8db731300699cc4Fabrice Di Meglio if (text[i] >= UNICODE_FIRST_RTL_CHAR) { 45f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt return true; 46f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt } 47f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt } 48f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt return false; 49f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt} 50f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 51e8e62a4a032a80409114a37908b5f18ab0080848Romain Guy// Draws or gets the path of a paragraph of text on a single line, running bidi and shaping. 52e8e62a4a032a80409114a37908b5f18ab0080848Romain Guy// This will draw if canvas is not null, otherwise path must be non-null and it will create 53e8e62a4a032a80409114a37908b5f18ab0080848Romain Guy// a path representing the text that would have been drawn. 54e8e62a4a032a80409114a37908b5f18ab0080848Romain Guyvoid TextLayout::handleText(SkPaint *paint, const jchar* text, jsize len, 55c511bee87cda99a252d1a62487f47c8f05aee78cFabrice Di Meglio jint bidiFlags, jfloat x, jfloat y, SkPath *path) { 56a731b082b2c43204e6e9f927ab82fb732934a83bFabrice Di Meglio sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, 57a731b082b2c43204e6e9f927ab82fb732934a83bFabrice Di Meglio text, 0, len, len, bidiFlags); 58b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio if (value == NULL) { 59b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio return ; 60e8e62a4a032a80409114a37908b5f18ab0080848Romain Guy } 61b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio SkScalar x_ = SkFloatToScalar(x); 62b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio SkScalar y_ = SkFloatToScalar(y); 63c511bee87cda99a252d1a62487f47c8f05aee78cFabrice Di Meglio // Beware: this needs Glyph encoding (already done on the Paint constructor) 64c511bee87cda99a252d1a62487f47c8f05aee78cFabrice Di Meglio paint->getTextPath(value->getGlyphs(), value->getGlyphsCount() * 2, x_, y_, path); 6561c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy} 6661c8c9c5b2006d18e9310b6521c65b36ffe75ce4Romain Guy 67d313c665e618af3194f504064bcd284fe5368682Fabrice Di Megliovoid TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint start, 68f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt jint count, jint contextCount, jint dirFlags, 6979df5323e7ed541b854cea5684a89e8be8c2dfc9Fabrice Di Meglio jfloat* resultAdvances, jfloat* resultTotalAdvance) { 70a731b082b2c43204e6e9f927ab82fb732934a83bFabrice Di Meglio sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, 71a731b082b2c43204e6e9f927ab82fb732934a83bFabrice Di Meglio chars, start, count, contextCount, dirFlags); 72b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio if (value == NULL) { 73b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio return ; 74b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio } 75b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio if (resultAdvances) { 76b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio memcpy(resultAdvances, value->getAdvances(), value->getAdvancesCount() * sizeof(jfloat)); 77b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio } 78b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio if (resultTotalAdvance) { 79b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio *resultTotalAdvance = value->getTotalAdvance(); 80fcf2be1846935e7983ea2fe87fdd4d7af27764b6Fabrice Di Meglio } 81f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt} 82f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 83eee49c699c035ffba188417489f40d34f587d65cFabrice Di Megliovoid TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start, 84eee49c699c035ffba188417489f40d34f587d65cFabrice Di Meglio jint count, jint contextCount, jint dirFlags, 85eee49c699c035ffba188417489f40d34f587d65cFabrice Di Meglio jfloat* resultAdvances, jfloat& resultTotalAdvance) { 86eee49c699c035ffba188417489f40d34f587d65cFabrice Di Meglio // Compute advances and return them 8754dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags, 88eee49c699c035ffba188417489f40d34f587d65cFabrice Di Meglio resultAdvances, &resultTotalAdvance); 89eee49c699c035ffba188417489f40d34f587d65cFabrice Di Meglio} 90f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 91f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Feltvoid TextLayout::getTextPath(SkPaint *paint, const jchar *text, jsize len, 92f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt jint bidiFlags, jfloat x, jfloat y, SkPath *path) { 93c511bee87cda99a252d1a62487f47c8f05aee78cFabrice Di Meglio handleText(paint, text, len, bidiFlags, x, y, path); 94f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt} 95f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 96f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 97f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Feltvoid TextLayout::drawTextOnPath(SkPaint* paint, const jchar* text, int count, 98f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt int bidiFlags, jfloat hOffset, jfloat vOffset, 99f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt SkPath* path, SkCanvas* canvas) { 100f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 101f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt SkScalar h_ = SkFloatToScalar(hOffset); 102f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt SkScalar v_ = SkFloatToScalar(vOffset); 103f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 104a731b082b2c43204e6e9f927ab82fb732934a83bFabrice Di Meglio sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint, 105a731b082b2c43204e6e9f927ab82fb732934a83bFabrice Di Meglio text, 0, count, count, bidiFlags); 106b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio if (value == NULL) { 107a731b082b2c43204e6e9f927ab82fb732934a83bFabrice Di Meglio return; 108f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt } 109a731b082b2c43204e6e9f927ab82fb732934a83bFabrice Di Meglio 110c511bee87cda99a252d1a62487f47c8f05aee78cFabrice Di Meglio // Beware: this needs Glyph encoding (already done on the Paint constructor) 111b02d0ca5553300063e4332192632312600caf4b9Fabrice Di Meglio canvas->drawTextOnPathHV(value->getGlyphs(), value->getGlyphsCount() * 2, *path, h_, v_, *paint); 112f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt} 113f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt 11454dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Megliovoid TextLayout::computeAdvancesWithICU(SkPaint* paint, const UChar* chars, 11554dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio size_t start, size_t count, size_t contextCount, int dirFlags, 11654dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio jfloat* outAdvances, jfloat* outTotalAdvance) { 11754dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio SkAutoSTMalloc<CHAR_BUFFER_SIZE, jchar> tempBuffer(contextCount); 11854dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio jchar* buffer = tempBuffer.get(); 11954dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio SkScalar* scalarArray = (SkScalar*)outAdvances; 12054dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio 12154dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio // this is where we'd call harfbuzz 12254dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio // for now we just use ushape.c 12354dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio size_t widths; 12454dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio const jchar* text; 12554dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio if (dirFlags & 0x1) { // rtl, call arabic shaping in case 12654dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio UErrorCode status = U_ZERO_ERROR; 12754dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio // Use fixed length since we need to keep start and count valid 12854dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio u_shapeArabic(chars, contextCount, buffer, contextCount, 12954dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio U_SHAPE_LENGTH_FIXED_SPACES_NEAR | 13054dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE | 13154dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status); 13254dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio // we shouldn't fail unless there's an out of memory condition, 13354dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio // in which case we're hosed anyway 13454dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio for (int i = start, e = i + count; i < e; ++i) { 13554dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio if (buffer[i] == UNICODE_NOT_A_CHAR) { 13654dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio buffer[i] = UNICODE_ZWSP; // zero-width-space for skia 13754dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio } 13854dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio } 13954dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio text = buffer + start; 14054dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio widths = paint->getTextWidths(text, count << 1, scalarArray); 14154dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio } else { 14254dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio text = chars + start; 14354dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio widths = paint->getTextWidths(text, count << 1, scalarArray); 14454dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio } 14554dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio 14654dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio jfloat totalAdvance = 0; 14754dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio if (widths < count) { 14854dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio#if DEBUG_ADVANCES 1495baa3a62a97544669fba6d65a11c07f252e654ddSteve Block ALOGD("ICU -- count=%d", widths); 15054dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio#endif 15154dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio // Skia operates on code points, not code units, so surrogate pairs return only 15254dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio // one value. Expand the result so we have one value per UTF-16 code unit. 15354dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio 15454dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio // Note, skia's getTextWidth gets confused if it encounters a surrogate pair, 15554dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio // leaving the remaining widths zero. Not nice. 15654dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio for (size_t i = 0, p = 0; i < widths; ++i) { 15754dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio totalAdvance += outAdvances[p++] = SkScalarToFloat(scalarArray[i]); 15854dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio if (p < count && 15954dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio text[p] >= UNICODE_FIRST_LOW_SURROGATE && 16054dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio text[p] < UNICODE_FIRST_PRIVATE_USE && 16154dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio text[p-1] >= UNICODE_FIRST_HIGH_SURROGATE && 16254dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio text[p-1] < UNICODE_FIRST_LOW_SURROGATE) { 16354dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio outAdvances[p++] = 0; 16454dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio } 16554dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio#if DEBUG_ADVANCES 1665baa3a62a97544669fba6d65a11c07f252e654ddSteve Block ALOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); 16754dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio#endif 16854dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio } 16954dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio } else { 17054dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio#if DEBUG_ADVANCES 1715baa3a62a97544669fba6d65a11c07f252e654ddSteve Block ALOGD("ICU -- count=%d", count); 17254dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio#endif 17354dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio for (size_t i = 0; i < count; i++) { 17454dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio totalAdvance += outAdvances[i] = SkScalarToFloat(scalarArray[i]); 17554dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio#if DEBUG_ADVANCES 1765baa3a62a97544669fba6d65a11c07f252e654ddSteve Block ALOGD("icu-adv = %f - total = %f", outAdvances[i], totalAdvance); 17754dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio#endif 17854dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio } 17954dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio } 18054dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio *outTotalAdvance = totalAdvance; 18154dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio} 18254dc642cc1e188b9eeecadb648b9e8c610f4b857Fabrice Di Meglio 183f7cb1f75fdaedf996cab7c4690b080adc7bc5b97Doug Felt} 184