Merge "Modify Canvas drawText to run bidi and shape."

This commit is contained in:
Kenny Root
2010-05-12 19:34:01 -07:00
committed by Android (Google) Code Review
7 changed files with 481 additions and 102 deletions

View File

@@ -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);

View File

@@ -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}
*/

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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",

View File

@@ -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,

View File

@@ -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();