Add TextShaper API
TextShaper API provides a primitive text layout result, e.g. what glyph is used etc. Here is the new APIs and its description - PositionedGlyphs This is a native instance backed object that gives layout information to developers. - GlyphStyle The glyph style is an object that holds the parameters that will be used for drawing. This object is a subset of the Paint object for avoiding creating lots of Paint copy. - TextShaper#shapeText This does a text shaping and gives TextShaper.Result. This is a native backed primitive shaping API. - StyledTextShaper This is a text shaper for a styled text. This will be the top-level developer facing API for shaping text. Bug: 168048923 Test: atest FontTest TextShaperTest StyledTextShaperTest Change-Id: I2e91d1ef8503b25b28efc94da7de7cad49c4c1a9
This commit is contained in:
@@ -16508,6 +16508,23 @@ package android.graphics.pdf {
|
||||
|
||||
package android.graphics.text {
|
||||
|
||||
public class GlyphStyle {
|
||||
ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
|
||||
ctor public GlyphStyle(@NonNull android.graphics.Paint);
|
||||
method public void applyToPaint(@NonNull android.graphics.Paint);
|
||||
method @ColorInt public int getColor();
|
||||
method public int getFlags();
|
||||
method @FloatRange(from=0) public float getFontSize();
|
||||
method @FloatRange(from=0) public float getScaleX();
|
||||
method @FloatRange(from=0) public float getSkewX();
|
||||
method public void setColor(@ColorInt int);
|
||||
method public void setFlags(int);
|
||||
method public void setFontSize(@FloatRange(from=0) float);
|
||||
method public void setFromPaint(@NonNull android.graphics.Paint);
|
||||
method public void setScaleX(@FloatRange(from=0) float);
|
||||
method public void setSkewX(@FloatRange(from=0) float);
|
||||
}
|
||||
|
||||
public class LineBreaker {
|
||||
method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
|
||||
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
|
||||
@@ -16568,6 +16585,25 @@ package android.graphics.text {
|
||||
method @NonNull public android.graphics.text.MeasuredText.Builder setComputeLayout(boolean);
|
||||
}
|
||||
|
||||
public final class PositionedGlyphs {
|
||||
method public float getAscent();
|
||||
method public float getDescent();
|
||||
method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
|
||||
method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
|
||||
method public float getOriginX();
|
||||
method public float getOriginY();
|
||||
method public float getPositionX(@IntRange(from=0) int);
|
||||
method public float getPositionY(@IntRange(from=0) int);
|
||||
method @NonNull public android.graphics.text.GlyphStyle getStyle();
|
||||
method public float getTotalAdvance();
|
||||
method @IntRange(from=0) public int glyphCount();
|
||||
}
|
||||
|
||||
public class TextShaper {
|
||||
method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
|
||||
method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.hardware {
|
||||
@@ -49990,6 +50026,10 @@ package android.text {
|
||||
method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
|
||||
}
|
||||
|
||||
public class StyledTextShaper {
|
||||
method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
|
||||
}
|
||||
|
||||
public interface TextDirectionHeuristic {
|
||||
method public boolean isRtl(char[], int, int);
|
||||
method public boolean isRtl(CharSequence, int, int);
|
||||
|
||||
67
core/java/android/text/StyledTextShaper.java
Normal file
67
core/java/android/text/StyledTextShaper.java
Normal file
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.text;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.text.PositionedGlyphs;
|
||||
import android.graphics.text.TextShaper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Provides text shaping for multi-styled text.
|
||||
*
|
||||
* @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
|
||||
* @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
|
||||
* @see StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic, TextPaint)
|
||||
*/
|
||||
public class StyledTextShaper {
|
||||
private StyledTextShaper() {}
|
||||
|
||||
|
||||
/**
|
||||
* Shape multi-styled text.
|
||||
*
|
||||
* @param text a styled text.
|
||||
* @param start a start index of shaping target in the text.
|
||||
* @param count a length of shaping target in the text.
|
||||
* @param dir a text direction.
|
||||
* @param paint a paint
|
||||
* @return a shape result.
|
||||
*/
|
||||
public static @NonNull List<PositionedGlyphs> shapeText(
|
||||
@NonNull CharSequence text, int start, int count,
|
||||
@NonNull TextDirectionHeuristic dir, @NonNull TextPaint paint) {
|
||||
MeasuredParagraph mp = MeasuredParagraph.buildForBidi(
|
||||
text, start, start + count, dir, null);
|
||||
TextLine tl = TextLine.obtain();
|
||||
try {
|
||||
tl.set(paint, text, start, start + count,
|
||||
mp.getParagraphDir(),
|
||||
mp.getDirections(start, start + count),
|
||||
false /* tabstop is not supported */,
|
||||
null,
|
||||
-1, -1 // ellipsis is not supported.
|
||||
);
|
||||
return tl.shape();
|
||||
} finally {
|
||||
TextLine.recycle(tl);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Paint.FontMetricsInt;
|
||||
import android.graphics.text.PositionedGlyphs;
|
||||
import android.graphics.text.TextShaper;
|
||||
import android.os.Build;
|
||||
import android.text.Layout.Directions;
|
||||
import android.text.Layout.TabStops;
|
||||
@@ -35,6 +37,7 @@ import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.ArrayUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Represents a line of styled text, for measuring in visual order and
|
||||
@@ -306,6 +309,36 @@ public class TextLine {
|
||||
return measure(mLen, false, fmi);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shape the TextLine.
|
||||
*/
|
||||
List<PositionedGlyphs> shape() {
|
||||
List<PositionedGlyphs> glyphs = new ArrayList<>();
|
||||
float horizontal = 0;
|
||||
float x = 0;
|
||||
final int runCount = mDirections.getRunCount();
|
||||
for (int runIndex = 0; runIndex < runCount; 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) {
|
||||
horizontal += shapeRun(glyphs, segStart, j, runIsRtl, x + horizontal,
|
||||
runIndex != (runCount - 1) || j != mLen);
|
||||
|
||||
if (j != runLimit) { // charAt(j) == TAB_CHAR
|
||||
horizontal = mDir * nextTab(horizontal * mDir);
|
||||
}
|
||||
segStart = j + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return glyphs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the signed graphical offset from the leading margin.
|
||||
*
|
||||
@@ -483,12 +516,12 @@ public class TextLine {
|
||||
|
||||
if ((mDir == Layout.DIR_LEFT_TO_RIGHT) == runIsRtl) {
|
||||
float w = -measureRun(start, limit, limit, runIsRtl, null);
|
||||
handleRun(start, limit, limit, runIsRtl, c, x + w, top,
|
||||
handleRun(start, limit, limit, runIsRtl, c, null, x + w, top,
|
||||
y, bottom, null, false);
|
||||
return w;
|
||||
}
|
||||
|
||||
return handleRun(start, limit, limit, runIsRtl, c, x, top,
|
||||
return handleRun(start, limit, limit, runIsRtl, c, null, x, top,
|
||||
y, bottom, null, needWidth);
|
||||
}
|
||||
|
||||
@@ -507,9 +540,34 @@ public class TextLine {
|
||||
*/
|
||||
private float measureRun(int start, int offset, int limit, boolean runIsRtl,
|
||||
FontMetricsInt fmi) {
|
||||
return handleRun(start, offset, limit, runIsRtl, null, 0, 0, 0, 0, fmi, true);
|
||||
return handleRun(start, offset, limit, runIsRtl, null, null, 0, 0, 0, 0, fmi, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shape a unidirectional (but possibly multi-styled) run of text.
|
||||
*
|
||||
* @param glyphs the output positioned glyphs list
|
||||
* @param start the line-relative start
|
||||
* @param limit the line-relative limit
|
||||
* @param runIsRtl true if the run is right-to-left
|
||||
* @param x the position of the run that is closest to the leading margin
|
||||
* @param needWidth true if the width value is required.
|
||||
* @return the signed width of the run, based on the paragraph direction.
|
||||
* Only valid if needWidth is true.
|
||||
*/
|
||||
private float shapeRun(List<PositionedGlyphs> glyphs, int start,
|
||||
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, glyphs, x + w, 0, 0, 0, null, false);
|
||||
return w;
|
||||
}
|
||||
|
||||
return handleRun(start, limit, limit, runIsRtl, null, glyphs, x, 0, 0, 0, null, needWidth);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Walk the cursor through this line, skipping conjuncts and
|
||||
* zero-width characters.
|
||||
@@ -841,6 +899,7 @@ public class TextLine {
|
||||
* @param end the end of the text
|
||||
* @param runIsRtl true if the run is right-to-left
|
||||
* @param c the canvas, can be null if rendering is not needed
|
||||
* @param glyphs the output positioned glyph list, can be null if not necessary
|
||||
* @param x the edge of the run closest to the leading margin
|
||||
* @param top the top of the line
|
||||
* @param y the baseline
|
||||
@@ -854,7 +913,7 @@ public class TextLine {
|
||||
*/
|
||||
private float handleText(TextPaint wp, int start, int end,
|
||||
int contextStart, int contextEnd, boolean runIsRtl,
|
||||
Canvas c, float x, int top, int y, int bottom,
|
||||
Canvas c, List<PositionedGlyphs> glyphs, float x, int top, int y, int bottom,
|
||||
FontMetricsInt fmi, boolean needWidth, int offset,
|
||||
@Nullable ArrayList<DecorationInfo> decorations) {
|
||||
|
||||
@@ -878,16 +937,20 @@ public class TextLine {
|
||||
totalWidth = getRunAdvance(wp, start, end, contextStart, contextEnd, runIsRtl, offset);
|
||||
}
|
||||
|
||||
if (c != null) {
|
||||
final float leftX, rightX;
|
||||
if (runIsRtl) {
|
||||
leftX = x - totalWidth;
|
||||
rightX = x;
|
||||
} else {
|
||||
leftX = x;
|
||||
rightX = x + totalWidth;
|
||||
}
|
||||
final float leftX, rightX;
|
||||
if (runIsRtl) {
|
||||
leftX = x - totalWidth;
|
||||
rightX = x;
|
||||
} else {
|
||||
leftX = x;
|
||||
rightX = x + totalWidth;
|
||||
}
|
||||
|
||||
if (glyphs != null) {
|
||||
shapeTextRun(glyphs, wp, start, end, contextStart, contextEnd, runIsRtl, leftX);
|
||||
}
|
||||
|
||||
if (c != null) {
|
||||
if (wp.bgColor != 0) {
|
||||
int previousColor = wp.getColor();
|
||||
Paint.Style previousStyle = wp.getStyle();
|
||||
@@ -1072,6 +1135,7 @@ public class TextLine {
|
||||
* @param limit the limit of the run
|
||||
* @param runIsRtl true if the run is right-to-left
|
||||
* @param c the canvas, can be null
|
||||
* @param glyphs the output positioned glyphs, can be null
|
||||
* @param x the end of the run closest to the leading margin
|
||||
* @param top the top of the line
|
||||
* @param y the baseline
|
||||
@@ -1082,7 +1146,8 @@ public class TextLine {
|
||||
* valid if needWidth is true
|
||||
*/
|
||||
private float handleRun(int start, int measureLimit,
|
||||
int limit, boolean runIsRtl, Canvas c, float x, int top, int y,
|
||||
int limit, boolean runIsRtl, Canvas c,
|
||||
List<PositionedGlyphs> glyphs, float x, int top, int y,
|
||||
int bottom, FontMetricsInt fmi, boolean needWidth) {
|
||||
|
||||
if (measureLimit < start || measureLimit > limit) {
|
||||
@@ -1115,7 +1180,7 @@ public class TextLine {
|
||||
wp.set(mPaint);
|
||||
wp.setStartHyphenEdit(adjustStartHyphenEdit(start, wp.getStartHyphenEdit()));
|
||||
wp.setEndHyphenEdit(adjustEndHyphenEdit(limit, wp.getEndHyphenEdit()));
|
||||
return handleText(wp, start, limit, start, limit, runIsRtl, c, x, top,
|
||||
return handleText(wp, start, limit, start, limit, runIsRtl, c, glyphs, x, top,
|
||||
y, bottom, fmi, needWidth, measureLimit, null);
|
||||
}
|
||||
|
||||
@@ -1196,8 +1261,8 @@ public class TextLine {
|
||||
adjustStartHyphenEdit(activeStart, mPaint.getStartHyphenEdit()));
|
||||
activePaint.setEndHyphenEdit(
|
||||
adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
|
||||
x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, x,
|
||||
top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
|
||||
x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c,
|
||||
glyphs, x, top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
|
||||
Math.min(activeEnd, mlimit), mDecorations);
|
||||
|
||||
activeStart = j;
|
||||
@@ -1223,7 +1288,7 @@ public class TextLine {
|
||||
adjustStartHyphenEdit(activeStart, mPaint.getStartHyphenEdit()));
|
||||
activePaint.setEndHyphenEdit(
|
||||
adjustEndHyphenEdit(activeEnd, mPaint.getEndHyphenEdit()));
|
||||
x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, x,
|
||||
x += handleText(activePaint, activeStart, activeEnd, i, inext, runIsRtl, c, glyphs, x,
|
||||
top, y, bottom, fmi, needWidth || activeEnd < measureLimit,
|
||||
Math.min(activeEnd, mlimit), mDecorations);
|
||||
}
|
||||
@@ -1259,6 +1324,45 @@ public class TextLine {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Shape a text run with the set-up paint.
|
||||
*
|
||||
* @param glyphs the output positioned glyphs list
|
||||
* @param paint the paint used to render the text
|
||||
* @param start the start of the run
|
||||
* @param end the end of the run
|
||||
* @param contextStart the start of context for the run
|
||||
* @param contextEnd the end of the context for the run
|
||||
* @param runIsRtl true if the run is right-to-left
|
||||
* @param x the x position of the left edge of the run
|
||||
*/
|
||||
private void shapeTextRun(List<PositionedGlyphs> glyphs, TextPaint paint,
|
||||
int start, int end, int contextStart, int contextEnd, boolean runIsRtl, float x) {
|
||||
|
||||
int count = end - start;
|
||||
int contextCount = contextEnd - contextStart;
|
||||
if (mCharsValid) {
|
||||
glyphs.add(TextShaper.shapeTextRun(
|
||||
mChars,
|
||||
start, count,
|
||||
contextStart, contextCount,
|
||||
x, 0f,
|
||||
runIsRtl,
|
||||
paint
|
||||
));
|
||||
} else {
|
||||
glyphs.add(TextShaper.shapeTextRun(
|
||||
mText,
|
||||
mStart + start, count,
|
||||
mStart + contextStart, contextCount,
|
||||
x, 0f,
|
||||
runIsRtl,
|
||||
paint
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the next tab position.
|
||||
*
|
||||
|
||||
@@ -26,8 +26,11 @@ import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.os.LocaleList;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.util.Log;
|
||||
import android.util.LongSparseArray;
|
||||
import android.util.TypedValue;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import dalvik.annotation.optimization.CriticalNative;
|
||||
@@ -40,6 +43,7 @@ import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.FileChannel;
|
||||
@@ -56,6 +60,15 @@ public final class Font {
|
||||
private static final int STYLE_ITALIC = 1;
|
||||
private static final int STYLE_NORMAL = 0;
|
||||
|
||||
private static final Object MAP_LOCK = new Object();
|
||||
// We need to have mapping from native ptr to Font object for later accessing from TextShape
|
||||
// result since Typeface doesn't have reference to Font object and it is not always created from
|
||||
// Font object. Sometimes Typeface is created in native layer only and there might not be Font
|
||||
// object in Java layer. So, if not found in this cache, create new Font object for API user.
|
||||
@GuardedBy("MAP_LOCK")
|
||||
private static final LongSparseArray<WeakReference<Font>> FONT_PTR_MAP =
|
||||
new LongSparseArray<>();
|
||||
|
||||
/**
|
||||
* A builder class for creating new Font.
|
||||
*/
|
||||
@@ -501,6 +514,10 @@ public final class Font {
|
||||
mTtcIndex = ttcIndex;
|
||||
mAxes = axes;
|
||||
mLocaleList = localeList;
|
||||
|
||||
synchronized (MAP_LOCK) {
|
||||
FONT_PTR_MAP.append(mNativePtr, new WeakReference<>(this));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -637,6 +654,63 @@ public final class Font {
|
||||
+ "}";
|
||||
}
|
||||
|
||||
/**
|
||||
* Lookup Font object from native pointer or create new one if not found.
|
||||
* @hide
|
||||
*/
|
||||
public static Font findOrCreateFontFromNativePtr(long ptr) {
|
||||
// First, lookup from known mapps.
|
||||
synchronized (MAP_LOCK) {
|
||||
WeakReference<Font> fontRef = FONT_PTR_MAP.get(ptr);
|
||||
if (fontRef != null) {
|
||||
Font font = fontRef.get();
|
||||
if (font != null) {
|
||||
return font;
|
||||
}
|
||||
}
|
||||
|
||||
// If not found, create Font object from native object for Java API users.
|
||||
ByteBuffer buffer = NativeFontBufferHelper.refByteBuffer(ptr);
|
||||
long packed = nGetFontInfo(ptr);
|
||||
int weight = (int) (packed & 0x0000_0000_0000_FFFFL);
|
||||
boolean italic = (packed & 0x0000_0000_0001_0000L) != 0;
|
||||
int ttcIndex = (int) ((packed & 0x0000_FFFF_0000_0000L) >> 32);
|
||||
int axisCount = (int) ((packed & 0xFFFF_0000_0000_0000L) >> 48);
|
||||
FontVariationAxis[] axes = new FontVariationAxis[axisCount];
|
||||
char[] charBuffer = new char[4];
|
||||
for (int i = 0; i < axisCount; ++i) {
|
||||
long packedAxis = nGetAxisInfo(ptr, i);
|
||||
float value = Float.intBitsToFloat((int) (packedAxis & 0x0000_0000_FFFF_FFFFL));
|
||||
charBuffer[0] = (char) ((packedAxis & 0xFF00_0000_0000_0000L) >> 56);
|
||||
charBuffer[1] = (char) ((packedAxis & 0x00FF_0000_0000_0000L) >> 48);
|
||||
charBuffer[2] = (char) ((packedAxis & 0x0000_FF00_0000_0000L) >> 40);
|
||||
charBuffer[3] = (char) ((packedAxis & 0x0000_00FF_0000_0000L) >> 32);
|
||||
axes[i] = new FontVariationAxis(new String(charBuffer), value);
|
||||
}
|
||||
Font.Builder builder = new Font.Builder(buffer)
|
||||
.setWeight(weight)
|
||||
.setSlant(italic ? FontStyle.FONT_SLANT_ITALIC : FontStyle.FONT_SLANT_UPRIGHT)
|
||||
.setTtcIndex(ttcIndex)
|
||||
.setFontVariationSettings(axes);
|
||||
|
||||
Font newFont = null;
|
||||
try {
|
||||
newFont = builder.build();
|
||||
FONT_PTR_MAP.append(ptr, new WeakReference<>(newFont));
|
||||
} catch (IOException e) {
|
||||
// This must not happen since the buffer was already created once.
|
||||
Log.e("Font", "Failed to create font object from existing buffer.", e);
|
||||
}
|
||||
return newFont;
|
||||
}
|
||||
}
|
||||
|
||||
@CriticalNative
|
||||
private static native long nGetFontInfo(long ptr);
|
||||
|
||||
@CriticalNative
|
||||
private static native long nGetAxisInfo(long ptr, int i);
|
||||
|
||||
@FastNative
|
||||
private static native float nGetGlyphBounds(long font, int glyphId, long paint, RectF rect);
|
||||
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.graphics.fonts;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import dalvik.annotation.optimization.CriticalNative;
|
||||
import dalvik.annotation.optimization.FastNative;
|
||||
|
||||
import libcore.util.NativeAllocationRegistry;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* This is a helper class for showing native allocated buffer in Java API.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class NativeFontBufferHelper {
|
||||
private NativeFontBufferHelper() {}
|
||||
|
||||
private static final NativeAllocationRegistry REGISTRY =
|
||||
NativeAllocationRegistry.createMalloced(
|
||||
ByteBuffer.class.getClassLoader(), nGetReleaseFunc());
|
||||
|
||||
/**
|
||||
* Wrap native buffer with ByteBuffer with adding reference to it.
|
||||
*/
|
||||
public static @NonNull ByteBuffer refByteBuffer(long fontPtr) {
|
||||
long refPtr = nRefFontBuffer(fontPtr);
|
||||
ByteBuffer buffer = nWrapByteBuffer(refPtr);
|
||||
|
||||
// Releasing native object so that decreasing shared pointer ref count when the byte buffer
|
||||
// is GCed.
|
||||
REGISTRY.registerNativeAllocation(buffer, refPtr);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
@CriticalNative
|
||||
private static native long nRefFontBuffer(long fontPtr);
|
||||
|
||||
@FastNative
|
||||
private static native ByteBuffer nWrapByteBuffer(long refPtr);
|
||||
|
||||
@CriticalNative
|
||||
private static native long nGetReleaseFunc();
|
||||
}
|
||||
234
graphics/java/android/graphics/text/GlyphStyle.java
Normal file
234
graphics/java/android/graphics/text/GlyphStyle.java
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.graphics.text;
|
||||
|
||||
import android.annotation.ColorInt;
|
||||
import android.annotation.FloatRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Paint;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents subset of Paint parameters such as font size, scaleX that is used to draw a glyph.
|
||||
*
|
||||
* Glyph is a most primitive unit of text drawing.
|
||||
*
|
||||
*/
|
||||
public class GlyphStyle {
|
||||
private @ColorInt int mColor;
|
||||
private float mFontSize;
|
||||
private float mScaleX;
|
||||
private float mSkewX;
|
||||
private int mFlags;
|
||||
|
||||
/**
|
||||
* @param color a color.
|
||||
* @param fontSize a font size in pixels.
|
||||
* @param scaleX a horizontal scale factor.
|
||||
* @param skewX a horizontal skew factor
|
||||
* @param flags paint flags
|
||||
*
|
||||
* @see Paint#getFlags()
|
||||
* @see Paint#setFlags(int)
|
||||
*/
|
||||
public GlyphStyle(
|
||||
@ColorInt int color,
|
||||
@FloatRange(from = 0) float fontSize,
|
||||
@FloatRange(from = 0) float scaleX,
|
||||
@FloatRange(from = 0) float skewX,
|
||||
int flags) {
|
||||
mColor = color;
|
||||
mFontSize = fontSize;
|
||||
mScaleX = scaleX;
|
||||
mSkewX = skewX;
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create glyph style from Paint
|
||||
*
|
||||
* @param paint a paint
|
||||
*/
|
||||
public GlyphStyle(@NonNull Paint paint) {
|
||||
setFromPaint(paint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the color.
|
||||
*
|
||||
* @return a color
|
||||
* @see Paint#getColor()
|
||||
* @see Paint#setColor(int)
|
||||
*/
|
||||
public @ColorInt int getColor() {
|
||||
return mColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the color.
|
||||
*
|
||||
* @param color a color
|
||||
* @see Paint#getColor()
|
||||
* @see Paint#setColor(int)
|
||||
*/
|
||||
public void setColor(@ColorInt int color) {
|
||||
mColor = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the font size in pixels.
|
||||
*
|
||||
* @return font size
|
||||
* @see Paint#getTextSize()
|
||||
* @see Paint#setTextSize(float)
|
||||
*/
|
||||
public @FloatRange(from = 0) float getFontSize() {
|
||||
return mFontSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the font size in pixels.
|
||||
*
|
||||
* @param fontSize font size in pixel
|
||||
* @see Paint#getTextSize()
|
||||
* @see Paint#setTextSize(float)
|
||||
*/
|
||||
public void setFontSize(@FloatRange(from = 0) float fontSize) {
|
||||
mFontSize = fontSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the horizontal scale factor
|
||||
*
|
||||
* @return a horizontal scale factor
|
||||
* @see Paint#getTextScaleX()
|
||||
* @see Paint#setTextScaleX(float)
|
||||
*/
|
||||
public @FloatRange(from = 0) float getScaleX() {
|
||||
return mScaleX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the horizontal scale factor
|
||||
*
|
||||
* @param scaleX a horizontal scale factor
|
||||
* @see Paint#getTextScaleX()
|
||||
* @see Paint#setTextScaleX(float)
|
||||
*/
|
||||
public void setScaleX(@FloatRange(from = 0) float scaleX) {
|
||||
mScaleX = scaleX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the horizontal skew factor
|
||||
*
|
||||
* @return a horizontal skew factor
|
||||
* @see Paint#getTextSkewX()
|
||||
* @see Paint#setTextSkewX(float)
|
||||
*/
|
||||
public @FloatRange(from = 0) float getSkewX() {
|
||||
return mSkewX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the horizontal skew factor
|
||||
*
|
||||
* @param skewX a horizontal skew factor
|
||||
* @see Paint#getTextSkewX()
|
||||
* @see Paint#setTextSkewX(float)
|
||||
*/
|
||||
public void setSkewX(@FloatRange(from = 0) float skewX) {
|
||||
mSkewX = skewX;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the Paint flags.
|
||||
*
|
||||
* @return a paint flags
|
||||
* @see Paint#getFlags()
|
||||
* @see Paint#setFlags(int)
|
||||
*/
|
||||
public int getFlags() {
|
||||
return mFlags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Paint flags.
|
||||
*
|
||||
* @param flags a paint flags
|
||||
* @see Paint#getFlags()
|
||||
* @see Paint#setFlags(int)
|
||||
*/
|
||||
public void setFlags(int flags) {
|
||||
mFlags = flags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies glyph style to the paint object.
|
||||
*
|
||||
* @param paint a paint object
|
||||
*/
|
||||
public void applyToPaint(@NonNull Paint paint) {
|
||||
paint.setColor(mColor);
|
||||
paint.setTextSize(mFontSize);
|
||||
paint.setTextScaleX(mScaleX);
|
||||
paint.setTextSkewX(mSkewX);
|
||||
paint.setFlags(mFlags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy parameters from a Paint object.
|
||||
*
|
||||
* @param paint a paint object
|
||||
*/
|
||||
public void setFromPaint(@NonNull Paint paint) {
|
||||
mColor = paint.getColor();
|
||||
mFontSize = paint.getTextSize();
|
||||
mScaleX = paint.getTextScaleX();
|
||||
mSkewX = paint.getTextSkewX();
|
||||
mFlags = paint.getFlags();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof GlyphStyle)) return false;
|
||||
GlyphStyle that = (GlyphStyle) o;
|
||||
return that.mColor == mColor
|
||||
&& Float.compare(that.mFontSize, mFontSize) == 0
|
||||
&& Float.compare(that.mScaleX, mScaleX) == 0
|
||||
&& Float.compare(that.mSkewX, mSkewX) == 0
|
||||
&& mFlags == that.mFlags;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mColor, mFontSize, mScaleX, mSkewX, mFlags);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "GlyphStyle{"
|
||||
+ "mColor=" + mColor
|
||||
+ ", mFontSize=" + mFontSize
|
||||
+ ", mScaleX=" + mScaleX
|
||||
+ ", mSkewX=" + mSkewX
|
||||
+ ", mFlags=" + mFlags
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
277
graphics/java/android/graphics/text/PositionedGlyphs.java
Normal file
277
graphics/java/android/graphics/text/PositionedGlyphs.java
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.graphics.text;
|
||||
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Typeface;
|
||||
import android.graphics.fonts.Font;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import dalvik.annotation.optimization.CriticalNative;
|
||||
|
||||
import libcore.util.NativeAllocationRegistry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Text shaping result object for single style text.
|
||||
*
|
||||
* You can get text shaping result by
|
||||
* {@link TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)} and
|
||||
* {@link TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)}.
|
||||
*
|
||||
* @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
|
||||
* @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
|
||||
*/
|
||||
public final class PositionedGlyphs {
|
||||
private static final NativeAllocationRegistry REGISTRY =
|
||||
NativeAllocationRegistry.createMalloced(
|
||||
Typeface.class.getClassLoader(), nReleaseFunc());
|
||||
|
||||
private final long mLayoutPtr;
|
||||
private final float mXOffset;
|
||||
private final float mYOffset;
|
||||
private final GlyphStyle mGlyphStyle;
|
||||
private final ArrayList<Font> mFonts;
|
||||
|
||||
/**
|
||||
* Returns the total amount of advance consumed by this positioned glyphs.
|
||||
*
|
||||
* The advance is an amount of width consumed by the glyph. The total amount of advance is
|
||||
* a total amount of advance consumed by this series of glyphs. In other words, if another
|
||||
* glyph is placed next to this series of glyphs, it's X offset should be shifted this amount
|
||||
* of width.
|
||||
*
|
||||
* @return total amount of advance
|
||||
*/
|
||||
public float getTotalAdvance() {
|
||||
return nGetTotalAdvance(mLayoutPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Effective ascent value of this positioned glyphs.
|
||||
*
|
||||
* If two or more font files are used in this series of glyphs, the effective ascent will be
|
||||
* the minimum ascent value across the all font files.
|
||||
*
|
||||
* @return effective ascent value
|
||||
*/
|
||||
public float getAscent() {
|
||||
return nGetAscent(mLayoutPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Effective descent value of this positioned glyphs.
|
||||
*
|
||||
* If two or more font files are used in this series of glyphs, the effective descent will be
|
||||
* the maximum descent value across the all font files.
|
||||
*
|
||||
* @return effective descent value
|
||||
*/
|
||||
public float getDescent() {
|
||||
return nGetDescent(mLayoutPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the glyph style used for drawing the glyph at the given index.
|
||||
*
|
||||
* @return A glyph style
|
||||
*/
|
||||
@NonNull
|
||||
public GlyphStyle getStyle() {
|
||||
return mGlyphStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of X offset added to glyph position.
|
||||
*
|
||||
* @return The X offset added to glyph position.
|
||||
*/
|
||||
public float getOriginX() {
|
||||
return mXOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of Y offset added to glyph position.
|
||||
*
|
||||
* @return The Y offset added to glyph position.
|
||||
*/
|
||||
public float getOriginY() {
|
||||
return mYOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of glyphs stored.
|
||||
*
|
||||
* @return the number of glyphs
|
||||
*/
|
||||
@IntRange(from = 0)
|
||||
public int glyphCount() {
|
||||
return nGetGlyphCount(mLayoutPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the font object used for drawing the glyph at the given index.
|
||||
*
|
||||
* @param index the glyph index
|
||||
* @return the font object used for drawing the glyph at the given index
|
||||
*/
|
||||
@NonNull
|
||||
public Font getFont(@IntRange(from = 0) int index) {
|
||||
Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
|
||||
return mFonts.get(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the glyph ID used for drawing the glyph at the given index.
|
||||
*
|
||||
* @param index the glyph index
|
||||
* @return A font object
|
||||
*/
|
||||
@IntRange(from = 0)
|
||||
public int getGlyphId(@IntRange(from = 0) int index) {
|
||||
Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
|
||||
return nGetGlyphId(mLayoutPtr, index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the x coordinate of the glyph position at the given index.
|
||||
*
|
||||
* @param index the glyph index
|
||||
* @return A X offset in pixels
|
||||
*/
|
||||
public float getPositionX(@IntRange(from = 0) int index) {
|
||||
Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
|
||||
return nGetX(mLayoutPtr, index) + mXOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the y coordinate of the glyph position at the given index.
|
||||
*
|
||||
* @param index the glyph index
|
||||
* @return A Y offset in pixels.
|
||||
*/
|
||||
public float getPositionY(@IntRange(from = 0) int index) {
|
||||
Preconditions.checkArgumentInRange(index, 0, glyphCount() - 1, "index");
|
||||
return nGetY(mLayoutPtr, index) + mYOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create single style layout from native result.
|
||||
*
|
||||
* @hide
|
||||
*
|
||||
* @param layoutPtr the address of native layout object.
|
||||
* @param paint a paint object
|
||||
*/
|
||||
public PositionedGlyphs(long layoutPtr, @NonNull Paint paint, float xOffset, float yOffset) {
|
||||
mLayoutPtr = layoutPtr;
|
||||
mGlyphStyle = new GlyphStyle(paint);
|
||||
int glyphCount = nGetGlyphCount(layoutPtr);
|
||||
mFonts = new ArrayList<>(glyphCount);
|
||||
mXOffset = xOffset;
|
||||
mYOffset = yOffset;
|
||||
|
||||
long prevPtr = 0;
|
||||
Font prevFont = null;
|
||||
for (int i = 0; i < glyphCount; ++i) {
|
||||
long ptr = nGetFont(layoutPtr, i);
|
||||
if (prevPtr != ptr) {
|
||||
prevPtr = ptr;
|
||||
prevFont = Font.findOrCreateFontFromNativePtr(ptr);
|
||||
}
|
||||
mFonts.add(prevFont);
|
||||
}
|
||||
|
||||
REGISTRY.registerNativeAllocation(this, layoutPtr);
|
||||
}
|
||||
|
||||
@CriticalNative
|
||||
private static native int nGetGlyphCount(long minikinLayout);
|
||||
@CriticalNative
|
||||
private static native float nGetTotalAdvance(long minikinLayout);
|
||||
@CriticalNative
|
||||
private static native float nGetAscent(long minikinLayout);
|
||||
@CriticalNative
|
||||
private static native float nGetDescent(long minikinLayout);
|
||||
@CriticalNative
|
||||
private static native int nGetGlyphId(long minikinLayout, int i);
|
||||
@CriticalNative
|
||||
private static native float nGetX(long minikinLayout, int i);
|
||||
@CriticalNative
|
||||
private static native float nGetY(long minikinLayout, int i);
|
||||
@CriticalNative
|
||||
private static native long nGetFont(long minikinLayout, int i);
|
||||
@CriticalNative
|
||||
private static native long nReleaseFunc();
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof PositionedGlyphs)) return false;
|
||||
PositionedGlyphs that = (PositionedGlyphs) o;
|
||||
|
||||
if (!mGlyphStyle.equals(that.mGlyphStyle)) return false;
|
||||
if (mXOffset != that.mXOffset || mYOffset != that.mYOffset) return false;
|
||||
if (glyphCount() != that.glyphCount()) return false;
|
||||
|
||||
for (int i = 0; i < glyphCount(); ++i) {
|
||||
if (getGlyphId(i) != that.getGlyphId(i)) return false;
|
||||
if (getPositionX(i) != that.getPositionX(i)) return false;
|
||||
if (getPositionY(i) != that.getPositionY(i)) return false;
|
||||
// Intentionally using reference equality since font equality is heavy due to buffer
|
||||
// compare.
|
||||
if (getFont(i) != that.getFont(i)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int hashCode = Objects.hash(mXOffset, mYOffset, mGlyphStyle);
|
||||
for (int i = 0; i < glyphCount(); ++i) {
|
||||
hashCode = Objects.hash(hashCode,
|
||||
getGlyphId(i), getPositionX(i), getPositionY(i), getFont(i));
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("[");
|
||||
for (int i = 0; i < glyphCount(); ++i) {
|
||||
if (i != 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append("[ ID = " + getGlyphId(i) + ","
|
||||
+ " pos = (" + getPositionX(i) + "," + getPositionY(i) + ")"
|
||||
+ " font = " + getFont(i) + " ]");
|
||||
}
|
||||
sb.append("]");
|
||||
return "PositionedGlyphs{"
|
||||
+ "glyphs = " + sb.toString()
|
||||
+ ", mXOffset=" + mXOffset
|
||||
+ ", mYOffset=" + mYOffset
|
||||
+ ", mGlyphStyle=" + mGlyphStyle
|
||||
+ '}';
|
||||
}
|
||||
}
|
||||
125
graphics/java/android/graphics/text/TextShaper.java
Normal file
125
graphics/java/android/graphics/text/TextShaper.java
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.graphics.text;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.graphics.Paint;
|
||||
import android.text.TextDirectionHeuristic;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import dalvik.annotation.optimization.FastNative;
|
||||
|
||||
/**
|
||||
* Provides conversion from a text into glyph array.
|
||||
*
|
||||
* Text shaping is a preprocess for drawing text into canvas with glyphs. The glyph is a most
|
||||
* primitive unit of the text drawing, consist of glyph identifier in the font file and its position
|
||||
* and style. You can draw the shape result to Canvas by calling Canvas#drawGlyphs.
|
||||
|
||||
*
|
||||
* @see TextShaper#shapeTextRun(CharSequence, int, int, int, int, float, float, boolean, Paint)
|
||||
* @see TextShaper#shapeTextRun(char[], int, int, int, int, float, float, boolean, Paint)
|
||||
* @see android.text.StyledTextShaper#shapeText(CharSequence, int, int, TextDirectionHeuristic,
|
||||
* TextPaint)
|
||||
*/
|
||||
public class TextShaper {
|
||||
private TextShaper() {} // Do not instantiate
|
||||
|
||||
/**
|
||||
* Shape non-styled text.
|
||||
*
|
||||
* This function shapes the text of the given range under the context of given context range.
|
||||
* Some script, e.g. Arabic or Devanagari, changes letter shape based on its location or
|
||||
* surrounding characters.
|
||||
*
|
||||
* @param text a text buffer to be shaped
|
||||
* @param start a start index of shaping target in the buffer.
|
||||
* @param count a length of shaping target in the buffer.
|
||||
* @param contextStart a start index of context used for shaping in the buffer.
|
||||
* @param contextCount a length of context used for shaping in the buffer.
|
||||
* @param xOffset an additional amount of x offset of the result glyphs.
|
||||
* @param yOffset an additional amount of y offset of the result glyphs.
|
||||
* @param isRtl true if this text is shaped for RTL direction, false otherwise.
|
||||
* @param paint a paint used for shaping text.
|
||||
* @return a shape result.
|
||||
*/
|
||||
@NonNull
|
||||
public static PositionedGlyphs shapeTextRun(
|
||||
@NonNull char[] text, int start, int count, int contextStart, int contextCount,
|
||||
float xOffset, float yOffset, boolean isRtl, @NonNull Paint paint) {
|
||||
Preconditions.checkNotNull(text);
|
||||
Preconditions.checkNotNull(paint);
|
||||
return new PositionedGlyphs(
|
||||
nativeShapeTextRun(text, start, count, contextStart, contextCount, isRtl,
|
||||
paint.getNativeInstance()),
|
||||
paint, xOffset, yOffset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shape non-styled text.
|
||||
*
|
||||
* This function shapes the text of the given range under the context of given context range.
|
||||
* Some script, e.g. Arabic or Devanagari, changes letter shape based on its location or
|
||||
* surrounding characters.
|
||||
*
|
||||
* @param text a text buffer to be shaped. Any styled spans stored in this text are ignored.
|
||||
* @param start a start index of shaping target in the buffer.
|
||||
* @param count a length of shaping target in the buffer.
|
||||
* @param contextStart a start index of context used for shaping in the buffer.
|
||||
* @param contextCount a length of context used for shaping in the buffer.
|
||||
* @param xOffset an additional amount of x offset of the result glyphs.
|
||||
* @param yOffset an additional amount of y offset of the result glyphs.
|
||||
* @param isRtl true if this text is shaped for RTL direction, false otherwise.
|
||||
* @param paint a paint used for shaping text.
|
||||
* @return a shape result
|
||||
*/
|
||||
@NonNull
|
||||
public static PositionedGlyphs shapeTextRun(
|
||||
@NonNull CharSequence text, int start, int count, int contextStart, int contextCount,
|
||||
float xOffset, float yOffset, boolean isRtl, @NonNull Paint paint) {
|
||||
Preconditions.checkNotNull(text);
|
||||
Preconditions.checkNotNull(paint);
|
||||
if (text instanceof String) {
|
||||
return new PositionedGlyphs(
|
||||
nativeShapeTextRun(
|
||||
(String) text, start, count, contextStart, contextCount, isRtl,
|
||||
paint.getNativeInstance()),
|
||||
paint, xOffset, yOffset);
|
||||
} else {
|
||||
char[] buf = new char[contextCount];
|
||||
TextUtils.getChars(text, contextStart, contextStart + contextCount, buf, 0);
|
||||
return new PositionedGlyphs(
|
||||
nativeShapeTextRun(
|
||||
buf, start - contextStart, count,
|
||||
0, contextCount, isRtl, paint.getNativeInstance()),
|
||||
paint, xOffset, yOffset);
|
||||
}
|
||||
}
|
||||
|
||||
@FastNative
|
||||
private static native long nativeShapeTextRun(
|
||||
char[] text, int start, int count, int contextStart, int contextCount,
|
||||
boolean isRtl, long nativePaint);
|
||||
|
||||
@FastNative
|
||||
private static native long nativeShapeTextRun(
|
||||
String text, int start, int count, int contextStart, int contextCount,
|
||||
boolean isRtl, long nativePaint);
|
||||
|
||||
}
|
||||
@@ -334,6 +334,7 @@ cc_defaults {
|
||||
"jni/fonts/FontFamily.cpp",
|
||||
"jni/text/LineBreaker.cpp",
|
||||
"jni/text/MeasuredText.cpp",
|
||||
"jni/text/TextShaper.cpp",
|
||||
],
|
||||
|
||||
header_libs: [ "android_graphics_jni_headers" ],
|
||||
|
||||
@@ -73,6 +73,7 @@ extern int register_android_graphics_pdf_PdfEditor(JNIEnv* env);
|
||||
extern int register_android_graphics_pdf_PdfRenderer(JNIEnv* env);
|
||||
extern int register_android_graphics_text_MeasuredText(JNIEnv* env);
|
||||
extern int register_android_graphics_text_LineBreaker(JNIEnv *env);
|
||||
extern int register_android_graphics_text_TextShaper(JNIEnv *env);
|
||||
|
||||
extern int register_android_util_PathParser(JNIEnv* env);
|
||||
extern int register_android_view_DisplayListCanvas(JNIEnv* env);
|
||||
@@ -137,6 +138,7 @@ static const RegJNIRec gRegJNI[] = {
|
||||
REG_JNI(register_android_graphics_pdf_PdfRenderer),
|
||||
REG_JNI(register_android_graphics_text_MeasuredText),
|
||||
REG_JNI(register_android_graphics_text_LineBreaker),
|
||||
REG_JNI(register_android_graphics_text_TextShaper),
|
||||
|
||||
REG_JNI(register_android_util_PathParser),
|
||||
REG_JNI(register_android_view_RenderNode),
|
||||
|
||||
@@ -72,7 +72,7 @@ public:
|
||||
size_t start = 0;
|
||||
size_t nGlyphs = layout.nGlyphs();
|
||||
for (size_t i = 0; i < nGlyphs; i++) {
|
||||
const minikin::MinikinFont* nextFont = layout.getFont(i);
|
||||
const minikin::MinikinFont* nextFont = layout.getFont(i)->typeface().get();
|
||||
if (i > 0 && nextFont != curFont) {
|
||||
SkFont* skfont = &paint->getSkFont();
|
||||
MinikinFontSkia::populateSkFont(skfont, curFont, layout.getFakery(start));
|
||||
|
||||
@@ -38,6 +38,14 @@ struct FontWrapper {
|
||||
minikin::Font font;
|
||||
};
|
||||
|
||||
// We assume FontWrapper's address is the same as underlying Font's address.
|
||||
// This assumption is used for looking up Java font object from native address.
|
||||
// The Font object can be created without Java's Font object but all Java's Font objects point to
|
||||
// the native FontWrapper. So when looking up Java object from minikin::Layout which gives us Font
|
||||
// address, we lookup Font Java object from Font address with assumption that it is the same as
|
||||
// FontWrapper address.
|
||||
static_assert(offsetof(FontWrapper, font) == 0);
|
||||
|
||||
// Utility wrapper for java.util.List
|
||||
class ListHelper {
|
||||
public:
|
||||
|
||||
@@ -198,6 +198,59 @@ static jfloat Font_getFontMetrics(JNIEnv* env, jobject, jlong fontHandle, jlong
|
||||
return spacing;
|
||||
}
|
||||
|
||||
// Critical Native
|
||||
static jlong Font_getFontInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
|
||||
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
|
||||
MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
|
||||
|
||||
uint64_t result = font->font.style().weight();
|
||||
result |= font->font.style().slant() == minikin::FontStyle::Slant::ITALIC ? 0x10000 : 0x00000;
|
||||
result |= ((static_cast<uint64_t>(minikinSkia->GetFontIndex())) << 32);
|
||||
result |= ((static_cast<uint64_t>(minikinSkia->GetAxes().size())) << 48);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Critical Native
|
||||
static jlong Font_getAxisInfo(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle, jint index) {
|
||||
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
|
||||
MinikinFontSkia* minikinSkia = static_cast<MinikinFontSkia*>(font->font.typeface().get());
|
||||
const minikin::FontVariation& var = minikinSkia->GetAxes().at(index);
|
||||
uint32_t floatBinary = *reinterpret_cast<const uint32_t*>(&var.value);
|
||||
return (static_cast<uint64_t>(var.axisTag) << 32) | static_cast<uint64_t>(floatBinary);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct FontBufferWrapper {
|
||||
FontBufferWrapper(const std::shared_ptr<minikin::MinikinFont>& font) : minikinFont(font) {}
|
||||
// MinikinFont holds a shared pointer of SkTypeface which has reference to font data.
|
||||
std::shared_ptr<minikin::MinikinFont> minikinFont;
|
||||
};
|
||||
|
||||
static void unrefBuffer(jlong nativePtr) {
|
||||
FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
|
||||
delete wrapper;
|
||||
}
|
||||
|
||||
// Critical Native
|
||||
static jlong FontBufferHelper_refFontBuffer(CRITICAL_JNI_PARAMS_COMMA jlong fontHandle) {
|
||||
FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
|
||||
return reinterpret_cast<jlong>(new FontBufferWrapper(font->font.typeface()));
|
||||
}
|
||||
|
||||
// Fast Native
|
||||
static jobject FontBufferHelper_wrapByteBuffer(JNIEnv* env, jobject, jlong nativePtr) {
|
||||
FontBufferWrapper* wrapper = reinterpret_cast<FontBufferWrapper*>(nativePtr);
|
||||
return env->NewDirectByteBuffer(
|
||||
const_cast<void*>(wrapper->minikinFont->GetFontData()),
|
||||
wrapper->minikinFont->GetFontSize());
|
||||
}
|
||||
|
||||
// Critical Native
|
||||
static jlong FontBufferHelper_getReleaseFunc(CRITICAL_JNI_PARAMS) {
|
||||
return reinterpret_cast<jlong>(unrefBuffer);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const JNINativeMethod gFontBuilderMethods[] = {
|
||||
@@ -211,13 +264,23 @@ static const JNINativeMethod gFontBuilderMethods[] = {
|
||||
static const JNINativeMethod gFontMethods[] = {
|
||||
{ "nGetGlyphBounds", "(JIJLandroid/graphics/RectF;)F", (void*) Font_getGlyphBounds },
|
||||
{ "nGetFontMetrics", "(JJLandroid/graphics/Paint$FontMetrics;)F", (void*) Font_getFontMetrics },
|
||||
{ "nGetFontInfo", "(J)J", (void*) Font_getFontInfo },
|
||||
{ "nGetAxisInfo", "(JI)J", (void*) Font_getAxisInfo },
|
||||
};
|
||||
|
||||
static const JNINativeMethod gFontBufferHelperMethods[] = {
|
||||
{ "nRefFontBuffer", "(J)J", (void*) FontBufferHelper_refFontBuffer },
|
||||
{ "nWrapByteBuffer", "(J)Ljava/nio/ByteBuffer;", (void*) FontBufferHelper_wrapByteBuffer },
|
||||
{ "nGetReleaseFunc", "()J", (void*) FontBufferHelper_getReleaseFunc },
|
||||
};
|
||||
|
||||
int register_android_graphics_fonts_Font(JNIEnv* env) {
|
||||
return RegisterMethodsOrDie(env, "android/graphics/fonts/Font$Builder", gFontBuilderMethods,
|
||||
NELEM(gFontBuilderMethods)) +
|
||||
RegisterMethodsOrDie(env, "android/graphics/fonts/Font", gFontMethods,
|
||||
NELEM(gFontMethods));
|
||||
NELEM(gFontMethods)) +
|
||||
RegisterMethodsOrDie(env, "android/graphics/fonts/NativeFontBufferHelper",
|
||||
gFontBufferHelperMethods, NELEM(gFontBufferHelperMethods));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
206
libs/hwui/jni/text/TextShaper.cpp
Normal file
206
libs/hwui/jni/text/TextShaper.cpp
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#undef LOG_TAG
|
||||
#define LOG_TAG "TextShaper"
|
||||
|
||||
#include "graphics_jni_helpers.h"
|
||||
#include <nativehelper/ScopedStringChars.h>
|
||||
#include <nativehelper/ScopedPrimitiveArray.h>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
#include "SkPaint.h"
|
||||
#include "SkTypeface.h"
|
||||
#include <hwui/MinikinSkia.h>
|
||||
#include <hwui/MinikinUtils.h>
|
||||
#include <hwui/Paint.h>
|
||||
#include <minikin/MinikinPaint.h>
|
||||
#include <minikin/MinikinFont.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
struct LayoutWrapper {
|
||||
LayoutWrapper(minikin::Layout&& layout, float ascent, float descent)
|
||||
: layout(std::move(layout)), ascent(ascent), descent(descent) {}
|
||||
minikin::Layout layout;
|
||||
float ascent;
|
||||
float descent;
|
||||
};
|
||||
|
||||
static void releaseLayout(jlong ptr) {
|
||||
delete reinterpret_cast<LayoutWrapper*>(ptr);
|
||||
}
|
||||
|
||||
static jlong shapeTextRun(const uint16_t* text, int textSize, int start, int count,
|
||||
int contextStart, int contextCount, minikin::Bidi bidiFlags,
|
||||
const Paint& paint, const Typeface* typeface) {
|
||||
|
||||
minikin::MinikinPaint minikinPaint = MinikinUtils::prepareMinikinPaint(&paint, typeface);
|
||||
|
||||
minikin::Layout layout = MinikinUtils::doLayout(&paint, bidiFlags, typeface,
|
||||
text, textSize, start, count, contextStart, contextCount, nullptr);
|
||||
|
||||
std::set<const minikin::Font*> seenFonts;
|
||||
float overallAscent = 0;
|
||||
float overallDescent = 0;
|
||||
for (int i = 0; i < layout.nGlyphs(); ++i) {
|
||||
const minikin::Font* font = layout.getFont(i);
|
||||
if (seenFonts.find(font) != seenFonts.end()) continue;
|
||||
minikin::MinikinExtent extent = {};
|
||||
font->typeface()->GetFontExtent(&extent, minikinPaint, layout.getFakery(i));
|
||||
overallAscent = std::min(overallAscent, extent.ascent);
|
||||
overallDescent = std::max(overallDescent, extent.descent);
|
||||
}
|
||||
|
||||
std::unique_ptr<LayoutWrapper> ptr = std::make_unique<LayoutWrapper>(
|
||||
std::move(layout), overallAscent, overallDescent
|
||||
);
|
||||
|
||||
return reinterpret_cast<jlong>(ptr.release());
|
||||
}
|
||||
|
||||
static jlong TextShaper_shapeTextRunChars(JNIEnv *env, jobject, jcharArray charArray,
|
||||
jint start, jint count, jint contextStart, jint contextCount, jboolean isRtl,
|
||||
jlong paintPtr) {
|
||||
ScopedCharArrayRO text(env, charArray);
|
||||
Paint* paint = reinterpret_cast<Paint*>(paintPtr);
|
||||
const Typeface* typeface = paint->getAndroidTypeface();
|
||||
const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
|
||||
return shapeTextRun(
|
||||
text.get(), text.size(),
|
||||
start, count,
|
||||
contextStart, contextCount,
|
||||
bidiFlags,
|
||||
*paint, typeface);
|
||||
|
||||
}
|
||||
|
||||
static jlong TextShaper_shapeTextRunString(JNIEnv *env, jobject, jstring string,
|
||||
jint start, jint count, jint contextStart, jint contextCount, jboolean isRtl,
|
||||
jlong paintPtr) {
|
||||
ScopedStringChars text(env, string);
|
||||
Paint* paint = reinterpret_cast<Paint*>(paintPtr);
|
||||
const Typeface* typeface = paint->getAndroidTypeface();
|
||||
const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
|
||||
return shapeTextRun(
|
||||
text.get(), text.size(),
|
||||
start, count,
|
||||
contextStart, contextCount,
|
||||
bidiFlags,
|
||||
*paint, typeface);
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jint TextShaper_Result_getGlyphCount(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
|
||||
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
|
||||
return layout->layout.nGlyphs();
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jfloat TextShaper_Result_getTotalAdvance(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
|
||||
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
|
||||
return layout->layout.getAdvance();
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jfloat TextShaper_Result_getAscent(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
|
||||
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
|
||||
return layout->ascent;
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jfloat TextShaper_Result_getDescent(CRITICAL_JNI_PARAMS_COMMA jlong ptr) {
|
||||
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
|
||||
return layout->descent;
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jint TextShaper_Result_getGlyphId(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
|
||||
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
|
||||
return layout->layout.getGlyphId(i);
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jfloat TextShaper_Result_getX(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
|
||||
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
|
||||
return layout->layout.getX(i);
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jfloat TextShaper_Result_getY(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
|
||||
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
|
||||
return layout->layout.getY(i);
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jlong TextShaper_Result_getFont(CRITICAL_JNI_PARAMS_COMMA jlong ptr, jint i) {
|
||||
const LayoutWrapper* layout = reinterpret_cast<LayoutWrapper*>(ptr);
|
||||
return reinterpret_cast<jlong>(layout->layout.getFont(i));
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jlong TextShaper_Result_nReleaseFunc(CRITICAL_JNI_PARAMS) {
|
||||
return reinterpret_cast<jlong>(releaseLayout);
|
||||
}
|
||||
|
||||
static const JNINativeMethod gMethods[] = {
|
||||
// Fast Natives
|
||||
{"nativeShapeTextRun", "("
|
||||
"[C" // text
|
||||
"I" // start
|
||||
"I" // count
|
||||
"I" // contextStart
|
||||
"I" // contextCount
|
||||
"Z" // isRtl
|
||||
"J)" // paint
|
||||
"J", // LayoutPtr
|
||||
(void*) TextShaper_shapeTextRunChars},
|
||||
|
||||
{"nativeShapeTextRun", "("
|
||||
"Ljava/lang/String;" // text
|
||||
"I" // start
|
||||
"I" // count
|
||||
"I" // contextStart
|
||||
"I" // contextCount
|
||||
"Z" // isRtl
|
||||
"J)" // paint
|
||||
"J", // LayoutPtr
|
||||
(void*) TextShaper_shapeTextRunString},
|
||||
|
||||
};
|
||||
|
||||
static const JNINativeMethod gResultMethods[] = {
|
||||
{ "nGetGlyphCount", "(J)I", (void*)TextShaper_Result_getGlyphCount },
|
||||
{ "nGetTotalAdvance", "(J)F", (void*)TextShaper_Result_getTotalAdvance },
|
||||
{ "nGetAscent", "(J)F", (void*)TextShaper_Result_getAscent },
|
||||
{ "nGetDescent", "(J)F", (void*)TextShaper_Result_getDescent },
|
||||
{ "nGetGlyphId", "(JI)I", (void*)TextShaper_Result_getGlyphId },
|
||||
{ "nGetX", "(JI)F", (void*)TextShaper_Result_getX },
|
||||
{ "nGetY", "(JI)F", (void*)TextShaper_Result_getY },
|
||||
{ "nGetFont", "(JI)J", (void*)TextShaper_Result_getFont },
|
||||
{ "nReleaseFunc", "()J", (void*)TextShaper_Result_nReleaseFunc },
|
||||
};
|
||||
|
||||
int register_android_graphics_text_TextShaper(JNIEnv* env) {
|
||||
return RegisterMethodsOrDie(env, "android/graphics/text/TextShaper", gMethods,
|
||||
NELEM(gMethods))
|
||||
+ RegisterMethodsOrDie(env, "android/graphics/text/PositionedGlyphs",
|
||||
gResultMethods, NELEM(gResultMethods));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16490,6 +16490,23 @@ package android.graphics.pdf {
|
||||
|
||||
package android.graphics.text {
|
||||
|
||||
public class GlyphStyle {
|
||||
ctor public GlyphStyle(@ColorInt int, @FloatRange(from=0) float, @FloatRange(from=0) float, @FloatRange(from=0) float, int);
|
||||
ctor public GlyphStyle(@NonNull android.graphics.Paint);
|
||||
method public void applyToPaint(@NonNull android.graphics.Paint);
|
||||
method @ColorInt public int getColor();
|
||||
method public int getFlags();
|
||||
method @FloatRange(from=0) public float getFontSize();
|
||||
method @FloatRange(from=0) public float getScaleX();
|
||||
method @FloatRange(from=0) public float getSkewX();
|
||||
method public void setColor(@ColorInt int);
|
||||
method public void setFlags(int);
|
||||
method public void setFontSize(@FloatRange(from=0) float);
|
||||
method public void setFromPaint(@NonNull android.graphics.Paint);
|
||||
method public void setScaleX(@FloatRange(from=0) float);
|
||||
method public void setSkewX(@FloatRange(from=0) float);
|
||||
}
|
||||
|
||||
public class LineBreaker {
|
||||
method @NonNull public android.graphics.text.LineBreaker.Result computeLineBreaks(@NonNull android.graphics.text.MeasuredText, @NonNull android.graphics.text.LineBreaker.ParagraphConstraints, @IntRange(from=0) int);
|
||||
field public static final int BREAK_STRATEGY_BALANCED = 2; // 0x2
|
||||
@@ -16550,6 +16567,25 @@ package android.graphics.text {
|
||||
method @NonNull public android.graphics.text.MeasuredText.Builder setComputeLayout(boolean);
|
||||
}
|
||||
|
||||
public final class PositionedGlyphs {
|
||||
method public float getAscent();
|
||||
method public float getDescent();
|
||||
method @NonNull public android.graphics.fonts.Font getFont(@IntRange(from=0) int);
|
||||
method @IntRange(from=0) public int getGlyphId(@IntRange(from=0) int);
|
||||
method public float getOriginX();
|
||||
method public float getOriginY();
|
||||
method public float getPositionX(@IntRange(from=0) int);
|
||||
method public float getPositionY(@IntRange(from=0) int);
|
||||
method @NonNull public android.graphics.text.GlyphStyle getStyle();
|
||||
method public float getTotalAdvance();
|
||||
method @IntRange(from=0) public int glyphCount();
|
||||
}
|
||||
|
||||
public class TextShaper {
|
||||
method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull char[], int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
|
||||
method @NonNull public static android.graphics.text.PositionedGlyphs shapeTextRun(@NonNull CharSequence, int, int, int, int, float, float, boolean, @NonNull android.graphics.Paint);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
package android.hardware {
|
||||
@@ -48127,6 +48163,10 @@ package android.text {
|
||||
method @NonNull public android.text.StaticLayout.Builder setUseLineSpacingFromFallbacks(boolean);
|
||||
}
|
||||
|
||||
public class StyledTextShaper {
|
||||
method @NonNull public static java.util.List<android.graphics.text.PositionedGlyphs> shapeText(@NonNull CharSequence, int, int, @NonNull android.text.TextDirectionHeuristic, @NonNull android.text.TextPaint);
|
||||
}
|
||||
|
||||
public interface TextDirectionHeuristic {
|
||||
method public boolean isRtl(char[], int, int);
|
||||
method public boolean isRtl(CharSequence, int, int);
|
||||
|
||||
Reference in New Issue
Block a user