summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/text/GraphicsOperations.java6
-rw-r--r--core/java/android/text/SpannableStringBuilder.java38
-rw-r--r--core/java/android/text/TextLine.java43
-rw-r--r--core/java/android/widget/TextView.java5
-rw-r--r--core/jni/android/graphics/Canvas.cpp255
-rw-r--r--graphics/java/android/graphics/Canvas.java137
-rw-r--r--graphics/java/android/graphics/Paint.java95
7 files changed, 479 insertions, 100 deletions
diff --git a/core/java/android/text/GraphicsOperations.java b/core/java/android/text/GraphicsOperations.java
index c3bd0ae..d51bf7c 100644
--- a/core/java/android/text/GraphicsOperations.java
+++ b/core/java/android/text/GraphicsOperations.java
@@ -34,6 +34,12 @@ extends CharSequence
float x, float y, Paint p);
/**
+ * Just like {@link Canvas#drawTextRun}.
+ */
+ void drawTextRun(Canvas c, int start, int end,
+ float x, float y, int flags, Paint p);
+
+ /**
* Just like {@link Paint#measureText}.
*/
float measureText(int start, int end, Paint p);
diff --git a/core/java/android/text/SpannableStringBuilder.java b/core/java/android/text/SpannableStringBuilder.java
index caaafa1..7563179 100644
--- a/core/java/android/text/SpannableStringBuilder.java
+++ b/core/java/android/text/SpannableStringBuilder.java
@@ -17,8 +17,9 @@
package android.text;
import com.android.internal.util.ArrayUtils;
-import android.graphics.Paint;
+
import android.graphics.Canvas;
+import android.graphics.Paint;
import java.lang.reflect.Array;
@@ -780,7 +781,7 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
}
if (count == 0) {
- return (T[]) ArrayUtils.emptyArray(kind);
+ return ArrayUtils.emptyArray(kind);
}
if (count == 1) {
ret = (Object[]) Array.newInstance(kind, 1);
@@ -1055,6 +1056,39 @@ implements CharSequence, GetChars, Spannable, Editable, Appendable,
}
/**
+ * Don't call this yourself -- exists for Canvas to use internally.
+ * {@hide}
+ */
+ public void drawTextRun(Canvas c, int start, int end,
+ float x, float y, int flags, Paint p) {
+ checkRange("drawTextRun", start, end);
+
+ // Assume context requires no more than 8 chars on either side.
+ // This is ample, only decomposed U+FDFA falls into this
+ // category, and no one should put a style break within it
+ // anyway.
+ int cstart = start - 8;
+ if (cstart < 0) {
+ cstart = 0;
+ }
+ int cend = end + 8;
+ int max = length();
+ if (cend > max) {
+ cend = max;
+ }
+ if (cend <= mGapStart) {
+ c.drawTextRun(mText, start, end - start, x, y, flags, p);
+ } else if (cstart >= mGapStart) {
+ c.drawTextRun(mText, start + mGapLength, end - start, x, y, flags, p);
+ } else {
+ char[] buf = TextUtils.obtain(cend - cstart);
+ getChars(cstart, cend, buf, 0);
+ c.drawTextRun(buf, start - cstart, end - start, x, y, flags, p);
+ TextUtils.recycle(buf);
+ }
+ }
+
+ /**
* Don't call this yourself -- exists for Paint to use internally.
* {@hide}
*/
diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java
index 4aeabf3..fae3fc3 100644
--- a/core/java/android/text/TextLine.java
+++ b/core/java/android/text/TextLine.java
@@ -951,48 +951,11 @@ class TextLine {
private void drawTextRun(Canvas c, TextPaint wp, int start, int limit,
boolean runIsRtl, float x, int y) {
- // Since currently skia only renders text left-to-right, we need to
- // put the shaped characters into visual order before rendering.
- // Since we might want to re-render the line again, we swap them
- // back when we're done. If we left them swapped, measurement
- // would be broken since it expects the characters in logical order.
- if (runIsRtl) {
- swapRun(start, limit);
- }
+ int flags = runIsRtl ? Canvas.DIRECTION_RTL : Canvas.DIRECTION_LTR;
if (mCharsValid) {
- c.drawText(mChars, start, limit - start, x, y, wp);
+ c.drawTextRun(mChars, start, limit - start, x, y, flags, wp);
} else {
- c.drawText(mText, mStart + start, mStart + limit, x, y, wp);
- }
- if (runIsRtl) {
- swapRun(start, limit);
- }
- }
-
- /**
- * Reverses the order of characters in the chars array between start and
- * limit, used by drawTextRun.
- * @param start the start of the run to reverse
- * @param limit the limit of the run to reverse
- */
- private void swapRun(int start, int limit) {
- // First we swap all the characters one for one, then we
- // do another pass looking for surrogate pairs and swapping them
- // back into their logical order.
- char[] chars = mChars;
- for (int s = start, e = limit - 1; s < e; ++s, --e) {
- char ch = chars[s]; chars[s] = chars[e]; chars[e] = ch;
- }
-
- for (int s = start, e = limit - 1; s < e; ++s) {
- char c1 = chars[s];
- if (c1 >= 0xdc00 && c1 < 0xe000) {
- char c2 = chars[s+1];
- if (c2 >= 0xd800 && c2 < 0xdc00) {
- chars[s++] = c2;
- chars[s] = c1;
- }
- }
+ c.drawTextRun(mText, mStart + start, mStart + limit, x, y, flags, wp);
}
}
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 64c9c99..968636b 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -2781,6 +2781,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
c.drawText(mChars, start + mStart, end - start, x, y, p);
}
+ public void drawTextRun(Canvas c, int start, int end,
+ float x, float y, int flags, Paint p) {
+ c.drawTextRun(mChars, start + mStart, end - start, x, y, flags, p);
+ }
+
public float measureText(int start, int end, Paint p) {
return p.measureText(mChars, start + mStart, end - start);
}
diff --git a/core/jni/android/graphics/Canvas.cpp b/core/jni/android/graphics/Canvas.cpp
index e1e9536..4c7e762 100644
--- a/core/jni/android/graphics/Canvas.cpp
+++ b/core/jni/android/graphics/Canvas.cpp
@@ -30,6 +30,8 @@
#include "SkBoundaryPatch.h"
#include "SkMeshUtils.h"
+#include "unicode/ubidi.h"
+
#define TIME_DRAWx
static uint32_t get_thread_msec() {
@@ -52,6 +54,24 @@ namespace android {
class SkCanvasGlue {
public:
+ enum {
+ kDirection_LTR = 0,
+ kDirection_RTL = 1
+ };
+
+ enum {
+ 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
+ };
+
static void finalizer(JNIEnv* env, jobject clazz, SkCanvas* canvas) {
canvas->unref();
}
@@ -743,45 +763,206 @@ public:
canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL,
indices, indexCount, *paint);
}
+
+ static void shapeRtlText__(const jchar* text, jsize len, jsize start, jsize count, jchar* shaped) {
+ // fake shaping, just reverse the text
+ for (int i = 0; i < count; ++i) {
+ shaped[i] = text[start + count - 1 - i];
+ }
+ // fix surrogate pairs, if any
+ for (int i = 1; i < count; ++i) {
+ if (shaped[i] >= 0xd800 && shaped[i] < 0xdc00 &&
+ shaped[i-1] >= 0xdc00 && shaped[i-1] < 0xe000) {
+ jchar c = shaped[i]; shaped[i] = shaped[i-1]; shaped[i-1] = c;
+ i += 1;
+ }
+ }
+ }
+
+ static void drawText__(JNIEnv* env, SkCanvas* canvas, const jchar* text, jsize len,
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
+ SkScalar x_ = SkFloatToScalar(x);
+ SkScalar y_ = SkFloatToScalar(y);
+
+ SkPaint::Align horiz = paint->getTextAlign();
+
+ bool needBidi = (flags == kBidi_RTL) || (flags == kBidi_Default_RTL);
+ if (!needBidi && flags < kBidi_Force_LTR) {
+ for (int i = 0; i < len; ++i) {
+ if (text[i] >= 0x0590) {
+ needBidi = TRUE;
+ break;
+ }
+ }
+ }
+
+ int dir = (flags == kBidi_Force_RTL) ? kDirection_RTL : kDirection_LTR; // will be reset if we run bidi
+ UErrorCode status = U_ZERO_ERROR;
+ jchar *shaped = NULL;
+ int32_t slen = 0;
+ if (needBidi || (flags == kBidi_Force_RTL)) {
+ shaped = (jchar *)malloc(len * sizeof(jchar));
+ if (!shaped) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ if (needBidi) {
+ static int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING |
+ UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE;
+ jint lineDir = 0;
+ switch (flags) {
+ case kBidi_LTR: lineDir = 0; break; // no ICU constant, canonical LTR level
+ case kBidi_RTL: lineDir = 1; break; // no ICU constant, canonical RTL level
+ case kBidi_Default_LTR: lineDir = UBIDI_DEFAULT_LTR; break;
+ case kBidi_Default_RTL: lineDir = UBIDI_DEFAULT_RTL; break;
+ }
+
+ UBiDi* bidi = ubidi_open();
+ ubidi_setPara(bidi, text, len, lineDir, NULL, &status);
+ if (U_SUCCESS(status)) {
+ dir = ubidi_getParaLevel(bidi) & 0x1;
+
+ int rc = ubidi_countRuns(bidi, &status);
+ if (U_SUCCESS(status)) {
+ int32_t start;
+ int32_t length;
+ UBiDiDirection dir;
+ jchar *buffer = NULL;
+ for (int i = 0; i < rc; ++i) {
+ dir = ubidi_getVisualRun(bidi, i, &start, &length);
+ // fake shaping, except it doesn't shape, just mirrors and reverses
+ // use harfbuzz when available
+ if (dir == UBIDI_RTL) {
+ slen += ubidi_writeReverse(text + start, length, shaped + slen,
+ length, RTL_OPTS, &status);
+ } else {
+ for (int i = 0; i < length; ++i) {
+ shaped[slen + i] = text[start + i];
+ }
+ slen += length;
+ }
+ }
+ }
+ ubidi_close(bidi);
+ }
+ } else {
+ shapeRtlText__(text, len, 0, len, shaped);
+ }
+ }
+ }
+
+ if (!U_SUCCESS(status)) {
+ char buffer[35];
+ sprintf(buffer, "DrawText bidi error %d", status);
+ doThrowIAE(env, buffer);
+ } else {
+ bool trimLeft = false;
+ bool trimRight = false;
+
+ switch (horiz) {
+ case SkPaint::kLeft_Align: trimLeft = dir & kDirection_Mask; break;
+ case SkPaint::kCenter_Align: trimLeft = trimRight = true; break;
+ case SkPaint::kRight_Align: trimRight = !(dir & kDirection_Mask);
+ default: break;
+ }
+ const jchar* workText = shaped ? shaped : text;
+ const jchar* workLimit = workText + len;
+
+ if (trimLeft) {
+ while (workText < workLimit && *workText == ' ') {
+ ++workText;
+ }
+ }
+ if (trimRight) {
+ while (workLimit > workText && *(workLimit - 1) == ' ') {
+ --workLimit;
+ }
+ }
+ int32_t workBytes = (workLimit - workText) << 1;
+
+ canvas->drawText(workText, workBytes, x_, y_, *paint);
+ }
+
+ if (shaped) {
+ free(shaped);
+ }
+ }
- static void drawText___CIIFFPaint(JNIEnv* env, jobject, SkCanvas* canvas,
+ static void drawText___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
jcharArray text, int index, int count,
- jfloat x, jfloat y, SkPaint* paint) {
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
jchar* textArray = env->GetCharArrayElements(text, NULL);
- jsize textCount = env->GetArrayLength(text);
- SkScalar x_ = SkFloatToScalar(x);
- SkScalar y_ = SkFloatToScalar(y);
- canvas->drawText(textArray + index, count << 1, x_, y_, *paint);
- env->ReleaseCharArrayElements(text, textArray, 0);
+ drawText__(env, canvas, textArray + index, count, x, y, flags, paint);
+ env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
}
- static void drawText__StringIIFFPaint(JNIEnv* env, jobject,
- SkCanvas* canvas, jstring text, int start, int end,
- jfloat x, jfloat y, SkPaint* paint) {
- const void* text_ = env->GetStringChars(text, NULL);
- SkScalar x_ = SkFloatToScalar(x);
- SkScalar y_ = SkFloatToScalar(y);
- canvas->drawText((const uint16_t*)text_ + start, (end - start) << 1,
- x_, y_, *paint);
- env->ReleaseStringChars(text, (const jchar*) text_);
+ static void drawText__StringIIFFIPaint(JNIEnv* env, jobject,
+ SkCanvas* canvas, jstring text,
+ int start, int end,
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
+ const jchar* textArray = env->GetStringChars(text, NULL);
+ drawText__(env, canvas, textArray + start, end - start, x, y, flags, paint);
+ env->ReleaseStringChars(text, textArray);
}
- static void drawString(JNIEnv* env, jobject canvas, jstring text,
- jfloat x, jfloat y, jobject paint) {
- NPE_CHECK_RETURN_VOID(env, canvas);
- NPE_CHECK_RETURN_VOID(env, paint);
- NPE_CHECK_RETURN_VOID(env, text);
- size_t count = env->GetStringLength(text);
- if (0 == count) {
- return;
+ // Draws a unidirectional run of text. Does not run bidi, but does reorder the
+ // text and run shaping (or will, when we have harfbuzz support).
+ static void drawTextRun__(JNIEnv* env, SkCanvas* canvas, const jchar* chars, int len,
+ int start, int count,
+ jfloat x, jfloat y, int flags, SkPaint* paint) {
+
+ SkScalar x_ = SkFloatToScalar(x);
+ SkScalar y_ = SkFloatToScalar(y);
+
+ uint8_t rtl = flags & 0x1;
+
+ UErrorCode status = U_ZERO_ERROR;
+ jchar *shaped = NULL;
+ if (rtl) {
+ shaped = (jchar *)malloc(count * sizeof(jchar));
+ if (!shaped) {
+ status = U_MEMORY_ALLOCATION_ERROR;
+ } else {
+ shapeRtlText__(chars, len, start, count, shaped);
+ }
+ }
+
+ if (!U_SUCCESS(status)) {
+ char buffer[30];
+ sprintf(buffer, "DrawTextRun error %d", status);
+ doThrowIAE(env, buffer);
+ } else {
+ if (shaped) {
+ canvas->drawText(shaped, count << 1, x_, y_, *paint);
+ } else {
+ canvas->drawText(chars + start, count << 1, x_, y_, *paint);
+ }
+ }
+
+ if (shaped) {
+ free(shaped);
}
- const jchar* text_ = env->GetStringChars(text, NULL);
- SkCanvas* c = GraphicsJNI::getNativeCanvas(env, canvas);
- c->drawText(text_, count << 1, SkFloatToScalar(x), SkFloatToScalar(y),
- *GraphicsJNI::getNativePaint(env, paint));
- env->ReleaseStringChars(text, text_);
}
-
+
+ static void drawTextRun___CIIFFIPaint(
+ JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index,
+ int count, jfloat x, jfloat y, int flags, SkPaint* paint) {
+
+ jint len = env->GetArrayLength(text);
+ jchar* chars = env->GetCharArrayElements(text, NULL);
+ drawTextRun__(env, canvas, chars, len, index, count, x, y, flags, paint);
+ env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
+ }
+
+ static void drawTextRun__StringIIFFIPaint(
+ JNIEnv* env, jobject obj, SkCanvas* canvas, jstring text, int start,
+ int end, jfloat x, jfloat y, int flags, SkPaint* paint) {
+
+ jint len = env->GetStringLength(text);
+ const jchar* chars = env->GetStringChars(text, NULL);
+ drawTextRun__(env, canvas, chars, len, start, end - start, x, y, flags, paint);
+ env->ReleaseStringChars(text, chars);
+ }
+
static void drawPosText___CII_FPaint(JNIEnv* env, jobject, SkCanvas* canvas,
jcharArray text, int index, int count,
jfloatArray pos, SkPaint* paint) {
@@ -947,12 +1128,14 @@ static JNINativeMethod gCanvasMethods[] = {
(void*)SkCanvasGlue::drawBitmapMesh},
{"nativeDrawVertices", "(III[FI[FI[II[SIII)V",
(void*)SkCanvasGlue::drawVertices},
- {"native_drawText","(I[CIIFFI)V",
- (void*) SkCanvasGlue::drawText___CIIFFPaint},
- {"native_drawText","(ILjava/lang/String;IIFFI)V",
- (void*) SkCanvasGlue::drawText__StringIIFFPaint},
- {"drawText","(Ljava/lang/String;FFLandroid/graphics/Paint;)V",
- (void*) SkCanvasGlue::drawString},
+ {"native_drawText","(I[CIIFFII)V",
+ (void*) SkCanvasGlue::drawText___CIIFFIPaint},
+ {"native_drawText","(ILjava/lang/String;IIFFII)V",
+ (void*) SkCanvasGlue::drawText__StringIIFFIPaint},
+ {"native_drawTextRun","(I[CIIFFII)V",
+ (void*) SkCanvasGlue::drawTextRun___CIIFFIPaint},
+ {"native_drawTextRun","(ILjava/lang/String;IIFFII)V",
+ (void*) SkCanvasGlue::drawTextRun__StringIIFFIPaint},
{"native_drawPosText","(I[CII[FI)V",
(void*) SkCanvasGlue::drawPosText___CII_FPaint},
{"native_drawPosText","(ILjava/lang/String;[FI)V",
diff --git a/graphics/java/android/graphics/Canvas.java b/graphics/java/android/graphics/Canvas.java
index 345f810..064fab6 100644
--- a/graphics/java/android/graphics/Canvas.java
+++ b/graphics/java/android/graphics/Canvas.java
@@ -16,11 +16,10 @@
package android.graphics;
-import android.text.TextUtils;
-import android.text.SpannedString;
-import android.text.SpannableString;
import android.text.GraphicsOperations;
-import android.util.DisplayMetrics;
+import android.text.SpannableString;
+import android.text.SpannedString;
+import android.text.TextUtils;
import javax.microedition.khronos.opengles.GL;
@@ -59,6 +58,18 @@ public class Canvas {
private int mSurfaceFormat;
/**
+ * Flag for drawTextRun indicating left-to-right run direction.
+ * @hide
+ */
+ public static final int DIRECTION_LTR = 0;
+
+ /**
+ * Flag for drawTextRun indicating right-to-left run direction.
+ * @hide
+ */
+ public static final int DIRECTION_RTL = 1;
+
+ /**
* Construct an empty raster canvas. Use setBitmap() to specify a bitmap to
* draw into. The initial target density is {@link Bitmap#DENSITY_NONE};
* this will typically be replaced when a target bitmap is set for the
@@ -1246,8 +1257,8 @@ public class Canvas {
(text.length - index - count)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_drawText(mNativeCanvas, text, index, count, x, y,
- paint.mNativePaint);
+ native_drawText(mNativeCanvas, text, index, count, x, y, paint.mBidiFlags,
+ paint.mNativePaint);
}
/**
@@ -1259,7 +1270,10 @@ public class Canvas {
* @param y The y-coordinate of the origin of the text being drawn
* @param paint The paint used for the text (e.g. color, size, style)
*/
- public native void drawText(String text, float x, float y, Paint paint);
+ public void drawText(String text, float x, float y, Paint paint) {
+ native_drawText(mNativeCanvas, text, 0, text.length(), x, y, paint.mBidiFlags,
+ paint.mNativePaint);
+ }
/**
* Draw the text, with origin at (x,y), using the specified paint.
@@ -1277,8 +1291,8 @@ public class Canvas {
if ((start | end | (end - start) | (text.length() - end)) < 0) {
throw new IndexOutOfBoundsException();
}
- native_drawText(mNativeCanvas, text, start, end, x, y,
- paint.mNativePaint);
+ native_drawText(mNativeCanvas, text, start, end, x, y, paint.mBidiFlags,
+ paint.mNativePaint);
}
/**
@@ -1299,16 +1313,100 @@ public class Canvas {
if (text instanceof String || text instanceof SpannedString ||
text instanceof SpannableString) {
native_drawText(mNativeCanvas, text.toString(), start, end, x, y,
- paint.mNativePaint);
- }
- else if (text instanceof GraphicsOperations) {
+ paint.mBidiFlags, paint.mNativePaint);
+ } else if (text instanceof GraphicsOperations) {
((GraphicsOperations) text).drawText(this, start, end, x, y,
paint);
+ } else {
+ char[] buf = TemporaryBuffer.obtain(end - start);
+ TextUtils.getChars(text, start, end, buf, 0);
+ native_drawText(mNativeCanvas, buf, 0, end - start, x, y,
+ paint.mBidiFlags, paint.mNativePaint);
+ TemporaryBuffer.recycle(buf);
}
- else {
+ }
+
+ /**
+ * Render a run of all LTR or all RTL text, with shaping. This does not run
+ * bidi on the provided text, but renders it as a uniform right-to-left or
+ * left-to-right run, as indicated by dir. Alignment of the text is as
+ * determined by the Paint's TextAlign value.
+ *
+ * @param text the text to render
+ * @param index the start of the text to render. Data before this position
+ * can be used for shaping context.
+ * @param length the length of the text to render. Data at or after this
+ * position (start + length) can be used for shaping context.
+ * @param x the x position at which to draw the text
+ * @param y the y position at which to draw the text
+ * @param dir the run direction, either {@link DIRECTION_LTR} or
+ * {@link DIRECTION_RTL}.
+ * @param paint the paint
+ * @hide
+ */
+ public void drawTextRun(char[] text, int index, int length, float x, float y, int dir,
+ Paint paint) {
+
+ if (text == null) {
+ throw new NullPointerException("text is null");
+ }
+ if (paint == null) {
+ throw new NullPointerException("paint is null");
+ }
+ if ((index | length | text.length - index - length) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ if (dir != DIRECTION_LTR && dir != DIRECTION_RTL) {
+ throw new IllegalArgumentException("unknown dir: " + dir);
+ }
+
+ native_drawTextRun(mNativeCanvas, text, index, length, x, y, dir, paint.mNativePaint);
+ }
+
+ /**
+ * Render a run of all LTR or all RTL text, with shaping. This does not run
+ * bidi on the provided text, but renders it as a uniform right-to-left or
+ * left-to-right run, as indicated by dir. Alignment of the text is as
+ * determined by the Paint's TextAlign value.
+ *
+ * @param text the text to render
+ * @param start the start of the text to render. Data before this position
+ * can be used for shaping context.
+ * @param end the end of the text to render. Data at or after this
+ * position can be used for shaping context.
+ * @param x the x position at which to draw the text
+ * @param y the y position at which to draw the text
+ * @param dir the run direction, either 0 for LTR or 1 for RTL.
+ * @param paint the paint
+ * @hide
+ */
+ public void drawTextRun(CharSequence text, int start, int end, float x,
+ float y, int dir, Paint paint) {
+
+ if (text == null) {
+ throw new NullPointerException("text is null");
+ }
+ if (paint == null) {
+ throw new NullPointerException("paint is null");
+ }
+ if ((start | end | end - start | text.length() - end) < 0) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ int flags = dir == 0 ? 0 : 1;
+
+ if (text instanceof String || text instanceof SpannedString ||
+ text instanceof SpannableString) {
+ native_drawTextRun(mNativeCanvas, text.toString(), start, end, x, y,
+ flags, paint.mNativePaint);
+ } else if (text instanceof GraphicsOperations) {
+ ((GraphicsOperations) text).drawTextRun(this, start, end, x, y, flags,
+ paint);
+ } else {
char[] buf = TemporaryBuffer.obtain(end - start);
TextUtils.getChars(text, start, end, buf, 0);
- drawText(buf, 0, end - start, x, y, paint);
+ native_drawTextRun(mNativeCanvas, buf, 0, end - start, x, y,
+ flags, paint.mNativePaint);
TemporaryBuffer.recycle(buf);
}
}
@@ -1555,10 +1653,17 @@ public class Canvas {
private static native void native_drawText(int nativeCanvas, char[] text,
int index, int count, float x,
- float y, int paint);
+ float y, int flags, int paint);
private static native void native_drawText(int nativeCanvas, String text,
int start, int end, float x,
- float y, int paint);
+ float y, int flags, int paint);
+
+ private static native void native_drawTextRun(int nativeCanvas, String
+ text, int start, int end, float x, float y, int flags, int paint);
+
+ private static native void native_drawTextRun(int nativeCanvas, char[]
+ text, int start, int len, float x, float y, int flags, int paint);
+
private static native void native_drawPosText(int nativeCanvas,
char[] text, int index,
int count, float[] pos,
diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java
index 183c896..b564929 100644
--- a/graphics/java/android/graphics/Paint.java
+++ b/graphics/java/android/graphics/Paint.java
@@ -16,10 +16,10 @@
package android.graphics;
-import android.text.TextUtils;
+import android.text.GraphicsOperations;
import android.text.SpannableString;
import android.text.SpannedString;
-import android.text.GraphicsOperations;
+import android.text.TextUtils;
/**
* The Paint class holds the style and color information about how to draw
@@ -39,6 +39,7 @@ public class Paint {
private boolean mHasCompatScaling;
private float mCompatScaling;
private float mInvCompatScaling;
+ /* package */ int mBidiFlags = BIDI_DEFAULT_LTR;
private static final Style[] sStyleArray = {
Style.FILL, Style.STROKE, Style.FILL_AND_STROKE
@@ -76,8 +77,64 @@ public class Paint {
private static final int DEFAULT_PAINT_FLAGS = DEV_KERN_TEXT_FLAG;
/**
- * The Style specifies if the primitive being drawn is filled,
- * stroked, or both (in the same color). The default is FILL.
+ * Bidi flag to set LTR paragraph direction.
+ *
+ * @hide
+ */
+ public static final int BIDI_LTR = 0x0;
+
+ /**
+ * Bidi flag to set RTL paragraph direction.
+ *
+ * @hide
+ */
+ public static final int BIDI_RTL = 0x1;
+
+ /**
+ * Bidi flag to detect paragraph direction via heuristics, defaulting to
+ * LTR.
+ *
+ * @hide
+ */
+ public static final int BIDI_DEFAULT_LTR = 0x2;
+
+ /**
+ * Bidi flag to detect paragraph direction via heuristics, defaulting to
+ * RTL.
+ *
+ * @hide
+ */
+ public static final int BIDI_DEFAULT_RTL = 0x3;
+
+ /**
+ * Bidi flag to override direction to all LTR (ignore bidi).
+ *
+ * @hide
+ */
+ public static final int BIDI_FORCE_LTR = 0x4;
+
+ /**
+ * Bidi flag to override direction to all RTL (ignore bidi).
+ *
+ * @hide
+ */
+ public static final int BIDI_FORCE_RTL = 0x5;
+
+ /**
+ * Maximum Bidi flag value.
+ * @hide
+ */
+ private static final int BIDI_MAX_FLAG_VALUE = BIDI_FORCE_RTL;
+
+ /**
+ * Mask for bidi flags.
+ * @hide
+ */
+ private static final int BIDI_FLAG_MASK = 0x7;
+
+ /**
+ * The Style specifies if the primitive being drawn is filled, stroked, or
+ * both (in the same color). The default is FILL.
*/
public enum Style {
/**
@@ -210,6 +267,7 @@ public class Paint {
mHasCompatScaling = paint.mHasCompatScaling;
mCompatScaling = paint.mCompatScaling;
mInvCompatScaling = paint.mInvCompatScaling;
+ mBidiFlags = paint.mBidiFlags;
}
/** Restores the paint to its default settings. */
@@ -218,6 +276,7 @@ public class Paint {
setFlags(DEFAULT_PAINT_FLAGS);
mHasCompatScaling = false;
mCompatScaling = mInvCompatScaling = 1;
+ mBidiFlags = BIDI_DEFAULT_LTR;
}
/**
@@ -240,6 +299,7 @@ public class Paint {
mHasCompatScaling = src.mHasCompatScaling;
mCompatScaling = src.mCompatScaling;
mInvCompatScaling = src.mInvCompatScaling;
+ mBidiFlags = src.mBidiFlags;
}
}
@@ -254,10 +314,33 @@ public class Paint {
mInvCompatScaling = 1.0f/factor;
}
}
-
+
+ /**
+ * Return the bidi flags on the paint.
+ *
+ * @return the bidi flags on the paint
+ * @hide
+ */
+ public int getBidiFlags() {
+ return mBidiFlags;
+ }
+
+ /**
+ * Set the bidi flags on the paint.
+ * @hide
+ */
+ public void setBidiFlags(int flags) {
+ // only flag value is the 3-bit BIDI control setting
+ flags &= BIDI_FLAG_MASK;
+ if (flags > BIDI_MAX_FLAG_VALUE) {
+ throw new IllegalArgumentException("unknown bidi flag: " + flags);
+ }
+ mBidiFlags = flags;
+ }
+
/**
* Return the paint's flags. Use the Flag enum to test flag values.
- *
+ *
* @return the paint's flags (see enums ending in _Flag for bit masks)
*/
public native int getFlags();