Merge "Implement fallback line spacing for BoringLayout"

This commit is contained in:
TreeHugger Robot
2022-01-08 00:58:35 +00:00
committed by Android (Google) Code Review
13 changed files with 518 additions and 80 deletions

View File

@@ -15977,6 +15977,8 @@ package android.graphics {
method public String getFontFeatureSettings();
method public float getFontMetrics(android.graphics.Paint.FontMetrics);
method public android.graphics.Paint.FontMetrics getFontMetrics();
method public void getFontMetricsInt(@NonNull CharSequence, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean, @NonNull android.graphics.Paint.FontMetricsInt);
method public void getFontMetricsInt(@NonNull char[], @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, @IntRange(from=0) int, boolean, @NonNull android.graphics.Paint.FontMetricsInt);
method public int getFontMetricsInt(android.graphics.Paint.FontMetricsInt);
method public android.graphics.Paint.FontMetricsInt getFontMetricsInt();
method public float getFontSpacing();
@@ -44654,6 +44656,7 @@ package android.text {
public class BoringLayout extends android.text.Layout implements android.text.TextUtils.EllipsizeCallback {
ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
ctor public BoringLayout(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
ctor public BoringLayout(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, float, float, @NonNull android.text.BoringLayout.Metrics, boolean, @NonNull android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
method public void ellipsized(int, int);
method public int getBottomPadding();
method public int getEllipsisCount(int);
@@ -44668,9 +44671,12 @@ package android.text {
method public int getTopPadding();
method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint);
method public static android.text.BoringLayout.Metrics isBoring(CharSequence, android.text.TextPaint, android.text.BoringLayout.Metrics);
method @Nullable public static android.text.BoringLayout.Metrics isBoring(@NonNull CharSequence, @NonNull android.text.TextPaint, @NonNull android.text.TextDirectionHeuristic, boolean, @Nullable android.text.BoringLayout.Metrics);
method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
method public static android.text.BoringLayout make(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
method @NonNull public static android.text.BoringLayout make(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @NonNull android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean);
method @NonNull public android.text.BoringLayout replaceOrMake(@NonNull CharSequence, @NonNull android.text.TextPaint, @IntRange(from=0) int, @NonNull android.text.Layout.Alignment, @NonNull android.text.BoringLayout.Metrics, boolean, @NonNull android.text.TextUtils.TruncateAt, @IntRange(from=0) int, boolean);
method public android.text.BoringLayout replaceOrMake(CharSequence, android.text.TextPaint, int, android.text.Layout.Alignment, float, float, android.text.BoringLayout.Metrics, boolean, android.text.TextUtils.TruncateAt, int);
}
@@ -44879,6 +44885,7 @@ package android.text {
method public abstract int getTopPadding();
method public final int getWidth();
method public final void increaseWidthTo(int);
method public boolean isFallbackLineSpacingEnabled();
method public boolean isRtlCharAt(int);
method protected final boolean isSpanned();
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2

View File

@@ -16,6 +16,9 @@
package android.text;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.compat.annotation.UnsupportedAppUsage;
import android.graphics.Canvas;
import android.graphics.Paint;
@@ -84,6 +87,37 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
includePad, ellipsize, ellipsizedWidth);
}
/**
* Utility function to construct a BoringLayout instance.
*
* The spacing multiplier and additional amount spacing are not used by BoringLayout.
* {@link Layout#getSpacingMultiplier()} will return 1.0 and {@link Layout#getSpacingAdd()} will
* return 0.0.
*
* @param source the text to render
* @param paint the default paint for the layout
* @param outerWidth the wrapping width for the text
* @param align whether to left, right, or center the text
* @param metrics {@code #Metrics} instance that contains information about FontMetrics and
* line width
* @param includePad set whether to include extra space beyond font ascent and descent which is
* needed to avoid clipping in some scripts
* @param ellipsize whether to ellipsize the text if width of the text is longer than the
* requested width
* @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
* {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
* not used, {@code outerWidth} is used instead
*/
public static @NonNull BoringLayout make(
@NonNull CharSequence source, @NonNull TextPaint paint,
@IntRange(from = 0) int outerWidth,
@NonNull Alignment align, @NonNull BoringLayout.Metrics metrics,
boolean includePad, @NonNull TextUtils.TruncateAt ellipsize,
@IntRange(from = 0) int ellipsizedWidth, boolean useFallbackLineSpacing) {
return new BoringLayout(source, paint, outerWidth, align, 1f, 0f, metrics, includePad,
ellipsize, ellipsizedWidth, useFallbackLineSpacing);
}
/**
* Returns a BoringLayout for the specified text, potentially reusing
* this one if it is already suitable. The caller must make sure that
@@ -109,7 +143,57 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
mEllipsizedStart = 0;
mEllipsizedCount = 0;
init(source, paint, align, metrics, includePad, true);
init(source, paint, align, metrics, includePad, true, false /* useFallbackLineSpacing */);
return this;
}
/**
* Returns a BoringLayout for the specified text, potentially reusing
* this one if it is already suitable. The caller must make sure that
* no one is still using this Layout.
*
* The spacing multiplier and additional amount spacing are not used by BoringLayout.
* {@link Layout#getSpacingMultiplier()} will return 1.0 and {@link Layout#getSpacingAdd()} will
* return 0.0.
*
* @param source the text to render
* @param paint the default paint for the layout
* @param outerWidth the wrapping width for the text
* @param align whether to left, right, or center the text
* @param metrics {@code #Metrics} instance that contains information about FontMetrics and
* line width
* @param includePad set whether to include extra space beyond font ascent and descent which is
* needed to avoid clipping in some scripts
* @param ellipsize whether to ellipsize the text if width of the text is longer than the
* requested width
* @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
* {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
* not used, {@code outerwidth} is used instead
*/
public @NonNull BoringLayout replaceOrMake(@NonNull CharSequence source,
@NonNull TextPaint paint, @IntRange(from = 0) int outerWidth,
@NonNull Alignment align, @NonNull BoringLayout.Metrics metrics, boolean includePad,
@NonNull TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth,
boolean useFallbackLineSpacing) {
boolean trust;
if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
replaceWith(source, paint, outerWidth, align, 1f, 0f);
mEllipsizedWidth = outerWidth;
mEllipsizedStart = 0;
mEllipsizedCount = 0;
trust = true;
} else {
replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this),
paint, outerWidth, align, 1f, 0f);
mEllipsizedWidth = ellipsizedWidth;
trust = false;
}
init(getText(), paint, align, metrics, includePad, trust,
useFallbackLineSpacing);
return this;
}
@@ -137,25 +221,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
public BoringLayout replaceOrMake(CharSequence source, TextPaint paint, int outerWidth,
Alignment align, float spacingMult, float spacingAdd, BoringLayout.Metrics metrics,
boolean includePad, TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
boolean trust;
if (ellipsize == null || ellipsize == TextUtils.TruncateAt.MARQUEE) {
replaceWith(source, paint, outerWidth, align, spacingMult, spacingAdd);
mEllipsizedWidth = outerWidth;
mEllipsizedStart = 0;
mEllipsizedCount = 0;
trust = true;
} else {
replaceWith(TextUtils.ellipsize(source, paint, ellipsizedWidth, ellipsize, true, this),
paint, outerWidth, align, spacingMult, spacingAdd);
mEllipsizedWidth = ellipsizedWidth;
trust = false;
}
init(getText(), paint, align, metrics, includePad, trust);
return this;
return replaceOrMake(source, paint, outerWidth, align, metrics,
includePad, ellipsize, ellipsizedWidth, false /* useFallbackLineSpacing */);
}
/**
@@ -178,7 +245,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
mEllipsizedStart = 0;
mEllipsizedCount = 0;
init(source, paint, align, metrics, includePad, true);
init(source, paint, align, metrics, includePad, true, false /* useFallbackLineSpacing */);
}
/**
@@ -202,6 +269,34 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
public BoringLayout(CharSequence source, TextPaint paint, int outerWidth, Alignment align,
float spacingMult, float spacingAdd, BoringLayout.Metrics metrics, boolean includePad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
this(source, paint, outerWidth, align, spacingMult, spacingAdd, metrics, includePad,
ellipsize, ellipsizedWidth, false /* fallbackLineSpacing */);
}
/**
*
* @param source the text to render
* @param paint the default paint for the layout
* @param outerWidth the wrapping width for the text
* @param align whether to left, right, or center the text
* @param spacingMult this value is no longer used by BoringLayout
* @param spacingAdd this value is no longer used by BoringLayout
* @param metrics {@code #Metrics} instance that contains information about FontMetrics and
* line width
* @param includePad set whether to include extra space beyond font ascent and descent which is
* needed to avoid clipping in some scripts
* @param ellipsize whether to ellipsize the text if width of the text is longer than the
* requested {@code outerwidth}
* @param ellipsizedWidth the width to which this Layout is ellipsizing. If {@code ellipsize} is
* {@code null}, or is {@link TextUtils.TruncateAt#MARQUEE} this value is
* not used, {@code outerwidth} is used instead
*/
public BoringLayout(
@NonNull CharSequence source, @NonNull TextPaint paint,
@IntRange(from = 0) int outerWidth, @NonNull Alignment align, float spacingMult,
float spacingAdd, @NonNull BoringLayout.Metrics metrics, boolean includePad,
@NonNull TextUtils.TruncateAt ellipsize, @IntRange(from = 0) int ellipsizedWidth,
boolean useFallbackLineSpacing) {
/*
* It is silly to have to call super() and then replaceWith(),
* but we can't use "this" for the callback until the call to
@@ -224,11 +319,12 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
trust = false;
}
init(getText(), paint, align, metrics, includePad, trust);
init(getText(), paint, align, metrics, includePad, trust, useFallbackLineSpacing);
}
/* package */ void init(CharSequence source, TextPaint paint, Alignment align,
BoringLayout.Metrics metrics, boolean includePad, boolean trustWidth) {
BoringLayout.Metrics metrics, boolean includePad, boolean trustWidth,
boolean useFallbackLineSpacing) {
int spacing;
if (source instanceof String && align == Layout.Alignment.ALIGN_NORMAL) {
@@ -260,7 +356,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
TextLine line = TextLine.obtain();
line.set(paint, source, 0, source.length(), Layout.DIR_LEFT_TO_RIGHT,
Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
mEllipsizedStart, mEllipsizedStart + mEllipsizedCount);
mEllipsizedStart, mEllipsizedStart + mEllipsizedCount, useFallbackLineSpacing);
mMax = (int) Math.ceil(line.metrics(null));
TextLine.recycle(line);
}
@@ -336,6 +432,24 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
@UnsupportedAppUsage
public static Metrics isBoring(CharSequence text, TextPaint paint,
TextDirectionHeuristic textDir, Metrics metrics) {
return isBoring(text, paint, textDir, false /* useFallbackLineSpacing */, metrics);
}
/**
* Returns null if not boring; the width, ascent, and descent in the
* provided Metrics object (or a new one if the provided one was null)
* if boring.
*
* @param text a text to be calculated text layout.
* @param paint a paint object used for styling.
* @param textDir a text direction.
* @param useFallbackLineSpacing true if use fallback line spacing, otherwise false.
* @param metrics the out metrics.
* @return metrics on success. null if text cannot be rendered by BoringLayout.
*/
public static @Nullable Metrics isBoring(@NonNull CharSequence text, @NonNull TextPaint paint,
@NonNull TextDirectionHeuristic textDir, boolean useFallbackLineSpacing,
@Nullable Metrics metrics) {
final int textLength = text.length();
if (hasAnyInterestingChars(text, textLength)) {
return null; // There are some interesting characters. Not boring.
@@ -362,7 +476,8 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null,
0 /* ellipsisStart, 0 since text has not been ellipsized at this point */,
0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */);
0 /* ellipsisEnd, 0 since text has not been ellipsized at this point */,
useFallbackLineSpacing);
fm.width = (int) Math.ceil(line.metrics(fm));
TextLine.recycle(line);
@@ -450,6 +565,11 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
return mEllipsizedWidth;
}
@Override
public boolean isFallbackLineSpacingEnabled() {
return mUseFallbackLineSpacing;
}
// Override draw so it will be faster.
@Override
public void draw(Canvas c, Path highlight, Paint highlightpaint,
@@ -471,6 +591,7 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
private String mDirect;
private Paint mPaint;
private boolean mUseFallbackLineSpacing;
/* package */ int mBottom, mDesc; // for Direct
private int mTopPadding, mBottomPadding;

View File

@@ -591,7 +591,8 @@ public abstract class Layout {
} else {
tl.set(paint, buf, start, end, dir, directions, hasTab, tabStops,
getEllipsisStart(lineNum),
getEllipsisStart(lineNum) + getEllipsisCount(lineNum));
getEllipsisStart(lineNum) + getEllipsisCount(lineNum),
isFallbackLineSpacingEnabled());
if (justify) {
tl.justify(right - left - indentWidth);
}
@@ -959,6 +960,15 @@ public abstract class Layout {
return 0;
}
/**
* Return true if the fallback line space is enabled in this Layout.
*
* @return true if the fallback line space is enabled. Otherwise returns false.
*/
public boolean isFallbackLineSpacingEnabled() {
return false;
}
/**
* Returns true if the character at offset and the preceding character
* are at different run levels (and thus there's a split caret).
@@ -1231,7 +1241,8 @@ public abstract class Layout {
TextLine tl = TextLine.obtain();
tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops,
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
isFallbackLineSpacingEnabled());
float wid = tl.measure(offset - start, trailing, null);
TextLine.recycle(tl);
@@ -1271,7 +1282,8 @@ public abstract class Layout {
TextLine tl = TextLine.obtain();
tl.set(mPaint, mText, start, end, dir, directions, hasTab, tabStops,
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
isFallbackLineSpacingEnabled());
boolean[] trailings = primaryIsTrailingPreviousAllLineOffsets(line);
if (!primary) {
for (int offset = 0; offset < trailings.length; ++offset) {
@@ -1456,7 +1468,8 @@ public abstract class Layout {
paint.setStartHyphenEdit(getStartHyphenEdit(line));
paint.setEndHyphenEdit(getEndHyphenEdit(line));
tl.set(paint, mText, start, end, dir, directions, hasTabs, tabStops,
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
isFallbackLineSpacingEnabled());
if (isJustificationRequired(line)) {
tl.justify(getJustifyWidth(line));
}
@@ -1486,7 +1499,8 @@ public abstract class Layout {
paint.setStartHyphenEdit(getStartHyphenEdit(line));
paint.setEndHyphenEdit(getEndHyphenEdit(line));
tl.set(paint, mText, start, end, dir, directions, hasTabs, tabStops,
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
isFallbackLineSpacingEnabled());
if (isJustificationRequired(line)) {
tl.justify(getJustifyWidth(line));
}
@@ -1572,7 +1586,8 @@ public abstract class Layout {
// XXX: we don't care about tabs as we just use TextLine#getOffsetToLeftRightOf here.
tl.set(mPaint, mText, lineStartOffset, lineEndOffset, getParagraphDirection(line), dirs,
false, null,
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
isFallbackLineSpacingEnabled());
final HorizontalMeasurementProvider horizontal =
new HorizontalMeasurementProvider(line, primary);
@@ -1828,7 +1843,8 @@ public abstract class Layout {
TextLine tl = TextLine.obtain();
// XXX: we don't care about tabs
tl.set(mPaint, mText, lineStart, lineEnd, lineDir, directions, false, null,
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line));
getEllipsisStart(line), getEllipsisStart(line) + getEllipsisCount(line),
isFallbackLineSpacingEnabled());
caret = lineStart + tl.getOffsetToLeftRightOf(caret - lineStart, toLeft);
TextLine.recycle(tl);
return caret;
@@ -2202,7 +2218,8 @@ public abstract class Layout {
}
}
tl.set(paint, text, start, end, dir, directions, hasTabs, tabStops,
0 /* ellipsisStart */, 0 /* ellipsisEnd */);
0 /* ellipsisStart */, 0 /* ellipsisEnd */,
false /* use fallback line spacing. unused */);
return margin + Math.abs(tl.metrics(null));
} finally {
TextLine.recycle(tl);

View File

@@ -612,7 +612,6 @@ public class StaticLayout extends Layout {
TextPaint paint = b.mPaint;
int outerWidth = b.mWidth;
TextDirectionHeuristic textDir = b.mTextDir;
final boolean fallbackLineSpacing = b.mFallbackLineSpacing;
float spacingmult = b.mSpacingMult;
float spacingadd = b.mSpacingAdd;
float ellipsizedWidth = b.mEllipsizedWidth;
@@ -630,6 +629,7 @@ public class StaticLayout extends Layout {
mLineCount = 0;
mEllipsized = false;
mMaxLineHeight = mMaximumVisibleLineCount < 1 ? 0 : DEFAULT_MAX_LINE_HEIGHT;
mFallbackLineSpacing = b.mFallbackLineSpacing;
int v = 0;
boolean needMultiply = (spacingmult != 1 || spacingadd != 0);
@@ -867,17 +867,17 @@ public class StaticLayout extends Layout {
boolean moreChars = (endPos < bufEnd);
final int ascent = fallbackLineSpacing
final int ascent = mFallbackLineSpacing
? Math.min(fmAscent, Math.round(ascents[breakIndex]))
: fmAscent;
final int descent = fallbackLineSpacing
final int descent = mFallbackLineSpacing
? Math.max(fmDescent, Math.round(descents[breakIndex]))
: fmDescent;
// The fallback ascent/descent may be larger than top/bottom of the default font
// metrics. Adjust top/bottom with ascent/descent for avoiding unexpected
// clipping.
if (fallbackLineSpacing) {
if (mFallbackLineSpacing) {
if (ascent < fmTop) {
fmTop = ascent;
}
@@ -1381,6 +1381,11 @@ public class StaticLayout extends Layout {
return mEllipsizedWidth;
}
@Override
public boolean isFallbackLineSpacingEnabled() {
return mFallbackLineSpacing;
}
/**
* Return the total height of this layout.
*
@@ -1407,6 +1412,7 @@ public class StaticLayout extends Layout {
@UnsupportedAppUsage
private int mColumns;
private int mEllipsizedWidth;
private boolean mFallbackLineSpacing;
/**
* Keeps track if ellipsize is applied to the text.

View File

@@ -71,6 +71,8 @@ public class TextLine {
private Spanned mSpanned;
private PrecomputedText mComputed;
private boolean mUseFallbackExtent = false;
// The start and end of a potentially existing ellipsis on this text line.
// We use them to filter out replacement and metric affecting spans on ellipsized away chars.
private int mEllipsisStart;
@@ -141,6 +143,7 @@ public class TextLine {
tl.mTabs = null;
tl.mChars = null;
tl.mComputed = null;
tl.mUseFallbackExtent = false;
tl.mMetricAffectingSpanSpanSet.recycle();
tl.mCharacterStyleSpanSet.recycle();
@@ -171,17 +174,20 @@ public class TextLine {
* @param ellipsisStart the start of the ellipsis relative to the line
* @param ellipsisEnd the end of the ellipsis relative to the line. When there
* is no ellipsis, this should be equal to ellipsisStart.
* @param useFallbackLineSpacing true for enabling fallback line spacing. false for disabling
* fallback line spacing.
*/
@VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
public void set(TextPaint paint, CharSequence text, int start, int limit, int dir,
Directions directions, boolean hasTabs, TabStops tabStops,
int ellipsisStart, int ellipsisEnd) {
int ellipsisStart, int ellipsisEnd, boolean useFallbackLineSpacing) {
mPaint = paint;
mText = text;
mStart = start;
mLen = limit - start;
mDir = dir;
mDirections = directions;
mUseFallbackExtent = useFallbackLineSpacing;
if (mDirections == null) {
throw new IllegalArgumentException("Directions cannot be null");
}
@@ -845,6 +851,31 @@ public class TextLine {
previousLeading);
}
private void expandMetricsFromPaint(TextPaint wp, int start, int end,
int contextStart, int contextEnd, boolean runIsRtl, FontMetricsInt fmi) {
final int previousTop = fmi.top;
final int previousAscent = fmi.ascent;
final int previousDescent = fmi.descent;
final int previousBottom = fmi.bottom;
final int previousLeading = fmi.leading;
if (mCharsValid) {
int count = end - start;
int contextCount = contextEnd - contextStart;
wp.getFontMetricsInt(mChars, start, count, contextStart, contextCount, runIsRtl,
fmi);
} else {
int delta = mStart;
wp.getFontMetricsInt(mText, delta + start, delta + end,
delta + contextStart, delta + contextEnd, runIsRtl, fmi);
}
updateMetrics(fmi, previousTop, previousAscent, previousDescent, previousBottom,
previousLeading);
}
static void updateMetrics(FontMetricsInt fmi, int previousTop, int previousAscent,
int previousDescent, int previousBottom, int previousLeading) {
fmi.top = Math.min(fmi.top, previousTop);
@@ -949,6 +980,10 @@ public class TextLine {
shapeTextRun(consumer, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
}
if (mUseFallbackExtent && fmi != null) {
expandMetricsFromPaint(wp, start, end, contextStart, contextEnd, runIsRtl, fmi);
}
if (c != null) {
if (wp.bgColor != 0) {
int previousColor = wp.getColor();

View File

@@ -222,7 +222,8 @@ public class TextShaper {
mp.getDirections(0, count),
false /* tabstop is not supported */,
null,
-1, -1 // ellipsis is not supported.
-1, -1, // ellipsis is not supported.
false /* fallback line spacing is not used */
);
tl.shape(consumer);
} finally {

View File

@@ -1073,7 +1073,7 @@ public class Editor {
com.android.internal.R.dimen.textview_error_popup_default_width);
final StaticLayout l = StaticLayout.Builder.obtain(text, 0, text.length(), tv.getPaint(),
defaultWidthInPixels)
.setUseLineSpacingFromFallbacks(tv.mUseFallbackLineSpacing)
.setUseLineSpacingFromFallbacks(tv.isFallbackLineSpacingForStaticLayout())
.build();
float max = 0;

View File

@@ -48,6 +48,9 @@ import android.annotation.XmlRes;
import android.app.Activity;
import android.app.PendingIntent;
import android.app.assist.AssistStructure;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.ClipData;
import android.content.ClipDescription;
@@ -453,6 +456,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private static final int FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY = 500;
/**
* This change ID enables the fallback text line spacing (line height) for BoringLayout.
* @hide
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU)
public static final long BORINGLAYOUT_FALLBACK_LINESPACING = 210923482L; // buganizer id
/**
* This change ID enables the fallback text line spacing (line height) for StaticLayout.
* @hide
*/
@ChangeId
@EnabledSince(targetSdkVersion = Build.VERSION_CODES.P)
public static final long STATICLAYOUT_FALLBACK_LINESPACING = 37756858; // buganizer id
// System wide time for last cut, copy or text changed action.
static long sLastCutCopyOrTextChangedTime;
@@ -766,8 +785,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private boolean mListenerChanged = false;
// True if internationalized input should be used for numbers and date and time.
private final boolean mUseInternationalizedInput;
// True if fallback fonts that end up getting used should be allowed to affect line spacing.
/* package */ boolean mUseFallbackLineSpacing;
// Fallback fonts that end up getting used should be allowed to affect line spacing.
private static final int FALLBACK_LINE_SPACING_NONE = 0;
private static final int FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY = 1;
private static final int FALLBACK_LINE_SPACING_ALL = 2;
private int mUseFallbackLineSpacing;
// True if the view text can be padded for compat reasons, when the view is translated.
private final boolean mUseTextPaddingForUiTranslation;
@@ -1479,7 +1503,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mUseInternationalizedInput = targetSdkVersion >= VERSION_CODES.O;
mUseFallbackLineSpacing = targetSdkVersion >= VERSION_CODES.P;
if (CompatChanges.isChangeEnabled(BORINGLAYOUT_FALLBACK_LINESPACING)) {
mUseFallbackLineSpacing = FALLBACK_LINE_SPACING_ALL;
} else if (CompatChanges.isChangeEnabled(STATICLAYOUT_FALLBACK_LINESPACING)) {
mUseFallbackLineSpacing = FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY;
} else {
mUseFallbackLineSpacing = FALLBACK_LINE_SPACING_NONE;
}
// TODO(b/179693024): Use a ChangeId instead.
mUseTextPaddingForUiTranslation = targetSdkVersion <= Build.VERSION_CODES.R;
@@ -4541,8 +4571,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
* @attr ref android.R.styleable#TextView_fallbackLineSpacing
*/
public void setFallbackLineSpacing(boolean enabled) {
if (mUseFallbackLineSpacing != enabled) {
mUseFallbackLineSpacing = enabled;
int fallbackStrategy;
if (enabled) {
if (CompatChanges.isChangeEnabled(BORINGLAYOUT_FALLBACK_LINESPACING)) {
fallbackStrategy = FALLBACK_LINE_SPACING_ALL;
} else {
fallbackStrategy = FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY;
}
} else {
fallbackStrategy = FALLBACK_LINE_SPACING_NONE;
}
if (mUseFallbackLineSpacing != fallbackStrategy) {
mUseFallbackLineSpacing = fallbackStrategy;
if (mLayout != null) {
nullLayouts();
requestLayout();
@@ -4560,7 +4600,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
*/
@InspectableProperty
public boolean isFallbackLineSpacing() {
return mUseFallbackLineSpacing;
return mUseFallbackLineSpacing != FALLBACK_LINE_SPACING_NONE;
}
private boolean isFallbackLineSpacingForBoringLayout() {
return mUseFallbackLineSpacing == FALLBACK_LINE_SPACING_ALL;
}
// Package privte for accessing from Editor.java
/* package */ boolean isFallbackLineSpacingForStaticLayout() {
return mUseFallbackLineSpacing == FALLBACK_LINE_SPACING_ALL
|| mUseFallbackLineSpacing == FALLBACK_LINE_SPACING_STATIC_LAYOUT_ONLY;
}
/**
@@ -9148,7 +9198,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
if (hintBoring == UNKNOWN_BORING) {
hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
mHintBoring);
isFallbackLineSpacingForBoringLayout(), mHintBoring);
if (hintBoring != null) {
mHintBoring = hintBoring;
}
@@ -9190,7 +9240,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
.setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency)
.setJustificationMode(mJustificationMode)
@@ -9250,7 +9300,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
.setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency)
.setJustificationMode(mJustificationMode)
@@ -9259,7 +9309,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
result = builder.build();
} else {
if (boring == UNKNOWN_BORING) {
boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
isFallbackLineSpacingForBoringLayout(), mBoring);
if (boring != null) {
mBoring = boring;
}
@@ -9303,7 +9354,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setTextDirection(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
.setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency)
.setJustificationMode(mJustificationMode)
@@ -9430,7 +9481,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (des < 0) {
boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir, mBoring);
boring = BoringLayout.isBoring(mTransformed, mTextPaint, mTextDir,
isFallbackLineSpacingForBoringLayout(), mBoring);
if (boring != null) {
mBoring = boring;
}
@@ -9463,7 +9515,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
}
if (hintDes < 0) {
hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir, mHintBoring);
hintBoring = BoringLayout.isBoring(mHint, mTextPaint, mTextDir,
isFallbackLineSpacingForBoringLayout(), mHintBoring);
if (hintBoring != null) {
mHintBoring = hintBoring;
}
@@ -9667,7 +9720,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
layoutBuilder.setAlignment(getLayoutAlignment())
.setLineSpacing(getLineSpacingExtra(), getLineSpacingMultiplier())
.setIncludePad(getIncludeFontPadding())
.setUseLineSpacingFromFallbacks(mUseFallbackLineSpacing)
.setUseLineSpacingFromFallbacks(isFallbackLineSpacingForStaticLayout())
.setBreakStrategy(getBreakStrategy())
.setHyphenationFrequency(getHyphenationFrequency())
.setJustificationMode(getJustificationMode())

View File

@@ -48,7 +48,7 @@ public class TextLineTest {
final TextLine tl = TextLine.obtain();
tl.set(paint, line, 0, line.length(), Layout.DIR_LEFT_TO_RIGHT,
Layout.DIRS_ALL_LEFT_TO_RIGHT, false /* hasTabs */, null /* tabStops */,
0, 0 /* no ellipsis */);
0, 0 /* no ellipsis */, false /* useFallbackLinespace */);
final float originalWidth = tl.metrics(null);
final float expandedWidth = 2 * originalWidth;
@@ -105,7 +105,7 @@ public class TextLineTest {
tl.set(paint, str, 0, str.length(),
TextDirectionHeuristics.FIRSTSTRONG_LTR.isRtl(str, 0, str.length()) ? -1 : 1,
layout.getLineDirections(0), tabStops != null, tabStops,
0, 0 /* no ellipsis */);
0, 0 /* no ellipsis */, false /* useFallbackLineSpacing */);
return tl;
}
@@ -276,7 +276,8 @@ public class TextLineTest {
final TextLine tl = TextLine.obtain();
tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
false /* hasTabs */, null /* tabStops */, 9, 12);
false /* hasTabs */, null /* tabStops */, 9, 12,
false /* useFallbackLineSpacing */);
tl.measure(text.length(), false /* trailing */, null /* fmi */);
assertFalse(span.mIsUsed);
@@ -292,7 +293,8 @@ public class TextLineTest {
final TextLine tl = TextLine.obtain();
tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
false /* hasTabs */, null /* tabStops */, 9, 12);
false /* hasTabs */, null /* tabStops */, 9, 12,
false /* useFallbackLineSpacing */);
tl.measure(text.length(), false /* trailing */, null /* fmi */);
assertTrue(span.mIsUsed);
@@ -308,7 +310,8 @@ public class TextLineTest {
final TextLine tl = TextLine.obtain();
tl.set(new TextPaint(), text, 0, text.length(), 1, Layout.DIRS_ALL_LEFT_TO_RIGHT,
false /* hasTabs */, null /* tabStops */, 9, 12);
false /* hasTabs */, null /* tabStops */, 9, 12,
false /* useFallbackLineSpacing */);
tl.measure(text.length(), false /* trailing */, null /* fmi */);
assertTrue(span.mIsUsed);
}

View File

@@ -46,6 +46,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Objects;
/**
* The Paint class holds the style and color information about how to draw
@@ -2130,6 +2131,116 @@ public class Paint {
return fm;
}
/**
* Returns the font metrics value for the given text.
*
* If the text is rendered with multiple font files, this function returns the large ascent and
* descent that are enough for drawing all font files.
*
* The context range is used for shaping context. Some script, e.g. Arabic or Devanagari,
* changes letter shape based on its location or surrounding characters.
*
* @param text a text to be measured.
* @param start a starting offset in the text.
* @param count a length of the text to be measured.
* @param contextStart a context starting offset in the text.
* @param contextCount a length of the context to be used.
* @param isRtl true if measuring on RTL context, otherwise false.
* @param outMetrics the output font metrics.
*/
public void getFontMetricsInt(
@NonNull CharSequence text,
@IntRange(from = 0) int start, @IntRange(from = 0) int count,
@IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextCount,
boolean isRtl,
@NonNull FontMetricsInt outMetrics) {
if (text == null) {
throw new IllegalArgumentException("text must not be null");
}
if (start < 0 || start >= text.length()) {
throw new IllegalArgumentException("start argument is out of bounds.");
}
if (count < 0 || start + count > text.length()) {
throw new IllegalArgumentException("count argument is out of bounds.");
}
if (contextStart < 0 || contextStart >= text.length()) {
throw new IllegalArgumentException("ctxStart argument is out of bounds.");
}
if (contextCount < 0 || contextStart + contextCount > text.length()) {
throw new IllegalArgumentException("ctxCount argument is out of bounds.");
}
if (outMetrics == null) {
throw new IllegalArgumentException("outMetrics must not be null.");
}
if (count == 0) {
getFontMetricsInt(outMetrics);
return;
}
if (text instanceof String) {
nGetFontMetricsIntForText(mNativePaint, (String) text, start, count, contextStart,
contextCount, isRtl, outMetrics);
} else {
char[] buf = TemporaryBuffer.obtain(contextCount);
TextUtils.getChars(text, contextStart, contextStart + contextCount, buf, 0);
nGetFontMetricsIntForText(mNativePaint, buf, start - contextStart, count, 0,
contextCount, isRtl, outMetrics);
}
}
/**
* Returns the font metrics value for the given text.
*
* If the text is rendered with multiple font files, this function returns the large ascent and
* descent that are enough for drawing all font files.
*
* The context range is used for shaping context. Some script, e.g. Arabic or Devanagari,
* changes letter shape based on its location or surrounding characters.
*
* @param text a text to be measured.
* @param start a starting offset in the text.
* @param count a length of the text to be measured.
* @param contextStart a context starting offset in the text.
* @param contextCount a length of the context to be used.
* @param isRtl true if measuring on RTL context, otherwise false.
* @param outMetrics the output font metrics.
*/
public void getFontMetricsInt(@NonNull char[] text,
@IntRange(from = 0) int start, @IntRange(from = 0) int count,
@IntRange(from = 0) int contextStart, @IntRange(from = 0) int contextCount,
boolean isRtl,
@NonNull FontMetricsInt outMetrics) {
if (text == null) {
throw new IllegalArgumentException("text must not be null");
}
if (start < 0 || start >= text.length) {
throw new IllegalArgumentException("start argument is out of bounds.");
}
if (count < 0 || start + count > text.length) {
throw new IllegalArgumentException("count argument is out of bounds.");
}
if (contextStart < 0 || contextStart >= text.length) {
throw new IllegalArgumentException("ctxStart argument is out of bounds.");
}
if (contextCount < 0 || contextStart + contextCount > text.length) {
throw new IllegalArgumentException("ctxCount argument is out of bounds.");
}
if (outMetrics == null) {
throw new IllegalArgumentException("outMetrics must not be null.");
}
if (count == 0) {
getFontMetricsInt(outMetrics);
return;
}
nGetFontMetricsIntForText(mNativePaint, text, start, count, contextStart, contextCount,
isRtl, outMetrics);
}
/**
* Convenience method for callers that want to have FontMetrics values as
* integers.
@@ -2163,6 +2274,23 @@ public class Paint {
" descent=" + descent + " bottom=" + bottom +
" leading=" + leading;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof FontMetricsInt)) return false;
FontMetricsInt that = (FontMetricsInt) o;
return top == that.top
&& ascent == that.ascent
&& descent == that.descent
&& bottom == that.bottom
&& leading == that.leading;
}
@Override
public int hashCode() {
return Objects.hash(top, ascent, descent, bottom, leading);
}
}
/**
@@ -3117,6 +3245,13 @@ public class Paint {
int contextStart, int contextEnd, boolean isRtl, int offset);
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,
int start, int count, int ctxStart, int ctxCount, boolean isRtl,
FontMetricsInt outMetrics);
private static native void nGetFontMetricsIntForText(long paintPtr, String text,
int start, int count, int ctxStart, int ctxCount, boolean isRtl,
FontMetricsInt outMetrics);
// ---------------- @FastNative ------------------------
@@ -3130,7 +3265,6 @@ public class Paint {
@FastNative
private static native int nGetFontMetricsInt(long paintPtr, FontMetricsInt fmi);
// ---------------- @CriticalNative ------------------------
@CriticalNative

View File

@@ -95,6 +95,16 @@ float MinikinUtils::measureText(const Paint* paint, minikin::Bidi bidiFlags,
endHyphen, advances);
}
minikin::MinikinExtent MinikinUtils::getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf,
size_t start, size_t count, size_t bufSize) {
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
const minikin::U16StringPiece textBuf(buf, bufSize);
const minikin::Range range(start, start + count);
return minikin::getFontExtent(textBuf, range, bidiFlags, minikinPaint);
}
bool MinikinUtils::hasVariationSelector(const Typeface* typeface, uint32_t codepoint, uint32_t vs) {
const Typeface* resolvedFace = Typeface::resolveDefault(typeface);
return resolvedFace->fFontCollection->hasVariationSelector(codepoint, vs);

View File

@@ -56,6 +56,10 @@ public:
size_t start, size_t count, size_t bufSize,
float* advances);
static minikin::MinikinExtent getFontExtent(const Paint* paint, minikin::Bidi bidiFlags,
const Typeface* typeface, const uint16_t* buf,
size_t start, size_t count, size_t bufSize);
static bool hasVariationSelector(const Typeface* typeface, uint32_t codepoint,
uint32_t vs);

View File

@@ -541,26 +541,6 @@ namespace PaintGlue {
return result;
}
// ------------------ @FastNative ---------------------------
static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
ScopedUtfChars localesChars(env, locales);
jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
obj->setMinikinLocaleListId(minikinLocaleListId);
return minikinLocaleListId;
}
static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
if (!settings) {
paint->setFontFeatureSettings(std::string());
} else {
ScopedUtfChars settingsChars(env, settings);
paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
}
}
static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) {
const int kElegantTop = 2500;
const int kElegantBottom = -1000;
@@ -593,6 +573,67 @@ namespace PaintGlue {
return spacing;
}
static void doFontExtent(JNIEnv* env, jlong paintHandle, const jchar buf[], jint start,
jint count, jint bufSize, jboolean isRtl, jobject fmi) {
const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
const Typeface* typeface = paint->getAndroidTypeface();
minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
minikin::MinikinExtent extent =
MinikinUtils::getFontExtent(paint, bidiFlags, typeface, buf, start, count, bufSize);
SkFontMetrics metrics;
getMetricsInternal(paintHandle, &metrics);
metrics.fAscent = extent.ascent;
metrics.fDescent = extent.descent;
// If top/bottom is narrower than ascent/descent, adjust top/bottom to ascent/descent.
metrics.fTop = std::min(metrics.fAscent, metrics.fTop);
metrics.fBottom = std::max(metrics.fDescent, metrics.fBottom);
GraphicsJNI::set_metrics_int(env, fmi, metrics);
}
static void getFontMetricsIntForText___C(JNIEnv* env, jclass, jlong paintHandle,
jcharArray text, jint start, jint count, jint ctxStart,
jint ctxCount, jboolean isRtl, jobject fmi) {
ScopedCharArrayRO textArray(env, text);
doFontExtent(env, paintHandle, textArray.get() + ctxStart, start - ctxStart, count,
ctxCount, isRtl, fmi);
}
static void getFontMetricsIntForText___String(JNIEnv* env, jclass, jlong paintHandle,
jstring text, jint start, jint count,
jint ctxStart, jint ctxCount, jboolean isRtl,
jobject fmi) {
ScopedStringChars textChars(env, text);
doFontExtent(env, paintHandle, textChars.get() + ctxStart, start - ctxStart, count,
ctxCount, isRtl, fmi);
}
// ------------------ @FastNative ---------------------------
static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
Paint* obj = reinterpret_cast<Paint*>(objHandle);
ScopedUtfChars localesChars(env, locales);
jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
obj->setMinikinLocaleListId(minikinLocaleListId);
return minikinLocaleListId;
}
static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle,
jstring settings) {
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
if (!settings) {
paint->setFontFeatureSettings(std::string());
} else {
ScopedUtfChars settingsChars(env, settings);
paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
}
}
static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
SkFontMetrics metrics;
SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
@@ -1015,6 +1056,11 @@ static const JNINativeMethod methods[] = {
{"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},
// --------------- @FastNative ----------------------
@@ -1093,6 +1139,7 @@ static const JNINativeMethod methods[] = {
{"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
};
int register_android_graphics_Paint(JNIEnv* env) {
return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
}