Merge "Compute full text layout in MeasuredText and use it for drawing"
This commit is contained in:
committed by
Android (Google) Code Review
commit
ab802003b9
@@ -347,7 +347,14 @@ public class BoringLayout extends Layout implements TextUtils.EllipsizeCallback
|
||||
TextLine line = TextLine.obtain();
|
||||
line.set(paint, text, 0, textLength, Layout.DIR_LEFT_TO_RIGHT,
|
||||
Layout.DIRS_ALL_LEFT_TO_RIGHT, false, null);
|
||||
fm.width = (int) Math.ceil(line.metrics(fm));
|
||||
if (text instanceof MeasuredText) {
|
||||
MeasuredText mt = (MeasuredText) text;
|
||||
// Reaching here means there is only one paragraph.
|
||||
MeasuredParagraph mp = mt.getMeasuredParagraph(0);
|
||||
fm.width = (int) Math.ceil(mp.getWidth(0, mp.getTextLength()));
|
||||
} else {
|
||||
fm.width = (int) Math.ceil(line.metrics(fm));
|
||||
}
|
||||
TextLine.recycle(line);
|
||||
|
||||
return fm;
|
||||
|
||||
@@ -175,6 +175,15 @@ public class MeasuredParagraph {
|
||||
unbindNativeObject();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the paragraph.
|
||||
*
|
||||
* This is always available.
|
||||
*/
|
||||
public int getTextLength() {
|
||||
return mTextLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the characters to be measured.
|
||||
*
|
||||
@@ -212,7 +221,7 @@ public class MeasuredParagraph {
|
||||
/**
|
||||
* Returns the whole text width.
|
||||
*
|
||||
* This is available only if the MeasureText is computed with computeForMeasurement.
|
||||
* This is available only if the MeasuredParagraph is computed with buildForMeasurement.
|
||||
* Returns 0 in other cases.
|
||||
*/
|
||||
public @FloatRange(from = 0.0f) float getWholeWidth() {
|
||||
@@ -222,7 +231,7 @@ public class MeasuredParagraph {
|
||||
/**
|
||||
* Returns the individual character's width.
|
||||
*
|
||||
* This is available only if the MeasureText is computed with computeForMeasurement.
|
||||
* This is available only if the MeasuredParagraph is computed with buildForMeasurement.
|
||||
* Returns empty array in other cases.
|
||||
*/
|
||||
public @NonNull FloatArray getWidths() {
|
||||
@@ -234,7 +243,7 @@ public class MeasuredParagraph {
|
||||
*
|
||||
* If the input text is not a spanned string, this has one value that is the length of the text.
|
||||
*
|
||||
* This is available only if the MeasureText is computed with computeForStaticLayout.
|
||||
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
|
||||
* Returns empty array in other cases.
|
||||
*/
|
||||
public @NonNull IntArray getSpanEndCache() {
|
||||
@@ -246,7 +255,7 @@ public class MeasuredParagraph {
|
||||
*
|
||||
* This array holds the repeat of top, bottom, ascent, descent of font metrics value.
|
||||
*
|
||||
* This is available only if the MeasureText is computed with computeForStaticLayout.
|
||||
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
|
||||
* Returns empty array in other cases.
|
||||
*/
|
||||
public @NonNull IntArray getFontMetrics() {
|
||||
@@ -256,13 +265,37 @@ public class MeasuredParagraph {
|
||||
/**
|
||||
* Returns the native ptr of the MeasuredParagraph.
|
||||
*
|
||||
* This is available only if the MeasureText is computed with computeForStaticLayout.
|
||||
* This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
|
||||
* Returns 0 in other cases.
|
||||
*/
|
||||
public /* Maybe Zero */ long getNativePtr() {
|
||||
return mNativePtr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of the given range.
|
||||
*
|
||||
* This is not available if the MeasuredParagraph is computed with buildForBidi.
|
||||
* Returns 0 if the MeasuredParagraph is computed with buildForBidi.
|
||||
*
|
||||
* @param start the inclusive start offset of the target region in the text
|
||||
* @param end the exclusive end offset of the target region in the text
|
||||
*/
|
||||
public float getWidth(int start, int end) {
|
||||
if (mNativePtr == 0) {
|
||||
// We have result in Java.
|
||||
final float[] widths = mWidths.getRawArray();
|
||||
float r = 0.0f;
|
||||
for (int i = start; i < end; ++i) {
|
||||
r += widths[i];
|
||||
}
|
||||
return r;
|
||||
} else {
|
||||
// We have result in native.
|
||||
return nGetWidth(mNativePtr, start, end);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates new MeasuredParagraph for Bidi computation.
|
||||
*
|
||||
@@ -357,6 +390,7 @@ public class MeasuredParagraph {
|
||||
@IntRange(from = 0) int end,
|
||||
@NonNull TextDirectionHeuristic textDir,
|
||||
boolean computeHyphenation,
|
||||
boolean computeLayout,
|
||||
@Nullable MeasuredParagraph recycle) {
|
||||
final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
|
||||
mt.resetAndAnalyzeBidi(text, start, end, textDir);
|
||||
@@ -367,7 +401,7 @@ public class MeasuredParagraph {
|
||||
try {
|
||||
mt.bindNativeObject(
|
||||
nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
|
||||
computeHyphenation));
|
||||
computeHyphenation, computeLayout));
|
||||
} finally {
|
||||
nFreeBuilder(nativeBuilderPtr);
|
||||
}
|
||||
@@ -397,7 +431,7 @@ public class MeasuredParagraph {
|
||||
}
|
||||
}
|
||||
mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
|
||||
computeHyphenation));
|
||||
computeHyphenation, computeLayout));
|
||||
} finally {
|
||||
nFreeBuilder(nativeBuilderPtr);
|
||||
}
|
||||
@@ -595,7 +629,7 @@ public class MeasuredParagraph {
|
||||
*
|
||||
* If forward=false is passed, returns the minimum index from the end instead.
|
||||
*
|
||||
* This only works if the MeasuredParagraph is computed with computeForMeasurement.
|
||||
* This only works if the MeasuredParagraph is computed with buildForMeasurement.
|
||||
* Undefined behavior in other case.
|
||||
*/
|
||||
@IntRange(from = 0) int breakText(int limit, boolean forwards, float width) {
|
||||
@@ -626,7 +660,7 @@ public class MeasuredParagraph {
|
||||
/**
|
||||
* Returns the length of the substring.
|
||||
*
|
||||
* This only works if the MeasuredParagraph is computed with computeForMeasurement.
|
||||
* This only works if the MeasuredParagraph is computed with buildForMeasurement.
|
||||
* Undefined behavior in other case.
|
||||
*/
|
||||
@FloatRange(from = 0.0f) float measure(int start, int limit) {
|
||||
@@ -672,10 +706,16 @@ public class MeasuredParagraph {
|
||||
|
||||
private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
|
||||
@NonNull char[] text,
|
||||
boolean computeHyphenation);
|
||||
boolean computeHyphenation,
|
||||
boolean computeLayout);
|
||||
|
||||
private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);
|
||||
|
||||
@CriticalNative
|
||||
private static native float nGetWidth(/* Non Zero */ long nativePtr,
|
||||
@IntRange(from = 0) int start,
|
||||
@IntRange(from = 0) int end);
|
||||
|
||||
@CriticalNative
|
||||
private static native /* Non Zero */ long nGetReleaseFunc();
|
||||
}
|
||||
|
||||
@@ -155,6 +155,11 @@ public class MeasuredText implements Spanned {
|
||||
* @return the measured text.
|
||||
*/
|
||||
public @NonNull MeasuredText build() {
|
||||
return build(true /* build full layout result */);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public @NonNull MeasuredText build(boolean computeLayout) {
|
||||
final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE
|
||||
&& mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE;
|
||||
|
||||
@@ -175,7 +180,7 @@ public class MeasuredText implements Spanned {
|
||||
paragraphEnds.add(paraEnd);
|
||||
measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
|
||||
mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation,
|
||||
null /* no recycle */));
|
||||
computeLayout, null /* no recycle */));
|
||||
}
|
||||
|
||||
return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy,
|
||||
@@ -198,7 +203,8 @@ public class MeasuredText implements Spanned {
|
||||
mText = text;
|
||||
mStart = start;
|
||||
mEnd = end;
|
||||
mPaint = paint;
|
||||
// Copy the paint so that we can keep the reference of typeface in native layout result.
|
||||
mPaint = new TextPaint(paint);
|
||||
mMeasuredParagraphs = measuredTexts;
|
||||
mParagraphBreakPoints = paragraphBreakPoints;
|
||||
mTextDir = textDir;
|
||||
@@ -283,6 +289,50 @@ public class MeasuredText implements Spanned {
|
||||
return mHyphenationFrequency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given TextPaint gives the same result of text layout for this text.
|
||||
* @hide
|
||||
*/
|
||||
public boolean canUseMeasuredResult(@NonNull TextPaint paint) {
|
||||
return mPaint.getTextSize() == paint.getTextSize()
|
||||
&& mPaint.getTextSkewX() == paint.getTextSkewX()
|
||||
&& mPaint.getTextScaleX() == paint.getTextScaleX()
|
||||
&& mPaint.getLetterSpacing() == paint.getLetterSpacing()
|
||||
&& mPaint.getWordSpacing() == paint.getWordSpacing()
|
||||
&& mPaint.getFlags() == paint.getFlags() // Maybe not all flag affects text layout.
|
||||
&& mPaint.getTextLocales() == paint.getTextLocales() // need to be equals?
|
||||
&& mPaint.getFontVariationSettings() == paint.getFontVariationSettings()
|
||||
&& mPaint.getTypeface() == paint.getTypeface()
|
||||
&& TextUtils.equals(mPaint.getFontFeatureSettings(), paint.getFontFeatureSettings());
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public int findParaIndex(@IntRange(from = 0) int pos) {
|
||||
// TODO: Maybe good to remove paragraph concept from MeasuredText and add substring layout
|
||||
// support to StaticLayout.
|
||||
for (int i = 0; i < mParagraphBreakPoints.length; ++i) {
|
||||
if (pos < mParagraphBreakPoints[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
throw new IndexOutOfBoundsException(
|
||||
"pos must be less than " + mParagraphBreakPoints[mParagraphBreakPoints.length - 1]
|
||||
+ ", gave " + pos);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public float getWidth(@IntRange(from = 0) int start, @IntRange(from = 0) int end) {
|
||||
final int paraIndex = findParaIndex(start);
|
||||
final int paraStart = getParagraphStart(paraIndex);
|
||||
final int paraEnd = getParagraphEnd(paraIndex);
|
||||
if (start < paraStart || paraEnd < end) {
|
||||
throw new RuntimeException("Cannot measured across the paragraph:"
|
||||
+ "para: (" + paraStart + ", " + paraEnd + "), "
|
||||
+ "request: (" + start + ", " + end + ")");
|
||||
}
|
||||
return getMeasuredParagraph(paraIndex).getWidth(start - paraStart, end - paraStart);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Spanned overrides
|
||||
//
|
||||
|
||||
@@ -678,7 +678,7 @@ public class StaticLayout extends Layout {
|
||||
.setTextDirection(textDir)
|
||||
.setBreakStrategy(b.mBreakStrategy)
|
||||
.setHyphenationFrequency(b.mHyphenationFrequency)
|
||||
.build();
|
||||
.build(false /* full layout is not necessary for line breaking */);
|
||||
spanned = (source instanceof Spanned) ? (Spanned) source : null;
|
||||
} else {
|
||||
final CharSequence original = measured.getText();
|
||||
|
||||
@@ -60,6 +60,7 @@ public class TextLine {
|
||||
private char[] mChars;
|
||||
private boolean mCharsValid;
|
||||
private Spanned mSpanned;
|
||||
private MeasuredText mMeasured;
|
||||
|
||||
// Additional width of whitespace for justification. This value is per whitespace, thus
|
||||
// the line width will increase by mAddedWidth x (number of stretchable whitespaces).
|
||||
@@ -118,6 +119,7 @@ public class TextLine {
|
||||
tl.mSpanned = null;
|
||||
tl.mTabs = null;
|
||||
tl.mChars = null;
|
||||
tl.mMeasured = null;
|
||||
|
||||
tl.mMetricAffectingSpanSpanSet.recycle();
|
||||
tl.mCharacterStyleSpanSet.recycle();
|
||||
@@ -168,6 +170,14 @@ public class TextLine {
|
||||
hasReplacement = mReplacementSpanSpanSet.numberOfSpans > 0;
|
||||
}
|
||||
|
||||
mMeasured = null;
|
||||
if (text instanceof MeasuredText) {
|
||||
MeasuredText mt = (MeasuredText) text;
|
||||
if (mt.canUseMeasuredResult(paint)) {
|
||||
mMeasured = mt;
|
||||
}
|
||||
}
|
||||
|
||||
mCharsValid = hasReplacement || hasTabs || directions != Layout.DIRS_ALL_LEFT_TO_RIGHT;
|
||||
|
||||
if (mCharsValid) {
|
||||
@@ -736,8 +746,13 @@ public class TextLine {
|
||||
return wp.getRunAdvance(mChars, start, end, contextStart, contextEnd, runIsRtl, offset);
|
||||
} else {
|
||||
final int delta = mStart;
|
||||
return wp.getRunAdvance(mText, delta + start, delta + end,
|
||||
delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
|
||||
if (mMeasured == null) {
|
||||
// TODO: Enable measured getRunAdvance for ReplacementSpan and RTL text.
|
||||
return wp.getRunAdvance(mText, delta + start, delta + end,
|
||||
delta + contextStart, delta + contextEnd, runIsRtl, delta + offset);
|
||||
} else {
|
||||
return mMeasured.getWidth(start + delta, end + delta);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.TemporaryBuffer;
|
||||
import android.text.GraphicsOperations;
|
||||
import android.text.MeasuredText;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannedString;
|
||||
import android.text.TextUtils;
|
||||
@@ -473,7 +474,8 @@ public class RecordingCanvas extends Canvas {
|
||||
}
|
||||
|
||||
nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
|
||||
x, y, isRtl, paint.getNativeInstance());
|
||||
x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */,
|
||||
0 /* measured text offset */);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -503,8 +505,20 @@ public class RecordingCanvas extends Canvas {
|
||||
int len = end - start;
|
||||
char[] buf = TemporaryBuffer.obtain(contextLen);
|
||||
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
|
||||
long measuredTextPtr = 0;
|
||||
int measuredTextOffset = 0;
|
||||
if (text instanceof MeasuredText) {
|
||||
MeasuredText mt = (MeasuredText) text;
|
||||
int paraIndex = mt.findParaIndex(start);
|
||||
if (end <= mt.getParagraphEnd(paraIndex)) {
|
||||
// Only support if the target is in the same paragraph.
|
||||
measuredTextPtr = mt.getMeasuredParagraph(paraIndex).getNativePtr();
|
||||
measuredTextOffset = start - mt.getParagraphStart(paraIndex);
|
||||
}
|
||||
}
|
||||
nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
|
||||
0, contextLen, x, y, isRtl, paint.getNativeInstance());
|
||||
0, contextLen, x, y, isRtl, paint.getNativeInstance(),
|
||||
measuredTextPtr, measuredTextOffset);
|
||||
TemporaryBuffer.recycle(buf);
|
||||
}
|
||||
}
|
||||
@@ -626,7 +640,8 @@ public class RecordingCanvas extends Canvas {
|
||||
|
||||
@FastNative
|
||||
private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
|
||||
int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint);
|
||||
int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
|
||||
long nativeMeasuredText, int measuredTextOffset);
|
||||
|
||||
@FastNative
|
||||
private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
|
||||
|
||||
@@ -307,7 +307,8 @@ namespace PaintGlue {
|
||||
static void getTextPath(JNIEnv* env, Paint* paint, const Typeface* typeface, const jchar* text,
|
||||
jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) {
|
||||
minikin::Layout layout = MinikinUtils::doLayout(
|
||||
paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, 0, count, count);
|
||||
paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text, 0, count, count,
|
||||
nullptr, 0);
|
||||
size_t nGlyphs = layout.nGlyphs();
|
||||
uint16_t* glyphs = new uint16_t[nGlyphs];
|
||||
SkPoint* pos = new SkPoint[nGlyphs];
|
||||
@@ -349,7 +350,8 @@ namespace PaintGlue {
|
||||
SkIRect ir;
|
||||
|
||||
minikin::Layout layout = MinikinUtils::doLayout(&paint,
|
||||
static_cast<minikin::Bidi>(bidiFlags), typeface, text, 0, count, count);
|
||||
static_cast<minikin::Bidi>(bidiFlags), typeface, text, 0, count, count, nullptr,
|
||||
0);
|
||||
minikin::MinikinRect rect;
|
||||
layout.getBounds(&rect);
|
||||
r.fLeft = rect.mLeft;
|
||||
@@ -465,7 +467,7 @@ namespace PaintGlue {
|
||||
}
|
||||
minikin::Layout layout = MinikinUtils::doLayout(paint,
|
||||
static_cast<minikin::Bidi>(bidiFlags), typeface, str.get(), 0, str.size(),
|
||||
str.size());
|
||||
str.size(), nullptr, 0);
|
||||
size_t nGlyphs = countNonSpaceGlyphs(layout);
|
||||
if (nGlyphs != 1 && nChars > 1) {
|
||||
// multiple-character input, and was not a ligature
|
||||
@@ -485,7 +487,8 @@ namespace PaintGlue {
|
||||
// U+1F1FF (REGIONAL INDICATOR SYMBOL LETTER Z) is \uD83C\uDDFF in UTF16.
|
||||
static const jchar ZZ_FLAG_STR[] = { 0xD83C, 0xDDFF, 0xD83C, 0xDDFF };
|
||||
minikin::Layout zzLayout = MinikinUtils::doLayout(paint,
|
||||
static_cast<minikin::Bidi>(bidiFlags), typeface, ZZ_FLAG_STR, 0, 4, 4);
|
||||
static_cast<minikin::Bidi>(bidiFlags), typeface, ZZ_FLAG_STR, 0, 4, 4,
|
||||
nullptr, 0);
|
||||
if (zzLayout.nGlyphs() != 1 || layoutContainsNotdef(zzLayout)) {
|
||||
// The font collection doesn't have a glyph for unknown flag. Just return true.
|
||||
return true;
|
||||
|
||||
@@ -30,6 +30,10 @@
|
||||
#include "SkRegion.h"
|
||||
#include "SkVertices.h"
|
||||
|
||||
namespace minikin {
|
||||
class MeasuredText;
|
||||
} // namespace minikin
|
||||
|
||||
namespace android {
|
||||
|
||||
namespace CanvasJNI {
|
||||
@@ -480,7 +484,7 @@ static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray t
|
||||
const Typeface* typeface = paint->getAndroidTypeface();
|
||||
jchar* jchars = env->GetCharArrayElements(text, NULL);
|
||||
get_canvas(canvasHandle)->drawText(jchars + index, 0, count, count, x, y,
|
||||
static_cast<minikin::Bidi>(bidiFlags), *paint, typeface);
|
||||
static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr, 0);
|
||||
env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
|
||||
}
|
||||
|
||||
@@ -492,20 +496,22 @@ static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring tex
|
||||
const int count = end - start;
|
||||
const jchar* jchars = env->GetStringChars(text, NULL);
|
||||
get_canvas(canvasHandle)->drawText(jchars + start, 0, count, count, x, y,
|
||||
static_cast<minikin::Bidi>(bidiFlags), *paint, typeface);
|
||||
static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr, 0);
|
||||
env->ReleaseStringChars(text, jchars);
|
||||
}
|
||||
|
||||
static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
|
||||
jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
|
||||
jboolean isRtl, jlong paintHandle) {
|
||||
jboolean isRtl, jlong paintHandle, jlong mtHandle, jint mtOffset) {
|
||||
Paint* paint = reinterpret_cast<Paint*>(paintHandle);
|
||||
minikin::MeasuredText* mt = reinterpret_cast<minikin::MeasuredText*>(mtHandle);
|
||||
const Typeface* typeface = paint->getAndroidTypeface();
|
||||
|
||||
const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
|
||||
jchar* jchars = env->GetCharArrayElements(text, NULL);
|
||||
get_canvas(canvasHandle)->drawText(jchars + contextIndex, index - contextIndex, count,
|
||||
contextCount, x, y, bidiFlags, *paint, typeface);
|
||||
contextCount, x, y, bidiFlags, *paint, typeface, mt,
|
||||
mtOffset);
|
||||
env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
|
||||
}
|
||||
|
||||
@@ -520,7 +526,7 @@ static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstr
|
||||
jint contextCount = contextEnd - contextStart;
|
||||
const jchar* jchars = env->GetStringChars(text, NULL);
|
||||
get_canvas(canvasHandle)->drawText(jchars + contextStart, start - contextStart, count,
|
||||
contextCount, x, y, bidiFlags, *paint, typeface);
|
||||
contextCount, x, y, bidiFlags, *paint, typeface, nullptr, 0);
|
||||
env->ReleaseStringChars(text, jchars);
|
||||
}
|
||||
|
||||
@@ -628,7 +634,7 @@ static const JNINativeMethod gDrawMethods[] = {
|
||||
{"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
|
||||
{"nDrawText","(J[CIIFFIJ)V", (void*) CanvasJNI::drawTextChars},
|
||||
{"nDrawText","(JLjava/lang/String;IIFFIJ)V", (void*) CanvasJNI::drawTextString},
|
||||
{"nDrawTextRun","(J[CIIIIFFZJ)V", (void*) CanvasJNI::drawTextRunChars},
|
||||
{"nDrawTextRun","(J[CIIIIFFZJJI)V", (void*) CanvasJNI::drawTextRunChars},
|
||||
{"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString},
|
||||
{"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars},
|
||||
{"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString},
|
||||
|
||||
@@ -85,12 +85,14 @@ static void nAddReplacementRun(JNIEnv* /* unused */, jclass /* unused */, jlong
|
||||
|
||||
// Regular JNI
|
||||
static jlong nBuildNativeMeasuredParagraph(JNIEnv* env, jclass /* unused */, jlong builderPtr,
|
||||
jcharArray javaText, jboolean computeHyphenation) {
|
||||
jcharArray javaText, jboolean computeHyphenation,
|
||||
jboolean computeLayout) {
|
||||
ScopedCharArrayRO text(env, javaText);
|
||||
const minikin::U16StringPiece textBuffer(text.get(), text.size());
|
||||
|
||||
// Pass the ownership to Java.
|
||||
return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation).release());
|
||||
return toJLong(toBuilder(builderPtr)->build(textBuffer, computeHyphenation,
|
||||
computeLayout).release());
|
||||
}
|
||||
|
||||
// Regular JNI
|
||||
@@ -98,6 +100,16 @@ static void nFreeBuilder(JNIEnv* env, jclass /* unused */, jlong builderPtr) {
|
||||
delete toBuilder(builderPtr);
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jfloat nGetWidth(jlong ptr, jint start, jint end) {
|
||||
minikin::MeasuredText* mt = toMeasuredParagraph(ptr);
|
||||
float r = 0.0f;
|
||||
for (int i = start; i < end; ++i) {
|
||||
r += mt->widths[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// CriticalNative
|
||||
static jlong nGetReleaseFunc() {
|
||||
return toJLong(&releaseMeasuredParagraph);
|
||||
@@ -108,10 +120,11 @@ static const JNINativeMethod gMethods[] = {
|
||||
{"nInitBuilder", "()J", (void*) nInitBuilder},
|
||||
{"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun},
|
||||
{"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun},
|
||||
{"nBuildNativeMeasuredParagraph", "(J[CZ)J", (void*) nBuildNativeMeasuredParagraph},
|
||||
{"nBuildNativeMeasuredParagraph", "(J[CZZ)J", (void*) nBuildNativeMeasuredParagraph},
|
||||
{"nFreeBuilder", "(J)V", (void*) nFreeBuilder},
|
||||
|
||||
// MeasuredParagraph native functions.
|
||||
{"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives
|
||||
{"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives
|
||||
};
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ public class MeasuredParagraphTest {
|
||||
public void buildForStaticLayout() {
|
||||
MeasuredParagraph mt = null;
|
||||
|
||||
mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, false, null);
|
||||
mt = MeasuredParagraph.buildForStaticLayout(PAINT, "XXX", 0, 3, LTR, false, false, null);
|
||||
assertNotNull(mt);
|
||||
assertNotNull(mt.getChars());
|
||||
assertEquals("XXX", charsToString(mt.getChars()));
|
||||
@@ -147,7 +147,7 @@ public class MeasuredParagraphTest {
|
||||
|
||||
// Recycle it
|
||||
MeasuredParagraph mt2 =
|
||||
MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, false, mt);
|
||||
MeasuredParagraph.buildForStaticLayout(PAINT, "_VVV_", 1, 4, RTL, false, false, mt);
|
||||
assertEquals(mt2, mt);
|
||||
assertNotNull(mt2.getChars());
|
||||
assertEquals("VVV", charsToString(mt.getChars()));
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.annotation.Nullable;
|
||||
import android.annotation.Size;
|
||||
import android.graphics.Canvas.VertexMode;
|
||||
import android.text.GraphicsOperations;
|
||||
import android.text.MeasuredParagraph;
|
||||
import android.text.MeasuredText;
|
||||
import android.text.SpannableString;
|
||||
import android.text.SpannedString;
|
||||
import android.text.TextUtils;
|
||||
@@ -453,7 +455,8 @@ public abstract class BaseCanvas {
|
||||
|
||||
throwIfHasHwBitmapInSwMode(paint);
|
||||
nDrawTextRun(mNativeCanvasWrapper, text, index, count, contextIndex, contextCount,
|
||||
x, y, isRtl, paint.getNativeInstance());
|
||||
x, y, isRtl, paint.getNativeInstance(), 0 /* measured text */,
|
||||
0 /* measured text offset */);
|
||||
}
|
||||
|
||||
public void drawTextRun(@NonNull CharSequence text, int start, int end, int contextStart,
|
||||
@@ -483,8 +486,20 @@ public abstract class BaseCanvas {
|
||||
int len = end - start;
|
||||
char[] buf = TemporaryBuffer.obtain(contextLen);
|
||||
TextUtils.getChars(text, contextStart, contextEnd, buf, 0);
|
||||
long measuredTextPtr = 0;
|
||||
int measuredTextOffset = 0;
|
||||
if (text instanceof MeasuredText) {
|
||||
MeasuredText mt = (MeasuredText) text;
|
||||
int paraIndex = mt.findParaIndex(start);
|
||||
if (end <= mt.getParagraphEnd(paraIndex)) {
|
||||
// Only suppor the same paragraph.
|
||||
measuredTextPtr = mt.getMeasuredParagraph(paraIndex).getNativePtr();
|
||||
measuredTextOffset = start - mt.getParagraphStart(paraIndex);
|
||||
}
|
||||
}
|
||||
nDrawTextRun(mNativeCanvasWrapper, buf, start - contextStart, len,
|
||||
0, contextLen, x, y, isRtl, paint.getNativeInstance());
|
||||
0, contextLen, x, y, isRtl, paint.getNativeInstance(),
|
||||
measuredTextPtr, measuredTextOffset);
|
||||
TemporaryBuffer.recycle(buf);
|
||||
}
|
||||
}
|
||||
@@ -623,7 +638,8 @@ public abstract class BaseCanvas {
|
||||
int contextStart, int contextEnd, float x, float y, boolean isRtl, long nativePaint);
|
||||
|
||||
private static native void nDrawTextRun(long nativeCanvas, char[] text, int start, int count,
|
||||
int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint);
|
||||
int contextStart, int contextCount, float x, float y, boolean isRtl, long nativePaint,
|
||||
long nativeMeasuredText, int measuredTextOffset);
|
||||
|
||||
private static native void nDrawTextOnPath(long nativeCanvas, char[] text, int index, int count,
|
||||
long nativePath, float hOffset, float vOffset, int bidiFlags, long nativePaint);
|
||||
|
||||
@@ -158,12 +158,13 @@ private:
|
||||
|
||||
void Canvas::drawText(const uint16_t* text, int start, int count, int contextCount, float x,
|
||||
float y, minikin::Bidi bidiFlags, const Paint& origPaint,
|
||||
const Typeface* typeface) {
|
||||
const Typeface* typeface, minikin::MeasuredText* mt, int mtOffset) {
|
||||
// minikin may modify the original paint
|
||||
Paint paint(origPaint);
|
||||
|
||||
minikin::Layout layout =
|
||||
MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, start, count, contextCount);
|
||||
MinikinUtils::doLayout(&paint, bidiFlags, typeface, text, start, count, contextCount,
|
||||
mt, mtOffset);
|
||||
|
||||
x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
|
||||
|
||||
@@ -211,7 +212,8 @@ void Canvas::drawTextOnPath(const uint16_t* text, int count, minikin::Bidi bidiF
|
||||
const Typeface* typeface) {
|
||||
Paint paintCopy(paint);
|
||||
minikin::Layout layout =
|
||||
MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface, text, 0, count, count);
|
||||
MinikinUtils::doLayout(&paintCopy, bidiFlags, typeface, text, 0, count, count, nullptr,
|
||||
0);
|
||||
hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
|
||||
|
||||
// Set align to left for drawing, as we don't want individual
|
||||
|
||||
@@ -34,6 +34,7 @@ class SkVertices;
|
||||
|
||||
namespace minikin {
|
||||
class Layout;
|
||||
class MeasuredText;
|
||||
enum class Bidi : uint8_t;
|
||||
}
|
||||
|
||||
@@ -260,7 +261,8 @@ public:
|
||||
* and delegating the final draw to virtual drawGlyphs method.
|
||||
*/
|
||||
void drawText(const uint16_t* text, int start, int count, int contextCount, float x, float y,
|
||||
minikin::Bidi bidiFlags, const Paint& origPaint, const Typeface* typeface);
|
||||
minikin::Bidi bidiFlags, const Paint& origPaint, const Typeface* typeface,
|
||||
minikin::MeasuredText* mt, int mtOffset);
|
||||
|
||||
void drawTextOnPath(const uint16_t* text, int count, minikin::Bidi bidiFlags,
|
||||
const SkPath& path, float hOffset, float vOffset, const Paint& paint,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <log/log.h>
|
||||
|
||||
#include <minikin/MeasuredText.h>
|
||||
#include "Paint.h"
|
||||
#include "SkPathMeasure.h"
|
||||
#include "Typeface.h"
|
||||
@@ -49,11 +50,24 @@ minikin::MinikinPaint MinikinUtils::prepareMinikinPaint(const Paint* paint,
|
||||
|
||||
minikin::Layout MinikinUtils::doLayout(const Paint* paint, minikin::Bidi bidiFlags,
|
||||
const Typeface* typeface, const uint16_t* buf, size_t start,
|
||||
size_t count, size_t bufSize) {
|
||||
size_t count, size_t bufSize, minikin::MeasuredText* mt,
|
||||
int mtOffset) {
|
||||
minikin::MinikinPaint minikinPaint = prepareMinikinPaint(paint, typeface);
|
||||
const auto& fc = Typeface::resolveDefault(typeface)->fFontCollection;
|
||||
minikin::Layout layout;
|
||||
layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint,
|
||||
Typeface::resolveDefault(typeface)->fFontCollection);
|
||||
|
||||
if (mt == nullptr) {
|
||||
layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint, fc);
|
||||
return layout;
|
||||
}
|
||||
|
||||
if (mt->buildLayout(minikin::U16StringPiece(buf, bufSize),
|
||||
minikin::Range(start, start + count),
|
||||
minikinPaint, fc, bidiFlags, mtOffset, &layout)) {
|
||||
return layout;
|
||||
}
|
||||
|
||||
layout.doLayout(buf, start, count, bufSize, bidiFlags, minikinPaint, fc);
|
||||
return layout;
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
#include "MinikinSkia.h"
|
||||
#include "Paint.h"
|
||||
#include "Typeface.h"
|
||||
#include <log/log.h>
|
||||
|
||||
namespace minikin {
|
||||
class MeasuredText;
|
||||
} // namespace minikin
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -39,7 +44,8 @@ public:
|
||||
|
||||
ANDROID_API static minikin::Layout doLayout(const Paint* paint, minikin::Bidi bidiFlags,
|
||||
const Typeface* typeface, const uint16_t* buf,
|
||||
size_t start, size_t count, size_t bufSize);
|
||||
size_t start, size_t count, size_t bufSize,
|
||||
minikin::MeasuredText* mt, int mtOffset);
|
||||
|
||||
ANDROID_API static float measureText(const Paint* paint, minikin::Bidi bidiFlags,
|
||||
const Typeface* typeface, const uint16_t* buf,
|
||||
|
||||
@@ -125,7 +125,7 @@ void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint
|
||||
SkPaint glyphPaint(paint);
|
||||
glyphPaint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
|
||||
canvas->drawText(utf16.get(), 0, strlen(text), strlen(text), x, y, minikin::Bidi::LTR,
|
||||
glyphPaint, nullptr);
|
||||
glyphPaint, nullptr, nullptr /* measured text */, 0 /* measured text offset */);
|
||||
}
|
||||
|
||||
void TestUtils::drawUtf8ToCanvas(Canvas* canvas, const char* text, const SkPaint& paint,
|
||||
|
||||
Reference in New Issue
Block a user