Merge "Introduce TextLine#measureAllbounds"

This commit is contained in:
Haoyu Zhang
2022-07-13 06:48:11 +00:00
committed by Android (Google) Code Review
5 changed files with 693 additions and 140 deletions

View File

@@ -14929,6 +14929,8 @@ package android.graphics {
method public android.graphics.PathEffect getPathEffect();
method public float getRunAdvance(char[], int, int, int, int, boolean, int);
method public float getRunAdvance(CharSequence, int, int, int, int, boolean, int);
method public float getRunCharacterAdvance(@NonNull char[], int, int, int, int, boolean, int, @Nullable float[], int);
method public float getRunCharacterAdvance(@NonNull CharSequence, int, int, int, int, boolean, int, @Nullable float[], int);
method public android.graphics.Shader getShader();
method @ColorInt public int getShadowLayerColor();
method @ColorLong public long getShadowLayerColorLong();

View File

@@ -408,14 +408,14 @@ public class TextLine {
final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
if (targetIsInThisSegment && sameDirection) {
return h + measureRun(segStart, offset, j, runIsRtl, fmi);
return h + measureRun(segStart, offset, j, runIsRtl, fmi, null, 0);
}
final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi);
final float segmentWidth = measureRun(segStart, j, j, runIsRtl, fmi, null, 0);
h += sameDirection ? segmentWidth : -segmentWidth;
if (targetIsInThisSegment) {
return h + measureRun(segStart, offset, j, runIsRtl, null);
return h + measureRun(segStart, offset, j, runIsRtl, null, null, 0);
}
if (j != runLimit) { // charAt(j) == TAB_CHAR
@@ -436,6 +436,116 @@ public class TextLine {
return h;
}
/**
* Return the signed horizontal bounds of the characters in the line.
*
* The length of the returned array equals to 2 * mLen. The left bound of the i th character
* is stored at index 2 * i. And the right bound of the i th character is stored at index
* (2 * i + 1).
*
* Check the following examples. LX(e.g. L0, L1, ...) denotes a character which has LTR BiDi
* property. On the other hand, RX(e.g. R0, R1, ...) denotes a character which has RTL BiDi
* property. Assuming all character has 1em width.
*
* Example 1: All LTR chars within LTR context
* Input Text (logical) : L0 L1 L2 L3
* Input Text (visual) : L0 L1 L2 L3
* Output : [0em, 1em, 1em, 2em, 2em, 3em, 3em, 4em]
*
* Example 2: All RTL chars within RTL context.
* Input Text (logical) : R0 R1 R2 R3
* Input Text (visual) : R3 R2 R1 R0
* Output : [-1em, 0em, -2em, -1em, -3em, -2em, -4em, -3em]
*
* Example 3: BiDi chars within LTR context.
* Input Text (logical) : L0 L1 R2 R3 L4 L5
* Input Text (visual) : L0 L1 R3 R2 L4 L5
* Output : [0em, 1em, 1em, 2em, 3em, 4em, 2em, 3em, 4em, 5em, 5em, 6em]
*
* Example 4: BiDi chars within RTL context.
* Input Text (logical) : L0 L1 R2 R3 L4 L5
* Input Text (visual) : L4 L5 R3 R2 L0 L1
* Output : [-2em, -1em, -1em, 0em, -3em, -2em, -4em, -3em, -6em, -5em, -5em, -4em]
*
* @param bounds the array to receive the character bounds data. Its length should be at least
* 2 times of the line length.
* @param advances the array to receive the character advance data, nullable. If provided, its
* length should be equal or larger than the line length.
*
* @throws IllegalArgumentException if the given {@code bounds} is null.
* @throws IndexOutOfBoundsException if the given {@code bounds} or {@code advances} doesn't
* have enough space to hold the result.
*/
public void measureAllBounds(@NonNull float[] bounds, @Nullable float[] advances) {
if (bounds == null) {
throw new IllegalArgumentException("bounds can't be null");
}
if (bounds.length < 2 * mLen) {
throw new IndexOutOfBoundsException("bounds doesn't have enough space to receive the "
+ "result, needed: " + (2 * mLen) + " had: " + bounds.length);
}
if (advances == null) {
advances = new float[mLen];
}
if (advances.length < mLen) {
throw new IndexOutOfBoundsException("advance doesn't have enough space to receive the "
+ "result, needed: " + mLen + " had: " + advances.length);
}
float h = 0;
for (int runIndex = 0; runIndex < mDirections.getRunCount(); runIndex++) {
final int runStart = mDirections.getRunStart(runIndex);
if (runStart > mLen) break;
final int runLimit = Math.min(runStart + mDirections.getRunLength(runIndex), mLen);
final boolean runIsRtl = mDirections.isRunRtl(runIndex);
int segStart = runStart;
for (int j = mHasTabs ? runStart : runLimit; j <= runLimit; j++) {
if (j == runLimit || charAt(j) == TAB_CHAR) {
final boolean sameDirection = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
final float segmentWidth =
measureRun(segStart, j, j, runIsRtl, null, advances, segStart);
final float oldh = h;
h += sameDirection ? segmentWidth : -segmentWidth;
float currh = sameDirection ? oldh : h;
for (int offset = segStart; offset < j && offset < mLen; ++offset) {
if (runIsRtl) {
bounds[2 * offset + 1] = currh;
currh -= advances[offset];
bounds[2 * offset] = currh;
} else {
bounds[2 * offset] = currh;
currh += advances[offset];
bounds[2 * offset + 1] = currh;
}
}
if (j != runLimit) { // charAt(j) == TAB_CHAR
final float leftX;
final float rightX;
if (runIsRtl) {
rightX = h;
h = mDir * nextTab(h * mDir);
leftX = h;
} else {
leftX = h;
h = mDir * nextTab(h * mDir);
rightX = h;
}
bounds[2 * j] = leftX;
bounds[2 * j + 1] = rightX;
advances[j] = rightX - leftX;
}
segStart = j + 1;
}
}
}
}
/**
* @see #measure(int, boolean, FontMetricsInt)
* @return The measure results for all possible offsets
@@ -464,15 +574,15 @@ public class TextLine {
if (j == runLimit || charAt(j) == TAB_CHAR) {
final float oldh = h;
final boolean advance = (mDir == Layout.DIR_RIGHT_TO_LEFT) == runIsRtl;
final float w = measureRun(segStart, j, j, runIsRtl, fmi);
final float w = measureRun(segStart, j, j, runIsRtl, fmi, null, 0);
h += advance ? w : -w;
final float baseh = advance ? oldh : h;
FontMetricsInt crtfmi = advance ? fmi : null;
for (int offset = segStart; offset <= j && offset <= mLen; ++offset) {
if (target[offset] >= segStart && target[offset] < j) {
measurement[offset] =
baseh + measureRun(segStart, offset, j, runIsRtl, crtfmi);
measurement[offset] = baseh
+ measureRun(segStart, offset, j, runIsRtl, crtfmi, null, 0);
}
}
@@ -518,14 +628,14 @@ public class TextLine {
boolean needWidth) {
if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
float w = -measureRun(start, limit, limit, runIsRtl, null);
float w = -measureRun(start, limit, limit, runIsRtl, null, null, 0);
handleRun(start, limit, limit, runIsRtl, c, null, x + w, top,
y, bottom, null, false);
y, bottom, null, false, null, 0);
return w;
}
return handleRun(start, limit, limit, runIsRtl, c, null, x, top,
y, bottom, null, needWidth);
y, bottom, null, needWidth, null, 0);
}
/**
@@ -538,12 +648,15 @@ public class TextLine {
* @param runIsRtl true if the run is right-to-left
* @param fmi receives metrics information about the requested
* run, can be null.
* @param advances receives the advance information about the requested run, can be null.
* @param advancesIndex the start index to fill in the advance information.
* @return the signed width from the start of the run to the leading edge
* of the character at offset, based on the run (not paragraph) direction
*/
private float measureRun(int start, int offset, int limit, boolean runIsRtl,
FontMetricsInt fmi) {
return handleRun(start, offset, limit, runIsRtl, null, null, 0, 0, 0, 0, fmi, true);
@Nullable FontMetricsInt fmi, @Nullable float[] advances, int advancesIndex) {
return handleRun(start, offset, limit, runIsRtl, null, null, 0, 0, 0, 0, fmi, true,
advances, advancesIndex);
}
/**
@@ -562,13 +675,14 @@ public class TextLine {
int limit, boolean runIsRtl, float x, boolean needWidth) {
if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
float w = -measureRun(start, limit, limit, runIsRtl, null);
handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null, false);
float w = -measureRun(start, limit, limit, runIsRtl, null, null, 0);
handleRun(start, limit, limit, runIsRtl, null, consumer, x + w, 0, 0, 0, null,
false, null, 0);
return w;
}
return handleRun(start, limit, limit, runIsRtl, null, consumer, x, 0, 0, 0, null,
needWidth);
needWidth, null, 0);
}
@@ -908,14 +1022,16 @@ public class TextLine {
}
private float getRunAdvance(TextPaint wp, int start, int end, int contextStart, int contextEnd,
boolean runIsRtl, int offset) {
boolean runIsRtl, int offset, @Nullable float[] advances, int advancesIndex) {
if (mCharsValid) {
return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset);
return wp.getRunCharacterAdvance(mChars, start, end, contextStart, contextEnd,
runIsRtl, offset, advances, advancesIndex);
} else {
final int delta = mStart;
if (mComputed == null) {
return wp.getRunAdvance(mText, delta + start, delta + end,
delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
if (mComputed == null || advances != null) {
return wp.getRunCharacterAdvance(mText, delta + start, delta + end,
delta + contextStart, delta + contextEnd, runIsRtl,
delta + offset, advances, advancesIndex);
} else {
return mComputed.getWidth(start + delta, end + delta);
}
@@ -940,6 +1056,8 @@ public class TextLine {
* @param needWidth true if the width of the run is needed
* @param offset the offset for the purpose of measuring
* @param decorations the list of locations and paremeters for drawing decorations
* @param advances receives the advance information about the requested run, can be null.
* @param advancesIndex the start index to fill in the advance information.
* @return the signed width of the run based on the run direction; only
* valid if needWidth is true
*/
@@ -947,7 +1065,8 @@ public class TextLine {
int contextStart, int contextEnd, boolean runIsRtl,
Canvas c, TextShaper.GlyphsConsumer consumer, float x, int top, int y, int bottom,
FontMetricsInt fmi, boolean needWidth, int offset,
@Nullable ArrayList<DecorationInfo> decorations) {
@Nullable ArrayList<DecorationInfo> decorations,
@Nullable float[] advances, int advancesIndex) {
if (mIsJustifying) {
wp.setWordSpacing(mAddedWidthForJustify);
@@ -967,7 +1086,8 @@ public class TextLine {
final int numDecorations = decorations == null ? 0 : decorations.size();
if (needWidth || ((c != null || consumer != null) && (wp.bgColor != 0
|| numDecorations != 0 || runIsRtl))) {
totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset);
totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset,
advances, advancesIndex);
}
final float leftX, rightX;
@@ -1009,10 +1129,10 @@ public class TextLine {
final int decorationStart = Math.max(info.start, start);
final int decorationEnd = Math.min(info.end, offset);
float decorationStartAdvance = getRunAdvance(
wp, start, end, contextStart, contextEnd, runIsRtl, decorationStart);
float decorationEndAdvance = getRunAdvance(
wp, start, end, contextStart, contextEnd, runIsRtl, decorationEnd);
float decorationStartAdvance = getRunAdvance(wp, start, end, contextStart,
contextEnd, runIsRtl, decorationStart, null, 0);
float decorationEndAdvance = getRunAdvance(wp, start, end, contextStart,
contextEnd, runIsRtl, decorationEnd, null, 0);
final float decorationXLeft, decorationXRight;
if (runIsRtl) {
decorationXLeft = rightX - decorationEndAdvance;
@@ -1179,19 +1299,27 @@ public class TextLine {
* @param bottom the bottom of the line
* @param fmi receives metrics information, can be null
* @param needWidth true if the width is required
* @param advances receives the advance information about the requested run, can be null.
* @param advancesIndex the start index to fill in the advance information.
* @return the signed width of the run based on the run direction; only
* valid if needWidth is true
*/
private float handleRun(int start, int measureLimit,
int limit, boolean runIsRtl, Canvas c,
TextShaper.GlyphsConsumer consumer, float x, int top, int y,
int bottom, FontMetricsInt fmi, boolean needWidth) {
int bottom, FontMetricsInt fmi, boolean needWidth,
@Nullable float[] advances, int advancesIndex) {
if (measureLimit < start || measureLimit > limit) {
throw new IndexOutOfBoundsException("measureLimit (" + measureLimit + ") is out of "
+ "start (" + start + ") and limit (" + limit + ") bounds");
}
if (advances != null && advances.length - advancesIndex < measureLimit - start) {
throw new IndexOutOfBoundsException("advances doesn't have enough space to receive the "
+ "result");
}
// Case of an empty line, make sure we update fmi according to mPaint
if (start == measureLimit) {
final TextPaint wp = mWorkPaint;
@@ -1218,7 +1346,7 @@ public class TextLine {
wp.setStartHyphenEdit(adjustStartHyphenEdit(start, wp.getStartHyphenEdit()));
wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
return handleText(wp, start, limit, start, limit, runIsRtl, c, consumer, x, top,
y, bottom, fmi, needWidth, measureLimit, null);
y, bottom, fmi, needWidth, measureLimit, null, advances, advancesIndex);
}
// Shaping needs to take into account context up to metric boundaries,
@@ -1257,8 +1385,16 @@ public class TextLine {
}
if (replacement != null) {
x += handleReplacement(replacement, wp, i, mlimit, runIsRtl, c, x, top, y,
bottom, fmi, needWidth || mlimit < measureLimit);
final float width = handleReplacement(replacement, wp, i, mlimit, runIsRtl, c,
x, top, y, bottom, fmi, needWidth || mlimit < measureLimit);
x += width;
if (advances != null) {
// For replacement, the entire width is assigned to the first character.
advances[advancesIndex + i - start] = runIsRtl ? -width : width;
for (int j = i + 1; j < mlimit; ++j) {
advances[advancesIndex + j - start] = 0.0f;
}
}
continue;
}
@@ -1300,7 +1436,8 @@ public class TextLine {
adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c,
consumer, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
Math.min(activeEnd, mlimit), mDecorations);
Math.min(activeEnd, mlimit), mDecorations,
advances, advancesIndex + activeStart - start);
activeStart = j;
activePaint.set(wp);
@@ -1327,7 +1464,8 @@ public class TextLine {
adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, consumer, x,
top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
Math.min(activeEnd, mlimit), mDecorations);
Math.min(activeEnd, mlimit), mDecorations,
advances, advancesIndex + activeStart - start);
}
return x - originalX;

View File

@@ -26,6 +26,7 @@ import android.graphics.Paint;
import android.graphics.Typeface;
import android.platform.test.annotations.Presubmit;
import android.text.Layout.TabStops;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.ReplacementSpan;
import android.text.style.TabStopSpan;
@@ -97,7 +98,7 @@ public class TextLineTest {
InstrumentationRegistry.getInstrumentation().getTargetContext().getAssets(),
"fonts/StaticLayoutLineBreakingTestFont.ttf");
private TextLine getTextLine(String str, TextPaint paint, TabStops tabStops) {
private TextLine getTextLine(CharSequence str, TextPaint paint, TabStops tabStops) {
Layout layout =
StaticLayout.Builder.obtain(str, 0, str.length(), paint, Integer.MAX_VALUE)
.build();
@@ -109,7 +110,7 @@ public class TextLineTest {
return tl;
}
private TextLine getTextLine(String str, TextPaint paint) {
private TextLine getTextLine(CharSequence str, TextPaint paint) {
return getTextLine(str, paint, null);
}
@@ -316,14 +317,275 @@ public class TextLineTest {
assertTrue(span.mIsUsed);
}
@Test
public void testMeasureAllBounds_LTR() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
TextLine tl = getTextLine("IIIIIV", paint);
float[] bounds = new float[12];
float[] advances = new float[6];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 20.0f, 20.0f, 30.0f, 30.0f, 40.0f,
40.0f, 50.0f, 50.0f, 100.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 50.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_LTR_StyledText() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
SpannableString text = new SpannableString("IIIIIV");
text.setSpan(new AbsoluteSizeSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextLine tl = getTextLine(text, paint);
float[] bounds = new float[12];
float[] advances = new float[6];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 15.0f, 15.0f, 20.0f, 20.0f, 30.0f,
30.0f, 40.0f, 40.0f, 90.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 5.0f, 5.0f, 10.0f, 10.0f, 50.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_RTL() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
TextLine tl = getTextLine("\u05D0\u05D0\u05D0\u05D0\u05D0\u05D1", paint);
float[] bounds = new float[12];
float[] advances = new float[6];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {-10.0f, 0.0f, -20.0f, -10.0f, -30.0f, -20.0f, -40.0f, -30.0f,
-50.0f, -40.0f, -100.0f, -50.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 50.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_RTL_StyledText() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
SpannableString text = new SpannableString("\u05D0\u05D0\u05D0\u05D0\u05D0\u05D1");
text.setSpan(new AbsoluteSizeSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextLine tl = getTextLine(text, paint);
float[] bounds = new float[12];
float[] advances = new float[6];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {-10.0f, 0.0f, -15.0f, -10.0f, -20.0f, -15.0f,
-30.0f, -20.0f, -40.0f, -30.0f, -90.0f, -40.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 5.0f, 5.0f, 10.0f, 10.0f, 50.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_BiDi() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
TextLine tl = getTextLine("II\u05D0\u05D0II", paint);
float[] bounds = new float[12];
float[] advances = new float[6];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 20.0f, 30.0f, 40.0f, 20.0f, 30.0f,
40.0f, 50.0f, 50.0f, 60.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 10.0f, 10.0f, 10.0f, 10.0f, 10.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_BiDi2() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
TextLine tl = getTextLine("I" + RLI + "I\u05D0\u05D0" + PDI + "I", paint);
float[] bounds = new float[14];
float[] advances = new float[7];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 10.0f, 30.0f, 40.0f, 20.0f, 30.0f,
10.0f, 20.0f, 40.0f, 40.0f, 40.0f, 50.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 0.0f, 10.0f, 10.0f, 10.0f, 0.0f, 10.0f}, advances,
0.0f);
}
@Test
public void testMeasureAllBounds_BiDi3() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
TextLine tl = getTextLine("\u05D0" + LRI + "\u05D0II" + PDI + "\u05D0", paint);
float[] bounds = new float[14];
float[] advances = new float[7];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {-10.0f, 0.0f, -10.0f, -10.0f, -40.0f, -30.0f,
-30.0f, -20.0f, -20.0f, -10.0f, -40.0f, -40.0f, -50.0f, -40.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 0.0f, 10.0f, 10.0f, 10.0f, 0.0f, 10.0f}, advances,
0.0f);
}
@Test
public void testMeasureAllBounds_styled_BiDi() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
SpannableString text = new SpannableString("II\u05D0\u05D0II");
text.setSpan(new AbsoluteSizeSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextLine tl = getTextLine(text, paint);
float[] bounds = new float[12];
float[] advances = new float[6];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 15.0f, 25.0f, 30.0f,
15.0f, 25.0f, 30.0f, 40.0f, 40.0f, 50.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 5.0f, 5.0f, 10.0f, 10.0f, 10.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_Tab_LTR() {
final Object[] spans = { new TabStopSpan.Standard(100) };
final TabStops stops = new TabStops(100, spans);
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
TextLine tl = getTextLine("II\tII", paint, stops);
float[] bounds = new float[10];
float[] advances = new float[5];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 20.0f, 20.0f, 100.0f, 100.0f, 110.0f,
110.0f, 120.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 10.0f, 80.0f, 10.0f, 10.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_Tab_RTL() {
final Object[] spans = { new TabStopSpan.Standard(100) };
final TabStops stops = new TabStops(100, spans);
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
TextLine tl = getTextLine("\u05D0\u05D0\t\u05D0\u05D0", paint, stops);
float[] bounds = new float[10];
float[] advances = new float[5];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {-10.0f, 0.0f, -20.0f, -10.0f, -100.0f, -20.0f,
-110.0f, -100.0f, -120.0f, -110.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 10.0f, 80.0f, 10.0f, 10.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_Tab_BiDi() {
final Object[] spans = { new TabStopSpan.Standard(100) };
final TabStops stops = new TabStops(100, spans);
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
TextLine tl = getTextLine("I\u05D0\tI\u05D0", paint, stops);
float[] bounds = new float[10];
float[] advances = new float[5];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 20.0f, 20.0f, 100.0f,
100.0f, 110.0f, 110.0f, 120.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 10.0f, 80.0f, 10.0f, 10.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_Tab_BiDi2() {
final Object[] spans = { new TabStopSpan.Standard(100) };
final TabStops stops = new TabStops(100, spans);
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
TextLine tl = getTextLine("\u05D0I\t\u05D0I", paint, stops);
float[] bounds = new float[10];
float[] advances = new float[5];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {-10.0f, 0.0f, -20.0f, -10.0f, -100.0f, -20.0f,
-110.0f, -100.0f, -120.0f, -110.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 10.0f, 80.0f, 10.0f, 10.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_replacement_LTR() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
SpannableString text = new SpannableString("IIIII");
text.setSpan(new TestReplacementSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextLine tl = getTextLine(text, paint);
float[] bounds = new float[10];
float[] advances = new float[5];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 15.0f, 15.0f, 15.0f,
15.0f, 25.0f, 25.0f, 35.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 5.0f, 0.0f, 10.0f, 10.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_replacement_RTL() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
SpannableString text = new SpannableString("\u05D0\u05D0\u05D0\u05D0\u05D0");
text.setSpan(new TestReplacementSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextLine tl = getTextLine(text, paint);
float[] bounds = new float[10];
float[] advances = new float[5];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {-10.0f, 0.0f, -15.0f, -10.0f, -15.0f, -15.0f,
-25.0f, -15.0f, -35.0f, -25.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 5.0f, 0.0f, 10.0f, 10.0f}, advances, 0.0f);
}
@Test
public void testMeasureAllBounds_replacement_BiDi() {
final TextPaint paint = new TextPaint();
paint.setTypeface(TYPEFACE);
paint.setTextSize(10.0f); // make 1em = 10px
SpannableString text = new SpannableString("II\u05D0\u05D0II");
text.setSpan(new TestReplacementSpan(5), 1, 3, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
TextLine tl = getTextLine(text, paint);
float[] bounds = new float[12];
float[] advances = new float[6];
tl.measureAllBounds(bounds, advances);
assertArrayEquals(new float[] {0.0f, 10.0f, 10.0f, 15.0f, 15.0f, 15.0f,
15.0f, 25.0f, 25.0f, 35.0f, 35.0f, 45.0f}, bounds, 0.0f);
assertArrayEquals(new float[] {10.0f, 5.0f, 0.0f, 10.0f, 10.0f, 10.0f}, advances, 0.0f);
}
private static class TestReplacementSpan extends ReplacementSpan {
boolean mIsUsed;
private final int mWidth;
TestReplacementSpan() {
mWidth = 0;
}
TestReplacementSpan(int width) {
mWidth = width;
}
@Override
public int getSize(Paint paint, CharSequence text, int start, int end,
Paint.FontMetricsInt fm) {
mIsUsed = true;
return 0;
return mWidth;
}
@Override

View File

@@ -3136,6 +3136,128 @@ public class Paint {
return result;
}
/**
* Measure the advance of each character within a run of text and also return the cursor
* position within the run.
*
* @see #getRunAdvance(char[], int, int, int, int, boolean, int) for more details.
*
* @param text the text to measure. Cannot be null.
* @param start the index of the start of the range to measure
* @param end the index + 1 of the end of the range to measure
* @param contextStart the index of the start of the shaping context
* @param contextEnd the index + 1 of the end of the shaping context
* @param isRtl whether the run is in RTL direction
* @param offset index of caret position
* @param advances the array that receives the computed character advances
* @param advancesIndex the start index from which the advances array is filled
* @return width measurement between start and offset
* @throws IndexOutOfBoundsException if a) contextStart or contextEnd is out of array's range
* or contextStart is larger than contextEnd,
* b) start or end is not within the range [contextStart, contextEnd), or start is larger than
* end,
* c) offset is not within the range [start, end),
* d) advances.length - advanceIndex is smaller than the length of the run, which equals to
* end - start.
*
*/
public float getRunCharacterAdvance(@NonNull char[] text, int start, int end, int contextStart,
int contextEnd, boolean isRtl, int offset,
@Nullable float[] advances, int advancesIndex) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
if (contextStart < 0 || contextEnd > text.length) {
throw new IndexOutOfBoundsException("Invalid Context Range: " + contextStart + ", "
+ contextEnd + " must be in 0, " + text.length);
}
if (start < contextStart || contextEnd < end) {
throw new IndexOutOfBoundsException("Invalid start/end range: " + start + ", " + end
+ " must be in " + contextStart + ", " + contextEnd);
}
if (offset < start || end < offset) {
throw new IndexOutOfBoundsException("Invalid offset position: " + offset
+ " must be in " + start + ", " + end);
}
if (advances != null && advances.length < advancesIndex - start + end) {
throw new IndexOutOfBoundsException("Given array doesn't have enough space to receive "
+ "the result, advances.length: " + advances.length + " advanceIndex: "
+ advancesIndex + " needed space: " + (offset - start));
}
if (end == start) {
return 0.0f;
}
return nGetRunCharacterAdvance(mNativePaint, text, start, end, contextStart, contextEnd,
isRtl, offset, advances, advancesIndex);
}
/**
* @see #getRunCharacterAdvance(char[], int, int, int, int, boolean, int, float[], int)
*
* @param text the text to measure. Cannot be null.
* @param start the index of the start of the range to measure
* @param end the index + 1 of the end of the range to measure
* @param contextStart the index of the start of the shaping context
* @param contextEnd the index + 1 of the end of the shaping context
* @param isRtl whether the run is in RTL direction
* @param offset index of caret position
* @param advances the array that receives the computed character advances
* @param advancesIndex the start index from which the advances array is filled
* @return width measurement between start and offset
* @throws IndexOutOfBoundsException if a) contextStart or contextEnd is out of array's range
* or contextStart is larger than contextEnd,
* b) start or end is not within the range [contextStart, contextEnd), or end is larger than
* start,
* c) offset is not within the range [start, end),
* d) advances.length - advanceIndex is smaller than the run length, which equals to
* end - start.
*/
public float getRunCharacterAdvance(@NonNull CharSequence text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, int offset,
@Nullable float[] advances, int advancesIndex) {
if (text == null) {
throw new IllegalArgumentException("text cannot be null");
}
if (contextStart < 0 || contextEnd > text.length()) {
throw new IndexOutOfBoundsException("Invalid Context Range: " + contextStart + ", "
+ contextEnd + " must be in 0, " + text.length());
}
if (start < contextStart || contextEnd < end) {
throw new IndexOutOfBoundsException("Invalid start/end range: " + start + ", " + end
+ " must be in " + contextStart + ", " + contextEnd);
}
if (offset < start || end < offset) {
throw new IndexOutOfBoundsException("Invalid offset position: " + offset
+ " must be in " + start + ", " + end);
}
if (advances != null && advances.length < advancesIndex - start + end) {
throw new IndexOutOfBoundsException("Given array doesn't have enough space to receive "
+ "the result, advances.length: " + advances.length + " advanceIndex: "
+ advancesIndex + " needed space: " + (offset - start));
}
if (end == start) {
return 0.0f;
}
char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart);
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
final float result = getRunCharacterAdvance(buf, start - contextStart, end - contextStart,
0, contextEnd - contextStart, isRtl, offset - contextStart,
advances, advancesIndex);
TemporaryBuffer.recycle(buf);
return result;
}
/**
* Get the character offset within the string whose position is closest to the specified
* horizontal position.
@@ -3247,6 +3369,9 @@ public class Paint {
private static native boolean nHasGlyph(long paintPtr, int bidiFlags, String string);
private static native float nGetRunAdvance(long paintPtr, char[] text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, int offset);
private static native float nGetRunCharacterAdvance(long paintPtr, char[] text, int start,
int end, int contextStart, int contextEnd, boolean isRtl, int offset, float[] advances,
int advancesIndex);
private static native int nGetOffsetForAdvance(long paintPtr, char[] text, int start, int end,
int contextStart, int contextEnd, boolean isRtl, float advance);
private static native void nGetFontMetricsIntForText(long paintPtr, char[] text,

View File

@@ -497,16 +497,29 @@ namespace PaintGlue {
return true;
}
static jfloat doRunAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[],
jint start, jint count, jint bufSize, jboolean isRtl, jint offset) {
static jfloat doRunAdvance(JNIEnv* env, const Paint* paint, const Typeface* typeface,
const jchar buf[], jint start, jint count, jint bufSize,
jboolean isRtl, jint offset, jfloatArray advances,
jint advancesIndex) {
if (advances) {
size_t advancesLength = env->GetArrayLength(advances);
if ((size_t)(count + advancesIndex) > advancesLength) {
doThrowAIOOBE(env);
return 0;
}
}
minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
if (offset == start + count) {
if (offset == start + count && advances == nullptr) {
return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
bufSize, nullptr);
}
std::unique_ptr<float[]> advancesArray(new float[count]);
MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
advancesArray.get());
if (advances) {
env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
}
return minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset);
}
@@ -515,9 +528,23 @@ namespace PaintGlue {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
const Typeface* typeface = paint->getAndroidTypeface();
ScopedCharArrayRO textArray(env, text);
jfloat result = doRunAdvance(paint, typeface, textArray.get() + contextStart,
start - contextStart, end - start, contextEnd - contextStart, isRtl,
offset - contextStart);
jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart,
start - contextStart, end - start, contextEnd - contextStart,
isRtl, offset - contextStart, nullptr, 0);
return result;
}
static jfloat getRunCharacterAdvance___CIIIIZI_FI_F(JNIEnv* env, jclass, jlong paintHandle,
jcharArray text, jint start, jint end,
jint contextStart, jint contextEnd,
jboolean isRtl, jint offset,
jfloatArray advances, jint advancesIndex) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
const Typeface* typeface = paint->getAndroidTypeface();
ScopedCharArrayRO textArray(env, text);
jfloat result = doRunAdvance(env, paint, typeface, textArray.get() + contextStart,
start - contextStart, end - start, contextEnd - contextStart,
isRtl, offset - contextStart, advances, advancesIndex);
return result;
}
@@ -1034,113 +1061,112 @@ namespace PaintGlue {
}; // namespace PaintGlue
static const JNINativeMethod methods[] = {
{"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer},
{"nInit","()J", (void*) PaintGlue::init},
{"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
{"nBreakText","(J[CIIFI[F)I", (void*) PaintGlue::breakTextC},
{"nBreakText","(JLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
{"nGetTextAdvances","(J[CIIIII[FI)F",
(void*) PaintGlue::getTextAdvances___CIIIII_FI},
{"nGetTextAdvances","(JLjava/lang/String;IIIII[FI)F",
(void*) PaintGlue::getTextAdvances__StringIIIII_FI},
{"nGetNativeFinalizer", "()J", (void*)PaintGlue::getNativeFinalizer},
{"nInit", "()J", (void*)PaintGlue::init},
{"nInitWithPaint", "(J)J", (void*)PaintGlue::initWithPaint},
{"nBreakText", "(J[CIIFI[F)I", (void*)PaintGlue::breakTextC},
{"nBreakText", "(JLjava/lang/String;ZFI[F)I", (void*)PaintGlue::breakTextS},
{"nGetTextAdvances", "(J[CIIIII[FI)F", (void*)PaintGlue::getTextAdvances___CIIIII_FI},
{"nGetTextAdvances", "(JLjava/lang/String;IIIII[FI)F",
(void*)PaintGlue::getTextAdvances__StringIIIII_FI},
{"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
{"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
(void*) PaintGlue::getTextRunCursor__String},
{"nGetTextPath", "(JI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
{"nGetTextPath", "(JILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
{"nGetStringBounds", "(JLjava/lang/String;IIILandroid/graphics/Rect;)V",
(void*) PaintGlue::getStringBounds },
{"nGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V",
(void*) PaintGlue::getCharArrayBounds },
{"nHasGlyph", "(JILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
{"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
{"nGetOffsetForAdvance", "(J[CIIIIZF)I",
(void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
{"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
(void*)PaintGlue::getFontMetricsIntForText___C},
{"nGetFontMetricsIntForText",
"(JLjava/lang/String;IIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
(void*)PaintGlue::getFontMetricsIntForText___String},
{"nGetTextRunCursor", "(J[CIIIII)I", (void*)PaintGlue::getTextRunCursor___C},
{"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
(void*)PaintGlue::getTextRunCursor__String},
{"nGetTextPath", "(JI[CIIFFJ)V", (void*)PaintGlue::getTextPath___C},
{"nGetTextPath", "(JILjava/lang/String;IIFFJ)V", (void*)PaintGlue::getTextPath__String},
{"nGetStringBounds", "(JLjava/lang/String;IIILandroid/graphics/Rect;)V",
(void*)PaintGlue::getStringBounds},
{"nGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V",
(void*)PaintGlue::getCharArrayBounds},
{"nHasGlyph", "(JILjava/lang/String;)Z", (void*)PaintGlue::hasGlyph},
{"nGetRunAdvance", "(J[CIIIIZI)F", (void*)PaintGlue::getRunAdvance___CIIIIZI_F},
{"nGetRunCharacterAdvance", "(J[CIIIIZI[FI)F",
(void*)PaintGlue::getRunCharacterAdvance___CIIIIZI_FI_F},
{"nGetOffsetForAdvance", "(J[CIIIIZF)I", (void*)PaintGlue::getOffsetForAdvance___CIIIIZF_I},
{"nGetFontMetricsIntForText", "(J[CIIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
(void*)PaintGlue::getFontMetricsIntForText___C},
{"nGetFontMetricsIntForText",
"(JLjava/lang/String;IIIIZLandroid/graphics/Paint$FontMetricsInt;)V",
(void*)PaintGlue::getFontMetricsIntForText___String},
// --------------- @FastNative ----------------------
// --------------- @FastNative ----------------------
{"nSetTextLocales","(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales},
{"nSetFontFeatureSettings","(JLjava/lang/String;)V",
(void*) PaintGlue::setFontFeatureSettings},
{"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F",
(void*)PaintGlue::getFontMetrics},
{"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I",
(void*)PaintGlue::getFontMetricsInt},
{"nSetTextLocales", "(JLjava/lang/String;)I", (void*)PaintGlue::setTextLocales},
{"nSetFontFeatureSettings", "(JLjava/lang/String;)V",
(void*)PaintGlue::setFontFeatureSettings},
{"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F",
(void*)PaintGlue::getFontMetrics},
{"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I",
(void*)PaintGlue::getFontMetricsInt},
// --------------- @CriticalNative ------------------
// --------------- @CriticalNative ------------------
{"nReset","(J)V", (void*) PaintGlue::reset},
{"nSet","(JJ)V", (void*) PaintGlue::assign},
{"nGetFlags","(J)I", (void*) PaintGlue::getFlags},
{"nSetFlags","(JI)V", (void*) PaintGlue::setFlags},
{"nGetHinting","(J)I", (void*) PaintGlue::getHinting},
{"nSetHinting","(JI)V", (void*) PaintGlue::setHinting},
{"nSetAntiAlias","(JZ)V", (void*) PaintGlue::setAntiAlias},
{"nSetSubpixelText","(JZ)V", (void*) PaintGlue::setSubpixelText},
{"nSetLinearText","(JZ)V", (void*) PaintGlue::setLinearText},
{"nSetUnderlineText","(JZ)V", (void*) PaintGlue::setUnderlineText},
{"nSetStrikeThruText","(JZ)V", (void*) PaintGlue::setStrikeThruText},
{"nSetFakeBoldText","(JZ)V", (void*) PaintGlue::setFakeBoldText},
{"nSetFilterBitmap","(JZ)V", (void*) PaintGlue::setFilterBitmap},
{"nSetDither","(JZ)V", (void*) PaintGlue::setDither},
{"nGetStyle","(J)I", (void*) PaintGlue::getStyle},
{"nSetStyle","(JI)V", (void*) PaintGlue::setStyle},
{"nSetColor","(JI)V", (void*) PaintGlue::setColor},
{"nSetColor","(JJJ)V", (void*) PaintGlue::setColorLong},
{"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha},
{"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth},
{"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth},
{"nGetStrokeMiter","(J)F", (void*) PaintGlue::getStrokeMiter},
{"nSetStrokeMiter","(JF)V", (void*) PaintGlue::setStrokeMiter},
{"nGetStrokeCap","(J)I", (void*) PaintGlue::getStrokeCap},
{"nSetStrokeCap","(JI)V", (void*) PaintGlue::setStrokeCap},
{"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin},
{"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin},
{"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath},
{"nSetShader","(JJ)J", (void*) PaintGlue::setShader},
{"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter},
{"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode},
{"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect},
{"nSetMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter},
{"nSetTypeface","(JJ)V", (void*) PaintGlue::setTypeface},
{"nGetTextAlign","(J)I", (void*) PaintGlue::getTextAlign},
{"nSetTextAlign","(JI)V", (void*) PaintGlue::setTextAlign},
{"nSetTextLocalesByMinikinLocaleListId","(JI)V",
(void*) PaintGlue::setTextLocalesByMinikinLocaleListId},
{"nIsElegantTextHeight","(J)Z", (void*) PaintGlue::isElegantTextHeight},
{"nSetElegantTextHeight","(JZ)V", (void*) PaintGlue::setElegantTextHeight},
{"nGetTextSize","(J)F", (void*) PaintGlue::getTextSize},
{"nSetTextSize","(JF)V", (void*) PaintGlue::setTextSize},
{"nGetTextScaleX","(J)F", (void*) PaintGlue::getTextScaleX},
{"nSetTextScaleX","(JF)V", (void*) PaintGlue::setTextScaleX},
{"nGetTextSkewX","(J)F", (void*) PaintGlue::getTextSkewX},
{"nSetTextSkewX","(JF)V", (void*) PaintGlue::setTextSkewX},
{"nGetLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing},
{"nSetLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing},
{"nGetWordSpacing","(J)F", (void*) PaintGlue::getWordSpacing},
{"nSetWordSpacing","(JF)V", (void*) PaintGlue::setWordSpacing},
{"nGetStartHyphenEdit", "(J)I", (void*) PaintGlue::getStartHyphenEdit},
{"nGetEndHyphenEdit", "(J)I", (void*) PaintGlue::getEndHyphenEdit},
{"nSetStartHyphenEdit", "(JI)V", (void*) PaintGlue::setStartHyphenEdit},
{"nSetEndHyphenEdit", "(JI)V", (void*) PaintGlue::setEndHyphenEdit},
{"nAscent","(J)F", (void*) PaintGlue::ascent},
{"nDescent","(J)F", (void*) PaintGlue::descent},
{"nGetUnderlinePosition","(J)F", (void*) PaintGlue::getUnderlinePosition},
{"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness},
{"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
{"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
{"nSetShadowLayer", "(JFFFJJ)V", (void*)PaintGlue::setShadowLayer},
{"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
{"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
{"nReset", "(J)V", (void*)PaintGlue::reset},
{"nSet", "(JJ)V", (void*)PaintGlue::assign},
{"nGetFlags", "(J)I", (void*)PaintGlue::getFlags},
{"nSetFlags", "(JI)V", (void*)PaintGlue::setFlags},
{"nGetHinting", "(J)I", (void*)PaintGlue::getHinting},
{"nSetHinting", "(JI)V", (void*)PaintGlue::setHinting},
{"nSetAntiAlias", "(JZ)V", (void*)PaintGlue::setAntiAlias},
{"nSetSubpixelText", "(JZ)V", (void*)PaintGlue::setSubpixelText},
{"nSetLinearText", "(JZ)V", (void*)PaintGlue::setLinearText},
{"nSetUnderlineText", "(JZ)V", (void*)PaintGlue::setUnderlineText},
{"nSetStrikeThruText", "(JZ)V", (void*)PaintGlue::setStrikeThruText},
{"nSetFakeBoldText", "(JZ)V", (void*)PaintGlue::setFakeBoldText},
{"nSetFilterBitmap", "(JZ)V", (void*)PaintGlue::setFilterBitmap},
{"nSetDither", "(JZ)V", (void*)PaintGlue::setDither},
{"nGetStyle", "(J)I", (void*)PaintGlue::getStyle},
{"nSetStyle", "(JI)V", (void*)PaintGlue::setStyle},
{"nSetColor", "(JI)V", (void*)PaintGlue::setColor},
{"nSetColor", "(JJJ)V", (void*)PaintGlue::setColorLong},
{"nSetAlpha", "(JI)V", (void*)PaintGlue::setAlpha},
{"nGetStrokeWidth", "(J)F", (void*)PaintGlue::getStrokeWidth},
{"nSetStrokeWidth", "(JF)V", (void*)PaintGlue::setStrokeWidth},
{"nGetStrokeMiter", "(J)F", (void*)PaintGlue::getStrokeMiter},
{"nSetStrokeMiter", "(JF)V", (void*)PaintGlue::setStrokeMiter},
{"nGetStrokeCap", "(J)I", (void*)PaintGlue::getStrokeCap},
{"nSetStrokeCap", "(JI)V", (void*)PaintGlue::setStrokeCap},
{"nGetStrokeJoin", "(J)I", (void*)PaintGlue::getStrokeJoin},
{"nSetStrokeJoin", "(JI)V", (void*)PaintGlue::setStrokeJoin},
{"nGetFillPath", "(JJJ)Z", (void*)PaintGlue::getFillPath},
{"nSetShader", "(JJ)J", (void*)PaintGlue::setShader},
{"nSetColorFilter", "(JJ)J", (void*)PaintGlue::setColorFilter},
{"nSetXfermode", "(JI)V", (void*)PaintGlue::setXfermode},
{"nSetPathEffect", "(JJ)J", (void*)PaintGlue::setPathEffect},
{"nSetMaskFilter", "(JJ)J", (void*)PaintGlue::setMaskFilter},
{"nSetTypeface", "(JJ)V", (void*)PaintGlue::setTypeface},
{"nGetTextAlign", "(J)I", (void*)PaintGlue::getTextAlign},
{"nSetTextAlign", "(JI)V", (void*)PaintGlue::setTextAlign},
{"nSetTextLocalesByMinikinLocaleListId", "(JI)V",
(void*)PaintGlue::setTextLocalesByMinikinLocaleListId},
{"nIsElegantTextHeight", "(J)Z", (void*)PaintGlue::isElegantTextHeight},
{"nSetElegantTextHeight", "(JZ)V", (void*)PaintGlue::setElegantTextHeight},
{"nGetTextSize", "(J)F", (void*)PaintGlue::getTextSize},
{"nSetTextSize", "(JF)V", (void*)PaintGlue::setTextSize},
{"nGetTextScaleX", "(J)F", (void*)PaintGlue::getTextScaleX},
{"nSetTextScaleX", "(JF)V", (void*)PaintGlue::setTextScaleX},
{"nGetTextSkewX", "(J)F", (void*)PaintGlue::getTextSkewX},
{"nSetTextSkewX", "(JF)V", (void*)PaintGlue::setTextSkewX},
{"nGetLetterSpacing", "(J)F", (void*)PaintGlue::getLetterSpacing},
{"nSetLetterSpacing", "(JF)V", (void*)PaintGlue::setLetterSpacing},
{"nGetWordSpacing", "(J)F", (void*)PaintGlue::getWordSpacing},
{"nSetWordSpacing", "(JF)V", (void*)PaintGlue::setWordSpacing},
{"nGetStartHyphenEdit", "(J)I", (void*)PaintGlue::getStartHyphenEdit},
{"nGetEndHyphenEdit", "(J)I", (void*)PaintGlue::getEndHyphenEdit},
{"nSetStartHyphenEdit", "(JI)V", (void*)PaintGlue::setStartHyphenEdit},
{"nSetEndHyphenEdit", "(JI)V", (void*)PaintGlue::setEndHyphenEdit},
{"nAscent", "(J)F", (void*)PaintGlue::ascent},
{"nDescent", "(J)F", (void*)PaintGlue::descent},
{"nGetUnderlinePosition", "(J)F", (void*)PaintGlue::getUnderlinePosition},
{"nGetUnderlineThickness", "(J)F", (void*)PaintGlue::getUnderlineThickness},
{"nGetStrikeThruPosition", "(J)F", (void*)PaintGlue::getStrikeThruPosition},
{"nGetStrikeThruThickness", "(J)F", (void*)PaintGlue::getStrikeThruThickness},
{"nSetShadowLayer", "(JFFFJJ)V", (void*)PaintGlue::setShadowLayer},
{"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
{"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
};
int register_android_graphics_Paint(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
}