Merge "Add Unicode BiDi Algo before drawing text in Canvas"
This commit is contained in:
committed by
Android (Google) Code Review
commit
f7b9e78486
@@ -756,26 +756,36 @@ public:
|
||||
env->ReleaseStringChars(text, textArray);
|
||||
}
|
||||
|
||||
static void logGlyphs(sp<TextLayoutCacheValue> value) {
|
||||
LOGD("drawTextWithGlyphs -- got glyphs - count=%d", value->getGlyphsCount());
|
||||
for (size_t i = 0; i < value->getGlyphsCount(); i++) {
|
||||
LOGD(" glyphs[%d]=%d", i, value->getGlyphs()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
|
||||
int start, int end,
|
||||
jfloat x, jfloat y, int flags, SkPaint* paint) {
|
||||
|
||||
jint count = end - start;
|
||||
sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
|
||||
paint, textArray, start, count, count, flags);
|
||||
if (value == NULL) {
|
||||
LOGE("drawTextWithGlyphs -- cannot get Cache value");
|
||||
return ;
|
||||
}
|
||||
#if DEBUG_GLYPHS
|
||||
logGlyphs(value);
|
||||
#endif
|
||||
doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
|
||||
x, y, flags, paint);
|
||||
}
|
||||
|
||||
static void drawTextWithGlyphs___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
|
||||
jcharArray text, int index, int count,
|
||||
jfloat x, jfloat y, int flags, SkPaint* paint) {
|
||||
jchar* textArray = env->GetCharArrayElements(text, NULL);
|
||||
#if RTL_USE_HARFBUZZ && USE_TEXT_LAYOUT_CACHE
|
||||
sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
|
||||
paint, textArray + index, 0, count, count, flags);
|
||||
if (value != NULL) {
|
||||
#if DEBUG_GLYPHS
|
||||
LOGD("drawTextWithGlyphs -- got glyphs - count=%d", value->getGlyphsCount());
|
||||
for (size_t i = 0; i < value->getGlyphsCount(); i++) {
|
||||
LOGD(" glyphs[%d]=%d", i, value->getGlyphs()[i]);
|
||||
}
|
||||
#endif
|
||||
doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
|
||||
x, y, flags, paint);
|
||||
}
|
||||
#else
|
||||
TextLayout::drawText(paint, textArray + index, count, flags, x, y, canvas);
|
||||
#endif
|
||||
drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint);
|
||||
env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
|
||||
}
|
||||
|
||||
@@ -785,23 +795,7 @@ public:
|
||||
jfloat x, jfloat y, int flags, SkPaint* paint) {
|
||||
|
||||
const jchar* textArray = env->GetStringChars(text, NULL);
|
||||
#if RTL_USE_HARFBUZZ && USE_TEXT_LAYOUT_CACHE
|
||||
size_t count = end - start;
|
||||
sp<TextLayoutCacheValue> value = gTextLayoutCache.getValue(
|
||||
paint, textArray, start, count, count, flags);
|
||||
if (value != NULL) {
|
||||
#if DEBUG_GLYPHS
|
||||
LOGD("drawTextWithGlyphs -- got glyphs - count=%d", value->getGlyphsCount());
|
||||
for (size_t i = 0; i < value->getGlyphsCount(); i++) {
|
||||
LOGD(" glyphs[%d]=%d", i, value->getGlyphs()[i]);
|
||||
}
|
||||
#endif
|
||||
doDrawGlyphs(canvas, value->getGlyphs(), 0, value->getGlyphsCount(),
|
||||
x, y, flags, paint);
|
||||
}
|
||||
#else
|
||||
TextLayout::drawText(paint, textArray + start, end - start, flags, x, y, canvas);
|
||||
#endif
|
||||
drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint);
|
||||
env->ReleaseStringChars(text, textArray);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ static RtlDebugLevel readRtlDebugLevel() {
|
||||
#define DEBUG_ADVANCES 0
|
||||
|
||||
// Define if we want (1) to have Glyphs debug values or not (0)
|
||||
#define DEBUG_GLYPHS 0
|
||||
#define DEBUG_GLYPHS 1
|
||||
|
||||
} // namespace android
|
||||
#endif // ANDROID_RTL_PROPERTIES_H
|
||||
|
||||
@@ -46,27 +46,27 @@ namespace android {
|
||||
static TextLayoutCache gTextLayoutCache;
|
||||
#endif
|
||||
|
||||
enum {
|
||||
kBidi_LTR = 0,
|
||||
kBidi_RTL = 1,
|
||||
kBidi_Default_LTR = 2,
|
||||
kBidi_Default_RTL = 3,
|
||||
kBidi_Force_LTR = 4,
|
||||
kBidi_Force_RTL = 5,
|
||||
|
||||
kBidi_Mask = 0x7
|
||||
};
|
||||
|
||||
enum {
|
||||
kDirection_LTR = 0,
|
||||
kDirection_RTL = 1,
|
||||
|
||||
kDirection_Mask = 0x1
|
||||
};
|
||||
|
||||
class TextLayout {
|
||||
public:
|
||||
|
||||
enum {
|
||||
kDirection_LTR = 0,
|
||||
kDirection_RTL = 1,
|
||||
|
||||
kDirection_Mask = 0x1
|
||||
};
|
||||
|
||||
enum {
|
||||
kBidi_LTR = 0,
|
||||
kBidi_RTL = 1,
|
||||
kBidi_Default_LTR = 2,
|
||||
kBidi_Default_RTL = 3,
|
||||
kBidi_Force_LTR = 4,
|
||||
kBidi_Force_RTL = 5,
|
||||
|
||||
kBidi_Mask = 0x7
|
||||
};
|
||||
|
||||
/*
|
||||
* Draws a unidirectional run of text.
|
||||
*/
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
|
||||
#include "TextLayoutCache.h"
|
||||
#include "TextLayout.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -381,13 +382,127 @@ void TextLayoutCacheValue::shapeWithHarfbuzz(HB_ShaperItem* shaperItem, HB_FontR
|
||||
}
|
||||
}
|
||||
|
||||
struct GlyphRun {
|
||||
inline GlyphRun() {}
|
||||
inline GlyphRun(jchar* glyphs, size_t glyphsCount, bool isRTL) :
|
||||
glyphs(glyphs), glyphsCount(glyphsCount), isRTL(isRTL) { }
|
||||
jchar* glyphs;
|
||||
size_t glyphsCount;
|
||||
int isRTL;
|
||||
};
|
||||
|
||||
void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
|
||||
size_t start, size_t count, size_t contextCount, int dirFlags,
|
||||
jfloat* outAdvances, jfloat* outTotalAdvance,
|
||||
jchar** outGlyphs, size_t* outGlyphsCount) {
|
||||
|
||||
UBiDiLevel bidiReq = 0;
|
||||
bool forceLTR = false;
|
||||
bool forceRTL = false;
|
||||
|
||||
switch (dirFlags) {
|
||||
case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
|
||||
case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
|
||||
case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
|
||||
case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
|
||||
case kBidi_Force_LTR: forceLTR = true; break; // every char is LTR
|
||||
case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
|
||||
}
|
||||
|
||||
if (forceLTR || forceRTL) {
|
||||
#if DEBUG_GLYPHS
|
||||
LOGD("computeValuesWithHarfbuzz -- forcing run with LTR=%d RTL=%d",
|
||||
forceLTR, forceRTL);
|
||||
#endif
|
||||
computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
|
||||
outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
|
||||
} else {
|
||||
UBiDi* bidi = ubidi_open();
|
||||
if (bidi) {
|
||||
UErrorCode status = U_ZERO_ERROR;
|
||||
LOGD("computeValuesWithHarfbuzz -- bidiReq=%d", bidiReq);
|
||||
ubidi_setPara(bidi, chars, contextCount, bidiReq, NULL, &status);
|
||||
if (U_SUCCESS(status)) {
|
||||
int paraDir = ubidi_getParaLevel(bidi) & kDirection_Mask; // 0 if ltr, 1 if rtl
|
||||
size_t rc = ubidi_countRuns(bidi, &status);
|
||||
#if DEBUG_GLYPHS
|
||||
LOGD("computeValuesWithHarfbuzz -- dirFlags=%d run-count=%d paraDir=%d", dirFlags, rc, paraDir);
|
||||
#endif
|
||||
|
||||
if (rc == 1 || !U_SUCCESS(status)) {
|
||||
LOGD("HERE !!!");
|
||||
computeRunValuesWithHarfbuzz(paint, chars, start, count, contextCount,
|
||||
dirFlags, outAdvances, outTotalAdvance, outGlyphs, outGlyphsCount);
|
||||
ubidi_close(bidi);
|
||||
return;
|
||||
}
|
||||
|
||||
size_t runIndex = 0;
|
||||
Vector<GlyphRun> glyphRuns;
|
||||
for (size_t i = 0; i < rc; ++i) {
|
||||
int32_t startRun;
|
||||
int32_t lengthRun;
|
||||
UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &startRun, &lengthRun);
|
||||
|
||||
int newFlags = (runDir == UBIDI_RTL) ? kDirection_RTL : kDirection_LTR;
|
||||
jfloat runTotalAdvance = 0;
|
||||
jchar* runGlyphs;
|
||||
size_t runGlyphsCount = 0;
|
||||
|
||||
#if DEBUG_GLYPHS
|
||||
LOGD("computeValuesWithHarfbuzz -- run-start=%d run-len=%d newFlags=%d",
|
||||
startRun, lengthRun, newFlags);
|
||||
#endif
|
||||
computeRunValuesWithHarfbuzz(paint, chars, startRun,
|
||||
lengthRun, contextCount, newFlags,
|
||||
outAdvances + runIndex, &runTotalAdvance,
|
||||
&runGlyphs, &runGlyphsCount);
|
||||
|
||||
runIndex += lengthRun;
|
||||
|
||||
*outTotalAdvance += runTotalAdvance;
|
||||
*outGlyphsCount += runGlyphsCount;
|
||||
|
||||
#if DEBUG_GLYPHS
|
||||
LOGD("computeValuesWithHarfbuzz -- run=%d run-glyphs-count=%d",
|
||||
i, runGlyphsCount);
|
||||
for (size_t j = 0; j < runGlyphsCount; j++) {
|
||||
LOGD(" -- glyphs[%d]=%d", j, runGlyphs[j]);
|
||||
}
|
||||
#endif
|
||||
glyphRuns.push(GlyphRun(runGlyphs, runGlyphsCount, newFlags));
|
||||
}
|
||||
|
||||
#if DEBUG_GLYPHS
|
||||
LOGD("computeValuesWithHarfbuzz -- total-glyphs-count=%d", *outGlyphsCount);
|
||||
#endif
|
||||
*outGlyphs = new jchar[*outGlyphsCount];
|
||||
jchar* glyphs = *outGlyphs;
|
||||
for (size_t i = 0; i < glyphRuns.size(); i++) {
|
||||
const GlyphRun& glyphRun = glyphRuns.itemAt(i);
|
||||
if (glyphRun.isRTL) {
|
||||
for (size_t n = 0; n < glyphRun.glyphsCount; n++) {
|
||||
glyphs[glyphRun.glyphsCount - n - 1] = glyphRun.glyphs[n];
|
||||
}
|
||||
} else {
|
||||
memcpy(glyphs, glyphRun.glyphs, glyphRun.glyphsCount * sizeof(jchar));
|
||||
}
|
||||
glyphs += glyphRun.glyphsCount;
|
||||
delete[] glyphRun.glyphs;
|
||||
}
|
||||
}
|
||||
ubidi_close(bidi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
|
||||
size_t start, size_t count, size_t contextCount, int dirFlags,
|
||||
jfloat* outAdvances, jfloat* outTotalAdvance,
|
||||
jchar** outGlyphs, size_t* outGlyphsCount) {
|
||||
|
||||
bool isRTL = dirFlags & 0x1;
|
||||
|
||||
// TODO: need to run BiDi algo here to breakdown the text in several runs
|
||||
HB_ShaperItem shaperItem;
|
||||
HB_FontRec font;
|
||||
FontData fontData;
|
||||
@@ -397,7 +512,7 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar
|
||||
#if DEBUG_GLYPHS
|
||||
LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs,
|
||||
shaperItem.kerning_applied);
|
||||
LOGD(" -- string= '%s'", String8(chars, contextCount).string());
|
||||
LOGD(" -- string= '%s'", String8(chars + start, count).string());
|
||||
LOGD(" -- isDevKernText=%d", paint->isDevKernText());
|
||||
#endif
|
||||
|
||||
|
||||
@@ -178,6 +178,10 @@ private:
|
||||
static void createGlyphArrays(HB_ShaperItem* shaperItem, int size);
|
||||
static void resetGlyphArrays(HB_ShaperItem* shaperItem);
|
||||
|
||||
static void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars, size_t start,
|
||||
size_t count, size_t contextCount, int dirFlags,
|
||||
jfloat* outAdvances, jfloat* outTotalAdvance,
|
||||
jchar** outGlyphs, size_t* outGlyphsCount);
|
||||
}; // TextLayoutCacheValue
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
android:versionName="1.0">
|
||||
|
||||
<application android:label="BiDiTests">
|
||||
<activity android:name="BiDiTestActivity">
|
||||
<activity android:name="BiDiTestActivity"
|
||||
android:windowSoftInputMode="stateAlwaysHidden">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
|
||||
@@ -20,9 +20,10 @@
|
||||
<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="arabic_text">لا انا hello world</string>
|
||||
<string name="chinese_text">利比亚局势或影响美俄关系发展</string>
|
||||
<string name="italic_text">Italic String</string>
|
||||
<string name="bold_text">Bold String - other text</string>
|
||||
<string name="bold_italic_text">Bold Italic String</string>
|
||||
<string name="mixed_text_1">he said in Arabic: لا. Wow this is cool</string>
|
||||
</resources>
|
||||
@@ -49,6 +49,7 @@ public class BiDiTestView extends View {
|
||||
private String BOLD_ITALIC_TEXT;
|
||||
private String ARABIC_TEXT;
|
||||
private String CHINESE_TEXT;
|
||||
private String MIXED_TEXT_1;
|
||||
|
||||
private Typeface typeface;
|
||||
|
||||
@@ -79,6 +80,7 @@ public class BiDiTestView extends View {
|
||||
BOLD_ITALIC_TEXT = context.getString(R.string.bold_italic_text);
|
||||
ARABIC_TEXT = context.getString(R.string.arabic_text);
|
||||
CHINESE_TEXT = context.getString(R.string.chinese_text);
|
||||
MIXED_TEXT_1 = context.getString(R.string.mixed_text_1);
|
||||
|
||||
typeface = paint.getTypeface();
|
||||
paint.setAntiAlias(true);
|
||||
@@ -124,6 +126,10 @@ public class BiDiTestView extends View {
|
||||
// Test Chinese
|
||||
deltaX = testString(canvas, CHINESE_TEXT, ORIGIN, ORIGIN + 10 * currentTextSize,
|
||||
paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize);
|
||||
|
||||
// Test Mixed (English and Arabic)
|
||||
deltaX = testString(canvas, MIXED_TEXT_1, ORIGIN, ORIGIN + 12 * currentTextSize,
|
||||
paint, typeface, false, false, Paint.DIRECTION_LTR, currentTextSize);
|
||||
}
|
||||
|
||||
private int testString(Canvas canvas, String text, int x, int y, Paint paint, Typeface typeface,
|
||||
@@ -139,12 +145,15 @@ public class BiDiTestView extends View {
|
||||
paint.setTextSkewX(DEFAULT_ITALIC_SKEW_X);
|
||||
}
|
||||
|
||||
drawTextWithCanvasDrawText(text, canvas, x, y, textSize, Color.WHITE);
|
||||
Log.v(TAG, "START -- drawTextWithCanvasDrawText");
|
||||
drawTextWithCanvasDrawText(text, canvas, x, y, textSize, Color.WHITE, dir);
|
||||
Log.v(TAG, "END -- drawTextWithCanvasDrawText");
|
||||
|
||||
int length = text.length();
|
||||
float[] advances = new float[length];
|
||||
float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, 0, advances, 0);
|
||||
float textWidthICU = paint.getTextRunAdvancesICU(text, 0, length, 0, length, 0, advances, 0);
|
||||
float textWidthHB = paint.getTextRunAdvances(text, 0, length, 0, length, dir, advances, 0);
|
||||
setPaintDir(paint, dir);
|
||||
float textWidthICU = paint.getTextRunAdvancesICU(text, 0, length, 0, length, dir, advances, 0);
|
||||
|
||||
logAdvances(text, textWidthHB, textWidthICU, advances);
|
||||
drawMetricsAroundText(canvas, x, y, textWidthHB, textWidthICU, textSize, Color.RED, Color.GREEN);
|
||||
@@ -156,7 +165,9 @@ public class BiDiTestView extends View {
|
||||
// logGlypths(glyphs, count);
|
||||
// drawTextWithDrawGlyph(canvas, glyphs, count, x, y + currentTextSize);
|
||||
|
||||
Log.v(TAG, "START -- drawTextWithGlyphs");
|
||||
drawTextWithGlyphs(canvas, text, x, y + currentTextSize, dir);
|
||||
Log.v(TAG, "END -- drawTextWithGlyphs");
|
||||
|
||||
// Restore old paint properties
|
||||
paint.setFakeBoldText(oldFakeBold);
|
||||
@@ -165,12 +176,17 @@ public class BiDiTestView extends View {
|
||||
return (int) Math.ceil(textWidthHB) + TEXT_PADDING;
|
||||
}
|
||||
|
||||
private void setPaintDir(Paint paint, int dir) {
|
||||
Log.v(TAG, "Setting Paint dir=" + dir);
|
||||
paint.setBidiFlags(dir);
|
||||
}
|
||||
|
||||
private void drawTextWithDrawGlyph(Canvas canvas, char[] glyphs, int count, int x, int y) {
|
||||
canvas.drawGlyphs(glyphs, 0, count, x, y, paint);
|
||||
}
|
||||
|
||||
private void drawTextWithGlyphs(Canvas canvas, String text, int x, int y, int dir) {
|
||||
paint.setBidiFlags(dir);
|
||||
setPaintDir(paint, dir);
|
||||
canvas.drawTextWithGlyphs(text, x, y, paint);
|
||||
}
|
||||
|
||||
@@ -182,7 +198,6 @@ public class BiDiTestView extends View {
|
||||
}
|
||||
|
||||
private int getGlyphs(String text, char[] glyphs, int dir) {
|
||||
// int dir = 1; // Paint.DIRECTION_LTR;
|
||||
return paint.getTextGlypths(text, 0, text.length(), 0, text.length(), dir, glyphs);
|
||||
}
|
||||
|
||||
@@ -195,7 +210,8 @@ public class BiDiTestView extends View {
|
||||
}
|
||||
|
||||
private void drawTextWithCanvasDrawText(String text, Canvas canvas,
|
||||
float x, float y, float textSize, int color) {
|
||||
float x, float y, float textSize, int color, int dir) {
|
||||
setPaintDir(paint, dir);
|
||||
paint.setColor(color);
|
||||
paint.setTextSize(textSize);
|
||||
canvas.drawText(text, x, y, paint);
|
||||
|
||||
Reference in New Issue
Block a user