Merge "Modify Canvas drawText to run bidi and shape."
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 drawText___CIIFFPaint(JNIEnv* env, jobject, SkCanvas* canvas,
|
||||
jcharArray text, int index, int count,
|
||||
jfloat x, jfloat y, SkPaint* paint) {
|
||||
jchar* textArray = env->GetCharArrayElements(text, NULL);
|
||||
jsize textCount = env->GetArrayLength(text);
|
||||
|
||||
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);
|
||||
canvas->drawText(textArray + index, count << 1, x_, y_, *paint);
|
||||
env->ReleaseCharArrayElements(text, textArray, 0);
|
||||
|
||||
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___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);
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
canvas->drawText((const uint16_t*)text_ + start, (end - start) << 1,
|
||||
x_, y_, *paint);
|
||||
env->ReleaseStringChars(text, (const jchar*) text_);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -58,6 +57,18 @@ public class Canvas {
|
||||
@SuppressWarnings({"UnusedDeclaration"})
|
||||
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};
|
||||
@@ -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 {
|
||||
} else {
|
||||
char[] buf = TemporaryBuffer.obtain(end - start);
|
||||
TextUtils.getChars(text, start, end, buf, 0);
|
||||
drawText(buf, 0, end - start, x, y, paint);
|
||||
native_drawText(mNativeCanvas, buf, 0, end - start, x, y,
|
||||
paint.mBidiFlags, paint.mNativePaint);
|
||||
TemporaryBuffer.recycle(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user