diff options
author | Fabrice Di Meglio <fdimeglio@google.com> | 2011-03-30 12:01:52 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-03-30 12:01:52 -0700 |
commit | 4111bec3b68a200932817f4b8924fea08f4d5905 (patch) | |
tree | 6d2a84f464791e15fe9da959bc4bc8860f0a1c04 | |
parent | 525194034156707bcb1134f5dc54283fe0ff470d (diff) | |
parent | eee49c699c035ffba188417489f40d34f587d65c (diff) | |
download | frameworks_base-4111bec3b68a200932817f4b8924fea08f4d5905.zip frameworks_base-4111bec3b68a200932817f4b8924fea08f4d5905.tar.gz frameworks_base-4111bec3b68a200932817f4b8924fea08f4d5905.tar.bz2 |
Merge "Fix text redering issue where the text was sometimes truncated"
-rw-r--r-- | core/java/android/text/GraphicsOperations.java | 7 | ||||
-rw-r--r-- | core/java/android/text/SpannableStringBuilder.java | 29 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 10 | ||||
-rw-r--r-- | core/jni/android/graphics/HarfbuzzSkia.cpp | 55 | ||||
-rw-r--r-- | core/jni/android/graphics/HarfbuzzSkia.h | 34 | ||||
-rw-r--r-- | core/jni/android/graphics/Paint.cpp | 45 | ||||
-rw-r--r-- | core/jni/android/graphics/RtlProperties.h | 5 | ||||
-rw-r--r-- | core/jni/android/graphics/TextLayout.cpp | 15 | ||||
-rw-r--r-- | core/jni/android/graphics/TextLayout.h | 8 | ||||
-rw-r--r-- | core/jni/android/graphics/TextLayoutCache.h | 56 | ||||
-rw-r--r-- | graphics/java/android/graphics/Paint.java | 122 | ||||
-rw-r--r-- | tests/BiDiTests/res/layout/biditest_main.xml | 5 | ||||
-rw-r--r-- | tests/BiDiTests/res/values/strings.xml | 4 | ||||
-rw-r--r-- | tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java | 28 | ||||
-rw-r--r-- | tests/BiDiTests/src/com/android/bidi/BiDiTestConstants.java | 22 | ||||
-rw-r--r-- | tests/BiDiTests/src/com/android/bidi/BiDiTestView.java | 94 |
16 files changed, 442 insertions, 97 deletions
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java index d426d12..6e2168b 100644 --- a/core/java/android/text/GraphicsOperations.java +++ b/core/java/android/text/GraphicsOperations.java @@ -58,6 +58,13 @@ extends CharSequence int flags, float[] advances, int advancesIndex, Paint paint); /** + * Just like {@link Paint#getTextRunAdvances}. + * @hide + */ + float getTextRunAdvancesICU(int start, int end, int contextStart, int contextEnd, + int flags, float[] advances, int advancesIndex, Paint paint); + + /** * Just like {@link Paint#getTextRunCursor}. * @hide */ diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java index ea5cdfe..ff6a4cd 100644 --- a/core/java/android/text/SpannableStringBuilder.java +++ b/core/java/android/text/SpannableStringBuilder.java @@ -1170,6 +1170,35 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable, } /** + * Don't call this yourself -- exists for Paint to use internally. + * {@hide} + */ + public float getTextRunAdvancesICU(int start, int end, int contextStart, int contextEnd, int flags, + float[] advances, int advancesPos, Paint p) { + + float ret; + + int contextLen = contextEnd - contextStart; + int len = end - start; + + if (end <= mGapStart) { + ret = p.getTextRunAdvancesICU(mText, start, len, contextStart, contextLen, + flags, advances, advancesPos); + } else if (start >= mGapStart) { + ret = p.getTextRunAdvancesICU(mText, start + mGapLength, len, + contextStart + mGapLength, contextLen, flags, advances, advancesPos); + } else { + char[] buf = TextUtils.obtain(contextLen); + getChars(contextStart, contextEnd, buf, 0); + ret = p.getTextRunAdvancesICU(buf, start - contextStart, len, + 0, contextLen, flags, advances, advancesPos); + TextUtils.recycle(buf); + } + + return ret; + } + + /** * Returns the next cursor position in the run. This avoids placing the cursor between * surrogates, between characters that form conjuncts, between base characters and combining * marks, or within a reordering cluster. diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index baf20a1..9e482b4 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -2951,6 +2951,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener advancesIndex); } + public float getTextRunAdvancesICU(int start, int end, int contextStart, + int contextEnd, int flags, float[] advances, int advancesIndex, + Paint p) { + int count = end - start; + int contextCount = contextEnd - contextStart; + return p.getTextRunAdvancesICU(mChars, start + mStart, count, + contextStart + mStart, contextCount, flags, advances, + advancesIndex); + } + public int getTextRunCursor(int contextStart, int contextEnd, int flags, int offset, int cursorOpt, Paint p) { int contextCount = contextEnd - contextStart; diff --git a/core/jni/android/graphics/HarfbuzzSkia.cpp b/core/jni/android/graphics/HarfbuzzSkia.cpp index 58fb32b..92c743f 100644 --- a/core/jni/android/graphics/HarfbuzzSkia.cpp +++ b/core/jni/android/graphics/HarfbuzzSkia.cpp @@ -34,6 +34,8 @@ #include "SkRect.h" #include "SkTypeface.h" +#include <utils/Log.h> + extern "C" { #include "harfbuzz-shaper.h" } @@ -43,20 +45,13 @@ extern "C" { namespace android { -static HB_Fixed SkiaScalarToHarfbuzzFixed(SkScalar value) -{ - // HB_Fixed is a 26.6 fixed point format. - return value * 64; -} - static void setupPaintWithFontData(SkPaint* paint, FontData* data) { - paint->setAntiAlias(true); - paint->setSubpixelText(true); - paint->setHinting(SkPaint::kSlight_Hinting); - paint->setTextSize(SkFloatToScalar(data->textSize)); paint->setTypeface(data->typeFace); - paint->setFakeBoldText(data->fakeBold); - paint->setTextSkewX(data->fakeItalic ? -SK_Scalar1/4 : 0); + paint->setTextSize(data->textSize); + paint->setTextSkewX(data->textSkewX); + paint->setTextScaleX(data->textScaleX); + paint->setFlags(data->flags); + paint->setHinting(data->hinting); } static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length, @@ -67,16 +62,13 @@ static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_u setupPaintWithFontData(&paint, data); paint.setTextEncoding(SkPaint::kUTF16_TextEncoding); - int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), - reinterpret_cast<uint16_t*>(glyphs)); + uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs); + int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs); // HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our // |glyphs| array needs to be converted. for (int i = numGlyphs - 1; i >= 0; --i) { - uint16_t value; - // We use a memcpy to avoid breaking strict aliasing rules. - memcpy(&value, reinterpret_cast<char*>(glyphs) + sizeof(uint16_t) * i, sizeof(value)); - glyphs[i] = value; + glyphs[i] = skiaGlyphs[i]; } *glyphsSize = numGlyphs; @@ -97,16 +89,17 @@ static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 n return; for (unsigned i = 0; i < numGlyphs; ++i) glyphs16[i] = glyphs[i]; - paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), reinterpret_cast<SkScalar*>(advances)); + SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances); + paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances); // The |advances| values which Skia outputs are SkScalars, which are floats // in Chromium. However, Harfbuzz wants them in 26.6 fixed point format. // These two formats are both 32-bits long. for (unsigned i = 0; i < numGlyphs; ++i) { - float value; - // We use a memcpy to avoid breaking strict aliasing rules. - memcpy(&value, reinterpret_cast<char*>(advances) + sizeof(float) * i, sizeof(value)); - advances[i] = SkiaScalarToHarfbuzzFixed(value); + advances[i] = SkScalarToHBFixed(scalarAdvances[i]); +#if DEBUG_ADVANCES + LOGD("glyphsToAdvances -- advances[%d]=%d", i, advances[i]); +#endif } delete glyphs16; } @@ -156,8 +149,8 @@ static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_ui return HB_Err_Invalid_SubTable; // Skia does let us get a single point from the path. path.getPoints(points, point + 1); - *xPos = SkiaScalarToHarfbuzzFixed(points[point].fX); - *yPos = SkiaScalarToHarfbuzzFixed(points[point].fY); + *xPos = SkScalarToHBFixed(points[point].fX); + *yPos = SkScalarToHBFixed(points[point].fY); *resultingNumPoints = numPoints; delete points; @@ -176,12 +169,12 @@ static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* met SkRect bounds; paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds); - metrics->x = SkiaScalarToHarfbuzzFixed(bounds.fLeft); - metrics->y = SkiaScalarToHarfbuzzFixed(bounds.fTop); - metrics->width = SkiaScalarToHarfbuzzFixed(bounds.width()); - metrics->height = SkiaScalarToHarfbuzzFixed(bounds.height()); + metrics->x = SkScalarToHBFixed(bounds.fLeft); + metrics->y = SkScalarToHBFixed(bounds.fTop); + metrics->width = SkScalarToHBFixed(bounds.width()); + metrics->height = SkScalarToHBFixed(bounds.height()); - metrics->xOffset = SkiaScalarToHarfbuzzFixed(width); + metrics->xOffset = SkScalarToHBFixed(width); // We can't actually get the |y| correct because Skia doesn't export // the vertical advance. However, nor we do ever render vertical text at // the moment so it's unimportant. @@ -199,7 +192,7 @@ static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric) switch (metric) { case HB_FontAscent: - return SkiaScalarToHarfbuzzFixed(-skiaMetrics.fAscent); + return SkScalarToHBFixed(-skiaMetrics.fAscent); // We don't support getting the rest of the metrics and Harfbuzz doesn't seem to need them. default: return 0; diff --git a/core/jni/android/graphics/HarfbuzzSkia.h b/core/jni/android/graphics/HarfbuzzSkia.h index d057d76..99b389a 100644 --- a/core/jni/android/graphics/HarfbuzzSkia.h +++ b/core/jni/android/graphics/HarfbuzzSkia.h @@ -27,22 +27,38 @@ #ifndef HarfbuzzSkia_h #define HarfbuzzSkia_h +#include "SkScalar.h" #include "SkTypeface.h" +#include "SkPaint.h" extern "C" { #include "harfbuzz-shaper.h" } namespace android { - typedef struct { - SkTypeface* typeFace; - float textSize; - bool fakeBold; - bool fakeItalic; - } FontData; - - HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len); - extern const HB_FontClass harfbuzzSkiaClass; + +static inline float HBFixedToFloat(HB_Fixed v) { + // Harfbuzz uses 26.6 fixed point values for pixel offsets + return v * (1.0f / 64); +} + +static inline HB_Fixed SkScalarToHBFixed(SkScalar value) { + // HB_Fixed is a 26.6 fixed point format. + return SkScalarToFloat(value) * 64.0f; +} + +typedef struct { + SkTypeface* typeFace; + SkScalar textSize; + SkScalar textSkewX; + SkScalar textScaleX; + uint32_t flags; + SkPaint::Hinting hinting; +} FontData; + +HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len); +extern const HB_FontClass harfbuzzSkiaClass; + } // namespace android #endif diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 5c3497f..27be871 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -414,6 +414,7 @@ public: for (int i = 0; i < glyphCount; i++) { glyphsArray[i] = (jchar) shaperItem.glyphs[i]; } + env->ReleaseCharArrayElements(glyphs, glyphsArray, JNI_ABORT); return glyphCount; } @@ -442,6 +443,21 @@ public: return totalAdvance; } + static jfloat doTextRunAdvancesICU(JNIEnv *env, SkPaint *paint, const jchar *text, + jint start, jint count, jint contextCount, jint flags, + jfloatArray advances, jint advancesIndex) { + jfloat advancesArray[count]; + jfloat totalAdvance; + + TextLayout::getTextRunAdvancesICU(paint, text, start, count, contextCount, flags, + advancesArray, totalAdvance); + + if (advances != NULL) { + env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray); + } + return totalAdvance; + } + static float getTextRunAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, jint flags, jfloatArray advances, jint advancesIndex) { @@ -463,6 +479,27 @@ public: return result; } + static float getTextRunAdvancesICU___CIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, + jcharArray text, jint index, jint count, jint contextIndex, jint contextCount, + jint flags, jfloatArray advances, jint advancesIndex) { + jchar* textArray = env->GetCharArrayElements(text, NULL); + jfloat result = doTextRunAdvancesICU(env, paint, textArray + contextIndex, + index - contextIndex, count, contextCount, flags, advances, advancesIndex); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); + return result; + } + + static float getTextRunAdvancesICU__StringIIIII_FI(JNIEnv* env, jobject clazz, SkPaint* paint, + jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint flags, + jfloatArray advances, jint advancesIndex) { + const jchar* textArray = env->GetStringChars(text, NULL); + jfloat result = doTextRunAdvancesICU(env, paint, textArray + contextStart, + start - contextStart, end - start, contextEnd - contextStart, flags, advances, + advancesIndex); + env->ReleaseStringChars(text, textArray); + return result; + } + static jint doTextRunCursor(JNIEnv *env, SkPaint* paint, const jchar *text, jint start, jint count, jint flags, jint offset, jint opt) { SkScalar scalarArray[count]; @@ -748,10 +785,14 @@ static JNINativeMethod methods[] = { {"native_breakText","(Ljava/lang/String;ZF[F)I", (void*) SkPaintGlue::breakTextS}, {"native_getTextWidths","(I[CII[F)I", (void*) SkPaintGlue::getTextWidths___CII_F}, {"native_getTextWidths","(ILjava/lang/String;II[F)I", (void*) SkPaintGlue::getTextWidths__StringII_F}, - {"native_getTextRunAdvances","(I[CIIIII[FI)F", (void*) - SkPaintGlue::getTextRunAdvances___CIIIII_FI}, + {"native_getTextRunAdvances","(I[CIIIII[FI)F", + (void*) SkPaintGlue::getTextRunAdvances___CIIIII_FI}, {"native_getTextRunAdvances","(ILjava/lang/String;IIIII[FI)F", (void*) SkPaintGlue::getTextRunAdvances__StringIIIII_FI}, + {"native_getTextRunAdvancesICU","(I[CIIIII[FI)F", + (void*) SkPaintGlue::getTextRunAdvancesICU___CIIIII_FI}, + {"native_getTextRunAdvancesICU","(ILjava/lang/String;IIIII[FI)F", + (void*) SkPaintGlue::getTextRunAdvancesICU__StringIIIII_FI}, {"native_getTextGlyphs","(ILjava/lang/String;IIIII[C)I", (void*) SkPaintGlue::getTextGlyphs__StringIIIII_C}, {"native_getTextRunCursor", "(I[CIIIII)I", (void*) SkPaintGlue::getTextRunCursor___C}, diff --git a/core/jni/android/graphics/RtlProperties.h b/core/jni/android/graphics/RtlProperties.h index 2c68fa3..f41f4a1 100644 --- a/core/jni/android/graphics/RtlProperties.h +++ b/core/jni/android/graphics/RtlProperties.h @@ -45,7 +45,12 @@ static RtlDebugLevel readRtlDebugLevel() { return kRtlDebugDisabled; } +// Define if we want to use Harfbuzz (1) or not (0) #define RTL_USE_HARFBUZZ 1 +// Define if we want (1) to have Advances debug values or not (0) +#define DEBUG_ADVANCES 0 + + } // namespace android #endif // ANDROID_RTL_PROPERTIES_H diff --git a/core/jni/android/graphics/TextLayout.cpp b/core/jni/android/graphics/TextLayout.cpp index f1bb696..434f63b 100644 --- a/core/jni/android/graphics/TextLayout.cpp +++ b/core/jni/android/graphics/TextLayout.cpp @@ -269,6 +269,21 @@ void TextLayout::getTextRunAdvances(SkPaint* paint, const jchar* chars, jint sta #endif } +void TextLayout::getTextRunAdvancesHB(SkPaint* paint, const jchar* chars, jint start, + jint count, jint contextCount, jint dirFlags, + jfloat* resultAdvances, jfloat& resultTotalAdvance) { + // Compute advances and return them + RunAdvanceDescription::computeAdvancesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, + resultAdvances, &resultTotalAdvance); +} + +void TextLayout::getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start, + jint count, jint contextCount, jint dirFlags, + jfloat* resultAdvances, jfloat& resultTotalAdvance) { + // Compute advances and return them + RunAdvanceDescription::computeAdvancesWithICU(paint, chars, start, count, contextCount, dirFlags, + resultAdvances, &resultTotalAdvance); +} // Draws a paragraph of text on a single line, running bidi and shaping void TextLayout::drawText(SkPaint* paint, const jchar* text, jsize len, diff --git a/core/jni/android/graphics/TextLayout.h b/core/jni/android/graphics/TextLayout.h index a950d13..138983c 100644 --- a/core/jni/android/graphics/TextLayout.h +++ b/core/jni/android/graphics/TextLayout.h @@ -73,6 +73,14 @@ public: jint count, jint contextCount, jint dirFlags, jfloat* resultAdvances, jfloat& resultTotalAdvance); + static void getTextRunAdvancesICU(SkPaint* paint, const jchar* chars, jint start, + jint count, jint contextCount, jint dirFlags, + jfloat* resultAdvances, jfloat& resultTotalAdvance); + + static void getTextRunAdvancesHB(SkPaint* paint, const jchar* chars, jint start, + jint count, jint contextCount, jint dirFlags, + jfloat* resultAdvances, jfloat& resultTotalAdvance); + static void drawText(SkPaint* paint, const jchar* text, jsize len, jint bidiFlags, jfloat x, jfloat y, SkCanvas* canvas); diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h index 925bb7c..31d802c 100644 --- a/core/jni/android/graphics/TextLayoutCache.h +++ b/core/jni/android/graphics/TextLayoutCache.h @@ -19,17 +19,20 @@ #include "RtlProperties.h" -#include "stddef.h" +#include <stddef.h> #include <utils/threads.h> #include <utils/String16.h> -#include "utils/GenerationCache.h" -#include "utils/Compare.h" +#include <utils/GenerationCache.h> +#include <utils/Compare.h> -#include "SkPaint.h" -#include "SkTemplates.h" +#include <SkPaint.h> +#include <SkTemplates.h> +#include <SkUtils.h> +#include <SkScalerContext.h> +#include <SkAutoKern.h> -#include "unicode/ubidi.h" -#include "unicode/ushape.h" +#include <unicode/ubidi.h> +#include <unicode/ushape.h> #include "HarfbuzzSkia.h" #include "harfbuzz-shaper.h" @@ -54,14 +57,8 @@ // Define the interval in number of cache hits between two statistics dump #define DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL 100 -// Define if we want to have Advances debug values -#define DEBUG_ADVANCES 0 - namespace android { -// Harfbuzz uses 26.6 fixed point values for pixel offsets -#define HB_FIXED_TO_FLOAT(v) (((float) v) * (1.0 / 64)) - /** * TextLayoutCacheKey is the Cache key */ @@ -202,6 +199,8 @@ public: shaperItem->font = font; shaperItem->face = HB_NewFace(shaperItem->font, harfbuzzSkiaGetTable); + shaperItem->kerning_applied = false; + // We cannot know, ahead of time, how many glyphs a given script run // will produce. We take a guess that script runs will not produce more // than twice as many glyphs as there are code points plus a bit of @@ -222,10 +221,12 @@ public: shaperItem->string = chars; shaperItem->stringLength = contextCount; - fontData->textSize = paint->getTextSize(); - fontData->fakeBold = paint->isFakeBoldText(); - fontData->fakeItalic = (paint->getTextSkewX() > 0); fontData->typeFace = paint->getTypeface(); + fontData->textSize = paint->getTextSize(); + fontData->textSkewX = paint->getTextSkewX(); + fontData->textScaleX = paint->getTextScaleX(); + fontData->flags = paint->getFlags(); + fontData->hinting = paint->getHinting(); shaperItem->font->userData = fontData; } @@ -248,6 +249,21 @@ public: } } +#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) + + static int adjust(int prev, int next) { + int delta = next - prev; + if (delta >= 32) { + return -1; + } + else if (delta < -32) { + return +1; + } + else { + return 0; + } + } + static void computeAdvancesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start, size_t count, size_t contextCount, int dirFlags, jfloat* outAdvances, jfloat* outTotalAdvance) { @@ -261,13 +277,15 @@ public: contextCount, dirFlags); #if DEBUG_ADVANCES - LOGD("HARFBUZZ -- num_glypth=%d", shaperItem.num_glyphs); + LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs, shaperItem.kerning_applied); + LOGD(" -- string= '%s'", String8(chars, contextCount).string()); + LOGD(" -- isDevKernText=%d", paint->isDevKernText()); #endif jfloat totalAdvance = 0; + for (size_t i = 0; i < count; i++) { - // Be careful: we need to use ceilf() for doing the same way as what Skia is doing - totalAdvance += outAdvances[i] = ceilf(HB_FIXED_TO_FLOAT(shaperItem.advances[i])); + totalAdvance += outAdvances[i] = HBFixedToFloat(shaperItem.advances[i]); #if DEBUG_ADVANCES LOGD("hb-adv = %d - rebased = %f - total = %f", shaperItem.advances[i], outAdvances[i], diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 96eb936..0949beb 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -1534,6 +1534,48 @@ public class Paint { } /** + * Convenience overload that takes a char array instead of a + * String. + * + * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int) + * @hide + */ + public float getTextRunAdvancesICU(char[] chars, int index, int count, + int contextIndex, int contextCount, int flags, float[] advances, + int advancesIndex) { + + if ((index | count | contextIndex | contextCount | advancesIndex + | (index - contextIndex) + | ((contextIndex + contextCount) - (index + count)) + | (chars.length - (contextIndex + contextCount)) + | (advances == null ? 0 : + (advances.length - (advancesIndex + count)))) < 0) { + throw new IndexOutOfBoundsException(); + } + if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) { + throw new IllegalArgumentException("unknown flags value: " + flags); + } + + if (!mHasCompatScaling) { + return native_getTextRunAdvancesICU(mNativePaint, chars, index, count, + contextIndex, contextCount, flags, advances, advancesIndex); + } + + final float oldSize = getTextSize(); + setTextSize(oldSize * mCompatScaling); + float res = native_getTextRunAdvancesICU(mNativePaint, chars, index, count, + contextIndex, contextCount, flags, advances, advancesIndex); + setTextSize(oldSize); + + if (advances != null) { + for (int i = advancesIndex, e = i + count; i < e; i++) { + advances[i] *= mInvCompatScaling; + } + } + return res * mInvCompatScaling; // assume errors are not significant + } + + /** * Convenience overload that takes a CharSequence instead of a * String. * @@ -1569,6 +1611,41 @@ public class Paint { } /** + * Convenience overload that takes a CharSequence instead of a + * String. + * + * @see #getTextRunAdvances(String, int, int, int, int, int, float[], int) + * @hide + */ + public float getTextRunAdvancesICU(CharSequence text, int start, int end, + int contextStart, int contextEnd, int flags, float[] advances, + int advancesIndex) { + + if (text instanceof String) { + return getTextRunAdvancesICU((String) text, start, end, + contextStart, contextEnd, flags, advances, advancesIndex); + } + if (text instanceof SpannedString || + text instanceof SpannableString) { + return getTextRunAdvancesICU(text.toString(), start, end, + contextStart, contextEnd, flags, advances, advancesIndex); + } + if (text instanceof GraphicsOperations) { + return ((GraphicsOperations) text).getTextRunAdvancesICU(start, end, + contextStart, contextEnd, flags, advances, advancesIndex, this); + } + + int contextLen = contextEnd - contextStart; + int len = end - start; + char[] buf = TemporaryBuffer.obtain(contextLen); + TextUtils.getChars(text, start, end, buf, 0); + float result = getTextRunAdvancesICU(buf, start - contextStart, len, + 0, contextLen, flags, advances, advancesIndex); + TemporaryBuffer.recycle(buf); + return result; + } + + /** * Returns the total advance width for the characters in the run * between start and end, and if advances is not null, the advance * assigned to each of these characters (java chars). @@ -1644,6 +1721,44 @@ public class Paint { } /** + * Temporary - DO NOT USE + * + * @hide + */ + public float getTextRunAdvancesICU(String text, int start, int end, int contextStart, + int contextEnd, int flags, float[] advances, int advancesIndex) { + + if ((start | end | contextStart | contextEnd | advancesIndex | (end - start) + | (start - contextStart) | (contextEnd - end) + | (text.length() - contextEnd) + | (advances == null ? 0 : + (advances.length - advancesIndex - (end - start)))) < 0) { + throw new IndexOutOfBoundsException(); + } + if (flags != DIRECTION_LTR && flags != DIRECTION_RTL) { + throw new IllegalArgumentException("unknown flags value: " + flags); + } + + if (!mHasCompatScaling) { + return native_getTextRunAdvancesICU(mNativePaint, text, start, end, + contextStart, contextEnd, flags, advances, advancesIndex); + } + + final float oldSize = getTextSize(); + setTextSize(oldSize * mCompatScaling); + float totalAdvance = native_getTextRunAdvances(mNativePaint, text, start, end, + contextStart, contextEnd, flags, advances, advancesIndex); + setTextSize(oldSize); + + if (advances != null) { + for (int i = advancesIndex, e = i + (end - start); i < e; i++) { + advances[i] *= mInvCompatScaling; + } + } + return totalAdvance * mInvCompatScaling; // assume errors are insignificant + } + + /** * Returns the next cursor position in the run. This avoids placing the * cursor between surrogates, between characters that form conjuncts, * between base characters and combining marks, or within a reordering @@ -1907,6 +2022,13 @@ public class Paint { String text, int start, int end, int contextStart, int contextEnd, int flags, float[] advances, int advancesIndex); + private static native float native_getTextRunAdvancesICU(int native_object, + char[] text, int index, int count, int contextIndex, int contextCount, + int flags, float[] advances, int advancesIndex); + private static native float native_getTextRunAdvancesICU(int native_object, + String text, int start, int end, int contextStart, int contextEnd, + int flags, float[] advances, int advancesIndex); + private native int native_getTextRunCursor(int native_object, char[] text, int contextStart, int contextLength, int flags, int offset, int cursorOpt); private native int native_getTextRunCursor(int native_object, String text, diff --git a/tests/BiDiTests/res/layout/biditest_main.xml b/tests/BiDiTests/res/layout/biditest_main.xml index 9f77ad2..087c9a3 100644 --- a/tests/BiDiTests/res/layout/biditest_main.xml +++ b/tests/BiDiTests/res/layout/biditest_main.xml @@ -48,6 +48,11 @@ </LinearLayout> + <SeekBar android:id="@+id/seekbar" + android:layout_height="wrap_content" + android:layout_width="match_parent" + /> + <view class="com.android.bidi.BiDiTestView" android:id="@+id/main" android:layout_width="match_parent" diff --git a/tests/BiDiTests/res/values/strings.xml b/tests/BiDiTests/res/values/strings.xml index ecff76e..632a02e 100644 --- a/tests/BiDiTests/res/values/strings.xml +++ b/tests/BiDiTests/res/values/strings.xml @@ -18,9 +18,11 @@ <string name="edittext_text">mmmmmmmmmmmmmmmmmmmmmmmm</string> <string name="normal_text">Normal String</string> <string name="normal_long_text">mmmmmmmmmmmmmmmmmmmmmmmm</string> + <string name="normal_long_text_2">nnnnnnnnnnnnnnnnnnnnnnnn</string> + <string name="normal_long_text_3">Notify me when an open network is available</string> <string name="arabic_text">لا</string> <string name="chinese_text">利比亚局势或影响美俄关系发展</string> <string name="italic_text">Italic String</string> - <string name="bold_text">Bold String</string> + <string name="bold_text">Bold String - other text</string> <string name="bold_italic_text">Bold Italic String</string> </resources>
\ No newline at end of file diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java index 3d7dd81..6c71574 100644 --- a/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestActivity.java @@ -20,16 +20,44 @@ import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; +import android.widget.SeekBar; + +import static com.android.bidi.BiDiTestConstants.FONT_MIN_SIZE; +import static com.android.bidi.BiDiTestConstants.FONT_MAX_SIZE; public class BiDiTestActivity extends Activity { static final String TAG = "BiDiTestActivity"; + static final int INIT_TEXT_SIZE = (FONT_MAX_SIZE - FONT_MIN_SIZE) / 2; + + private BiDiTestView textView; + private SeekBar textSizeSeekBar; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.biditest_main); + + textView = (BiDiTestView) findViewById(R.id.main); + textView.setCurrentTextSize(INIT_TEXT_SIZE); + + textSizeSeekBar = (SeekBar) findViewById(R.id.seekbar); + textSizeSeekBar.setProgress(INIT_TEXT_SIZE); + textSizeSeekBar.setMax(FONT_MAX_SIZE - FONT_MIN_SIZE); + + textSizeSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { + public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { + textView.setCurrentTextSize(FONT_MIN_SIZE + progress); + } + + public void onStartTrackingTouch(SeekBar seekBar) { + } + + public void onStopTrackingTouch(SeekBar seekBar) { + } + }); } @Override diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestConstants.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestConstants.java new file mode 100644 index 0000000..5c28e3d --- /dev/null +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestConstants.java @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bidi; + +public class BiDiTestConstants { + public static final int FONT_MIN_SIZE = 8; + public static final int FONT_MAX_SIZE = 72; +} diff --git a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java index e9b6fa6..cd415c2 100644 --- a/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java +++ b/tests/BiDiTests/src/com/android/bidi/BiDiTestView.java @@ -32,9 +32,8 @@ public class BiDiTestView extends View { private static final int BORDER_PADDING = 4; private static final int TEXT_PADDING = 16; - private static final int TEXT_SIZE = 32; - private static final int ORIGIN = 48; - private static final int DELTA_Y = TEXT_SIZE; + private static final int TEXT_SIZE = 16; + private static final int ORIGIN = 80; private static final float DEFAULT_ITALIC_SKEW_X = -0.25f; @@ -43,6 +42,8 @@ public class BiDiTestView extends View { private String NORMAL_TEXT; private String NORMAL_LONG_TEXT; + private String NORMAL_LONG_TEXT_2; + private String NORMAL_LONG_TEXT_3; private String ITALIC_TEXT; private String BOLD_TEXT; private String BOLD_ITALIC_TEXT; @@ -51,6 +52,8 @@ public class BiDiTestView extends View { private Typeface typeface; + private int currentTextSize; + public BiDiTestView(Context context) { super(context); init(context); @@ -69,6 +72,8 @@ public class BiDiTestView extends View { private void init(Context context) { NORMAL_TEXT = context.getString(R.string.normal_text); NORMAL_LONG_TEXT = context.getString(R.string.normal_long_text); + NORMAL_LONG_TEXT_2 = context.getString(R.string.normal_long_text_2); + NORMAL_LONG_TEXT_3 = context.getString(R.string.normal_long_text_3); ITALIC_TEXT = context.getString(R.string.italic_text); BOLD_TEXT = context.getString(R.string.bold_text); BOLD_ITALIC_TEXT = context.getString(R.string.bold_italic_text); @@ -79,34 +84,50 @@ public class BiDiTestView extends View { paint.setAntiAlias(true); } + public void setCurrentTextSize(int size) { + currentTextSize = size; + invalidate(); + } + @Override public void onDraw(Canvas canvas) { drawInsideRect(canvas, Color.BLACK); - int deltaX = testString(canvas, NORMAL_TEXT, ORIGIN, ORIGIN, paint, typeface, - false, false, Paint.DIRECTION_LTR); - deltaX += testString(canvas, ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, paint, typeface, - true, false, Paint.DIRECTION_LTR); - deltaX += testString(canvas, BOLD_TEXT, ORIGIN + deltaX, ORIGIN, paint, typeface, - false, true, Paint.DIRECTION_LTR); - deltaX += testString(canvas, BOLD_ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, paint, typeface, - true, true, Paint.DIRECTION_LTR); + int deltaX = testString(canvas, NORMAL_TEXT, ORIGIN, ORIGIN, + paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); + + deltaX += testString(canvas, ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, + paint, typeface, true, false, Paint.DIRECTION_LTR, currentTextSize); + + deltaX += testString(canvas, BOLD_TEXT, ORIGIN + deltaX, ORIGIN, + paint, typeface, false, true, Paint.DIRECTION_LTR, currentTextSize); + + deltaX += testString(canvas, BOLD_ITALIC_TEXT, ORIGIN + deltaX, ORIGIN, + paint, typeface, true, true, Paint.DIRECTION_LTR, currentTextSize); // Test with a long string - deltaX = testString(canvas, NORMAL_LONG_TEXT, ORIGIN, ORIGIN + 2 * DELTA_Y, paint, typeface, - false, false, Paint.DIRECTION_LTR); + deltaX = testString(canvas, NORMAL_LONG_TEXT, ORIGIN, ORIGIN + 2 * currentTextSize, + paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); + + // Test with a long string + deltaX = testString(canvas, NORMAL_LONG_TEXT_2, ORIGIN, ORIGIN + 4 * currentTextSize, + paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); + + // Test with a long string + deltaX = testString(canvas, NORMAL_LONG_TEXT_3, ORIGIN, ORIGIN + 6 * currentTextSize, + paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); // Test Arabic ligature - deltaX = testString(canvas, ARABIC_TEXT, ORIGIN, ORIGIN + 4 * DELTA_Y, paint, typeface, - false, false, Paint.DIRECTION_RTL); + deltaX = testString(canvas, ARABIC_TEXT, ORIGIN, ORIGIN + 8 * currentTextSize, + paint, typeface, false, false, Paint.DIRECTION_RTL, currentTextSize); // Test Chinese - deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 6 * DELTA_Y, paint, typeface, - false, false, Paint.DIRECTION_LTR); + deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 10 * currentTextSize, + paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize); } private int testString(Canvas canvas, String text, int x, int y, Paint paint, Typeface typeface, - boolean isItalic, boolean isBold, int dir) { + boolean isItalic, boolean isBold, int dir, int textSize) { paint.setTypeface(typeface); // Set paint properties @@ -118,27 +139,28 @@ public class BiDiTestView extends View { paint.setTextSkewX(DEFAULT_ITALIC_SKEW_X); } - drawTextWithCanvasDrawText(text, canvas, x, y, TEXT_SIZE, Color.WHITE); + drawTextWithCanvasDrawText(text, canvas, x, y, textSize, Color.WHITE); int length = text.length(); float[] advances = new float[length]; - float textWidth = paint.getTextRunAdvances(text, 0, length, 0, length, 0, advances, 0); + float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, 0, advances, 0); + float textWidthICU = paint.getTextRunAdvancesICU(text, 0, length, 0, length, 0, advances, 0); - logAdvances(text, textWidth, advances); - drawBoxAroundText(canvas, x, y, textWidth, TEXT_SIZE, Color.RED); + logAdvances(text, textWidthHB, textWidthICU, advances); + drawMetricsAroundText(canvas, x, y, textWidthHB, textWidthICU, textSize, Color.RED, Color.GREEN); paint.setColor(Color.WHITE); char[] glyphs = new char[2*length]; int count = getGlyphs(text, glyphs, dir); - logGlypths(glyphs, count); - drawTextWithDrawGlyph(canvas, glyphs, count, x, y + DELTA_Y); +// logGlypths(glyphs, count); + drawTextWithDrawGlyph(canvas, glyphs, count, x, y + currentTextSize); // Restore old paint properties paint.setFakeBoldText(oldFakeBold); paint.setTextSkewX(oldTextSkewX); - return (int) Math.ceil(textWidth) + TEXT_PADDING; + return (int) Math.ceil(textWidthHB) + TEXT_PADDING; } private void drawTextWithDrawGlyph(Canvas canvas, char[] glyphs, int count, int x, int y) { @@ -172,19 +194,21 @@ public class BiDiTestView extends View { canvas.drawText(text, x, y, paint); } - private void drawBoxAroundText(Canvas canvas, int x, int y, float textWidth, int textSize, - int color) { + private void drawMetricsAroundText(Canvas canvas, int x, int y, float textWidthHB, + float textWidthICU, int textSize, int color, int colorICU) { paint.setColor(color); canvas.drawLine(x, y - textSize, x, y + 8, paint); - canvas.drawLine(x, y + 8, x + textWidth, y + 8, paint); - canvas.drawLine(x + textWidth, y - textSize, x + textWidth, y + 8, paint); + canvas.drawLine(x, y + 8, x + textWidthHB, y + 8, paint); + canvas.drawLine(x + textWidthHB, y - textSize, x + textWidthHB, y + 8, paint); + paint.setColor(colorICU); + canvas.drawLine(x + textWidthICU, y - textSize, x + textWidthICU, y + 8, paint); } - private void logAdvances(String text, float textWidth, float[] advances) { - Log.v(TAG, "Advances for text: " + text + " total=" + textWidth); - int length = advances.length; - for(int n=0; n<length; n++){ - Log.v(TAG, "adv[" + n + "]=" + advances[n]); - } + private void logAdvances(String text, float textWidth, float textWidthICU, float[] advances) { + Log.v(TAG, "Advances for text: " + text + " total= " + textWidth + " - totalICU= " + textWidthICU); +// int length = advances.length; +// for(int n=0; n<length; n++){ +// Log.v(TAG, "adv[" + n + "]=" + advances[n]); +// } } } |