diff --git a/core/api/current.txt b/core/api/current.txt index bb3264701ca5a..8d703d048e3b3 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -14928,6 +14928,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(); diff --git a/core/java/android/text/TextLine.java b/core/java/android/text/TextLine.java index e39231c9d64b8..7394c22ee93fe 100644 --- a/core/java/android/text/TextLine.java +++ b/core/java/android/text/TextLine.java @@ -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 decorations) { + @Nullable ArrayList 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; diff --git a/core/tests/coretests/src/android/text/TextLineTest.java b/core/tests/coretests/src/android/text/TextLineTest.java index 412d6ec975ac1..e3bcc8d2c2001 100644 --- a/core/tests/coretests/src/android/text/TextLineTest.java +++ b/core/tests/coretests/src/android/text/TextLineTest.java @@ -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 diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index 451b99ea7550c..1a80ab308bb5d 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -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, diff --git a/libs/hwui/jni/Paint.cpp b/libs/hwui/jni/Paint.cpp index 0aa14655725c9..ed453b158579c 100644 --- a/libs/hwui/jni/Paint.cpp +++ b/libs/hwui/jni/Paint.cpp @@ -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 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(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(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)); }