Loading core/java/android/text/NativeLineBreaker.java +108 −37 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; Loading Loading @@ -258,16 +259,91 @@ public class NativeLineBreaker { /** * A result object of a line breaking */ public static class LineBreaks { public int breakCount; private static final int INITIAL_SIZE = 16; public int[] breaks = new int[INITIAL_SIZE]; public float[] widths = new float[INITIAL_SIZE]; public float[] ascents = new float[INITIAL_SIZE]; public float[] descents = new float[INITIAL_SIZE]; // TODO: Introduce Hyphenator for explaining the meaning of flags. public int[] flags = new int[INITIAL_SIZE]; // breaks, widths, and flags should all have the same length public static class Result { // Following two contstant must be synced with minikin's line breaker. private static final int TAB_MASK = 0x20000000; private static final int HYPHEN_MASK = 0xFF; private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( Result.class.getClassLoader(), nGetReleaseResultFunc(), 32); private final long mPtr; private Result(long ptr) { mPtr = ptr; sRegistry.registerNativeAllocation(this, mPtr); } /** * Returns a number of line count. * * @return number of lines */ public @IntRange(from = 0) int getLineCount() { return nGetLineCount(mPtr); } /** * Returns a break offset of the line. * * @param lineIndex an index of the line. * @return the break offset. */ public @IntRange(from = 0) int getLineBreakOffset(@IntRange(from = 0) int lineIndex) { return nGetLineBreakOffset(mPtr, lineIndex); } /** * Returns a width of the line in pixels. * * @param lineIndex an index of the line. * @return a width of the line in pixexls */ public @Px float getLineWidth(@IntRange(from = 0) int lineIndex) { return nGetLineWidth(mPtr, lineIndex); } /** * Returns an entier font ascent of the line in pixels. * * @param lineIndex an index of the line. * @return an entier font ascent of the line in pixels. */ public @Px float getLineAscent(@IntRange(from = 0) int lineIndex) { return nGetLineAscent(mPtr, lineIndex); } /** * Returns an entier font descent of the line in pixels. * * @param lineIndex an index of the line. * @return an entier font descent of the line in pixels. */ public @Px float getLineDescent(@IntRange(from = 0) int lineIndex) { return nGetLineDescent(mPtr, lineIndex); } /** * Returns true if the line has a TAB character. * * @param lineIndex an index of the line. * @return true if the line has a TAB character */ public boolean hasLineTab(int lineIndex) { return (nGetLineFlag(mPtr, lineIndex) & TAB_MASK) != 0; } /** * Returns a packed packed hyphen edit for the line. * * @param lineIndex an index of the line. * @return a packed hyphen edit for the line. * @see android.text.Hyphenator#unpackStartHyphenEdit(int) * @see android.text.Hyphenator#unpackEndHyphenEdit(int) * @see android.text.Hyphenator#packHyphenEdit(int,int) */ public int getLineHyphenEdit(int lineIndex) { return (nGetLineFlag(mPtr, lineIndex) & HYPHEN_MASK); } } private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( Loading @@ -294,14 +370,12 @@ public class NativeLineBreaker { * @param measuredPara a result of the text measurement * @param constraints for a single paragraph * @param lineNumber a line number of this paragraph * @param out object to set line break information for the given paragraph */ public void computeLineBreaks( public Result computeLineBreaks( @NonNull NativeMeasuredParagraph measuredPara, @NonNull ParagraphConstraints constraints, @IntRange(from = 0) int lineNumber, @NonNull LineBreaks out) { out.breakCount = nComputeLineBreaks( @IntRange(from = 0) int lineNumber) { return new Result(nComputeLineBreaks( mNativePtr, // Inputs Loading @@ -313,17 +387,7 @@ public class NativeLineBreaker { constraints.mWidth, constraints.mVariableTabStops, constraints.mDefaultTabStop, lineNumber, // Outputs out, out.breaks.length, out.breaks, out.widths, out.ascents, out.descents, out.flags); lineNumber)); } @FastNative Loading @@ -341,7 +405,7 @@ public class NativeLineBreaker { // arrays do not have to be resized // The individual character widths will be returned in charWidths. The length of // charWidths must be at least the length of the text. private static native int nComputeLineBreaks( private static native long nComputeLineBreaks( /* non zero */ long nativePtr, // Inputs Loading @@ -353,14 +417,21 @@ public class NativeLineBreaker { @FloatRange(from = 0.0f) float restWidth, @Nullable int[] variableTabStops, int defaultTabStop, @IntRange(from = 0) int indentsOffset, // Outputs @NonNull LineBreaks recycle, @IntRange(from = 0) int recycleLength, @NonNull int[] recycleBreaks, @NonNull float[] recycleWidths, @NonNull float[] recycleAscents, @NonNull float[] recycleDescents, @NonNull int[] recycleFlags); @IntRange(from = 0) int indentsOffset); // Result accessors @CriticalNative private static native int nGetLineCount(long ptr); @CriticalNative private static native int nGetLineBreakOffset(long ptr, int idx); @CriticalNative private static native float nGetLineWidth(long ptr, int idx); @CriticalNative private static native float nGetLineAscent(long ptr, int idx); @CriticalNative private static native float nGetLineDescent(long ptr, int idx); @CriticalNative private static native int nGetLineFlag(long ptr, int idx); @CriticalNative private static native long nGetReleaseResultFunc(); } core/java/android/text/StaticLayout.java +39 −18 Original line number Diff line number Diff line Loading @@ -599,7 +599,14 @@ public class StaticLayout extends Layout { float ellipsizedWidth = b.mEllipsizedWidth; TextUtils.TruncateAt ellipsize = b.mEllipsize; final boolean addLastLineSpacing = b.mAddLastLineLineSpacing; NativeLineBreaker.LineBreaks lineBreaks = new NativeLineBreaker.LineBreaks(); int lineBreakCapacity = 0; int[] breaks = null; float[] lineWidths = null; float[] ascents = null; float[] descents = null; boolean[] hasTabs = null; int[] hyphenEdits = null; mLineCount = 0; mEllipsized = false; Loading Loading @@ -732,14 +739,27 @@ public class StaticLayout extends Layout { constraints.setIndent(firstWidth, firstWidthLineCount); constraints.setTabStops(variableTabStops, TAB_INCREMENT); lineBreaker.computeLineBreaks(measuredPara.getNativeMeasuredParagraph(), constraints, mLineCount, lineBreaks); int breakCount = lineBreaks.breakCount; final int[] breaks = lineBreaks.breaks; final float[] lineWidths = lineBreaks.widths; final float[] ascents = lineBreaks.ascents; final float[] descents = lineBreaks.descents; final int[] flags = lineBreaks.flags; NativeLineBreaker.Result res = lineBreaker.computeLineBreaks( measuredPara.getNativeMeasuredParagraph(), constraints, mLineCount); int breakCount = res.getLineCount(); if (lineBreakCapacity < breakCount) { lineBreakCapacity = breakCount; breaks = new int[lineBreakCapacity]; lineWidths = new float[lineBreakCapacity]; ascents = new float[lineBreakCapacity]; descents = new float[lineBreakCapacity]; hasTabs = new boolean[lineBreakCapacity]; hyphenEdits = new int[lineBreakCapacity]; } for (int i = 0; i < breakCount; ++i) { breaks[i] = res.getLineBreakOffset(i); lineWidths[i] = res.getLineWidth(i); ascents[i] = res.getLineAscent(i); descents[i] = res.getLineDescent(i); hasTabs[i] = res.hasLineTab(i); hyphenEdits[i] = res.getLineHyphenEdit(i); } final int remainingLineCount = mMaximumVisibleLineCount - mLineCount; final boolean ellipsisMayBeApplied = ellipsize != null Loading @@ -750,7 +770,7 @@ public class StaticLayout extends Layout { && ellipsisMayBeApplied) { // Calculate width float width = 0; int flag = 0; // XXX May need to also have starting hyphen edit boolean hasTab = false; // XXX May need to also have starting hyphen edit for (int i = remainingLineCount - 1; i < breakCount; i++) { if (i == breakCount - 1) { width += lineWidths[i]; Loading @@ -759,12 +779,12 @@ public class StaticLayout extends Layout { width += measuredPara.getCharWidthAt(j - paraStart); } } flag |= flags[i] & TAB_MASK; hasTab |= hasTabs[i]; } // Treat the last line and overflowed lines as a single line. breaks[remainingLineCount - 1] = breaks[breakCount - 1]; lineWidths[remainingLineCount - 1] = width; flags[remainingLineCount - 1] = flag; hasTabs[remainingLineCount - 1] = hasTab; breakCount = remainingLineCount; } Loading Loading @@ -821,8 +841,8 @@ public class StaticLayout extends Layout { v = out(source, here, endPos, ascent, descent, fmTop, fmBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex], needMultiply, measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, chs, hasTabs[breakIndex], hyphenEdits[breakIndex], needMultiply, measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, chs, paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, moreChars); Loading Loading @@ -860,7 +880,7 @@ public class StaticLayout extends Layout { fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, 0, null, fm, false, 0, needMultiply, measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, null, bufStart, ellipsize, Loading @@ -871,7 +891,8 @@ public class StaticLayout extends Layout { private int out(final CharSequence text, final int start, final int end, int above, int below, int top, int bottom, int v, final float spacingmult, final float spacingadd, final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm, final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured, final boolean hasTab, final int hyphenEdit, final boolean needMultiply, @NonNull final MeasuredParagraph measured, final int bufEnd, final boolean includePad, final boolean trackPad, final boolean addLastLineLineSpacing, final char[] chs, final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth, Loading Loading @@ -1005,8 +1026,8 @@ public class StaticLayout extends Layout { // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining // one bit for start field lines[off + TAB] |= flags & TAB_MASK; lines[off + HYPHEN] = flags; lines[off + TAB] |= hasTab ? TAB_MASK : 0; lines[off + HYPHEN] = hyphenEdit; lines[off + DIR] |= dir << DIR_SHIFT; mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart); Loading core/jni/android_text_LineBreaker.cpp +50 −82 Original line number Diff line number Diff line Loading @@ -41,17 +41,6 @@ namespace android { struct JLineBreaksID { jfieldID breaks; jfieldID widths; jfieldID ascents; jfieldID descents; jfieldID flags; }; static jclass gLineBreaks_class; static JLineBreaksID gLineBreaks_fieldID; static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) { if (javaArray == nullptr) { return std::vector<float>(); Loading Loading @@ -85,34 +74,7 @@ static jlong nGetReleaseFunc() { return reinterpret_cast<jlong>(nFinish); } static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, jfloatArray recycleWidths, jfloatArray recycleAscents, jfloatArray recycleDescents, jintArray recycleFlags, jint recycleLength, const minikin::LineBreakResult& result) { const size_t nBreaks = result.breakPoints.size(); if ((size_t)recycleLength < nBreaks) { // have to reallocate buffers recycleBreaks = env->NewIntArray(nBreaks); recycleWidths = env->NewFloatArray(nBreaks); recycleAscents = env->NewFloatArray(nBreaks); recycleDescents = env->NewFloatArray(nBreaks); recycleFlags = env->NewIntArray(nBreaks); env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks); env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths); env->SetObjectField(recycle, gLineBreaks_fieldID.ascents, recycleAscents); env->SetObjectField(recycle, gLineBreaks_fieldID.descents, recycleDescents); env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags); } // copy data env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, result.breakPoints.data()); env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, result.widths.data()); env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, result.ascents.data()); env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, result.descents.data()); env->SetIntArrayRegion(recycleFlags, 0, nBreaks, result.flags.data()); } static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, static jlong nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, // Inputs jcharArray javaText, jlong measuredTextPtr, Loading @@ -122,18 +84,7 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, jfloat restWidth, jintArray variableTabStops, jint defaultTabStop, jint indentsOffset, // Outputs jobject recycle, jint recycleLength, jintArray recycleBreaks, jfloatArray recycleWidths, jfloatArray recycleAscents, jfloatArray recycleDescents, jintArray recycleFlags, jfloatArray charWidths) { jint indentsOffset) { minikin::android::StaticLayoutNative* builder = toNative(nativePtr); ScopedCharArrayRO text(env, javaText); Loading @@ -141,14 +92,44 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, minikin::U16StringPiece u16Text(text.get(), length); minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr); minikin::LineBreakResult result = builder->computeBreaks( std::unique_ptr<minikin::LineBreakResult> result = std::make_unique<minikin::LineBreakResult>(builder->computeBreaks( u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset, tabStops.get(), tabStops.size(), defaultTabStop); tabStops.get(), tabStops.size(), defaultTabStop)); return reinterpret_cast<jlong>(result.release()); } static jint nGetLineCount(jlong ptr) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints.size(); } static jint nGetLineBreakOffset(jlong ptr, jint i) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints[i]; } static jfloat nGetLineWidth(jlong ptr, jint i) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->widths[i]; } recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents, recycleFlags, recycleLength, result); static jfloat nGetLineAscent(jlong ptr, jint i) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->ascents[i]; } static jfloat nGetLineDescent(jlong ptr, jint i) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->descents[i]; } static jint nGetLineFlag(jlong ptr, jint i) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->flags[i]; } static void nReleaseResult(jlong ptr) { delete reinterpret_cast<minikin::LineBreakResult*>(ptr); } return static_cast<jint>(result.breakPoints.size()); static jlong nGetReleaseResultFunc() { return reinterpret_cast<jlong>(nReleaseResult); } static const JNINativeMethod gMethods[] = { Loading @@ -166,8 +147,6 @@ static const JNINativeMethod gMethods[] = { // Regular JNI {"nComputeLineBreaks", "(" "J" // nativePtr // Inputs "[C" // text "J" // MeasuredParagraph ptr. "I" // length Loading @@ -177,31 +156,20 @@ static const JNINativeMethod gMethods[] = { "[I" // variableTabStops "I" // defaultTabStop "I" // indentsOffset // Outputs "Landroid/text/NativeLineBreaker$LineBreaks;" // recycle "I" // recycleLength "[I" // recycleBreaks "[F" // recycleWidths "[F" // recycleAscents "[F" // recycleDescents "[I" // recycleFlags ")I", (void*) nComputeLineBreaks} ")J", (void*) nComputeLineBreaks}, // Result accessors, CriticalNatives {"nGetLineCount", "(J)I", (void*)nGetLineCount}, {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset}, {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth}, {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent}, {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent}, {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag}, {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc}, }; int register_android_text_LineBreaker(JNIEnv* env) { gLineBreaks_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/text/NativeLineBreaker$LineBreaks")); gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I"); gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F"); gLineBreaks_fieldID.ascents = GetFieldIDOrDie(env, gLineBreaks_class, "ascents", "[F"); gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F"); gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I"); return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", gMethods, NELEM(gMethods)); int register_android_text_LineBreaker(JNIEnv* env) { return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", gMethods, NELEM(gMethods)); } } Loading
core/java/android/text/NativeLineBreaker.java +108 −37 Original line number Diff line number Diff line Loading @@ -21,6 +21,7 @@ import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.Px; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; Loading Loading @@ -258,16 +259,91 @@ public class NativeLineBreaker { /** * A result object of a line breaking */ public static class LineBreaks { public int breakCount; private static final int INITIAL_SIZE = 16; public int[] breaks = new int[INITIAL_SIZE]; public float[] widths = new float[INITIAL_SIZE]; public float[] ascents = new float[INITIAL_SIZE]; public float[] descents = new float[INITIAL_SIZE]; // TODO: Introduce Hyphenator for explaining the meaning of flags. public int[] flags = new int[INITIAL_SIZE]; // breaks, widths, and flags should all have the same length public static class Result { // Following two contstant must be synced with minikin's line breaker. private static final int TAB_MASK = 0x20000000; private static final int HYPHEN_MASK = 0xFF; private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( Result.class.getClassLoader(), nGetReleaseResultFunc(), 32); private final long mPtr; private Result(long ptr) { mPtr = ptr; sRegistry.registerNativeAllocation(this, mPtr); } /** * Returns a number of line count. * * @return number of lines */ public @IntRange(from = 0) int getLineCount() { return nGetLineCount(mPtr); } /** * Returns a break offset of the line. * * @param lineIndex an index of the line. * @return the break offset. */ public @IntRange(from = 0) int getLineBreakOffset(@IntRange(from = 0) int lineIndex) { return nGetLineBreakOffset(mPtr, lineIndex); } /** * Returns a width of the line in pixels. * * @param lineIndex an index of the line. * @return a width of the line in pixexls */ public @Px float getLineWidth(@IntRange(from = 0) int lineIndex) { return nGetLineWidth(mPtr, lineIndex); } /** * Returns an entier font ascent of the line in pixels. * * @param lineIndex an index of the line. * @return an entier font ascent of the line in pixels. */ public @Px float getLineAscent(@IntRange(from = 0) int lineIndex) { return nGetLineAscent(mPtr, lineIndex); } /** * Returns an entier font descent of the line in pixels. * * @param lineIndex an index of the line. * @return an entier font descent of the line in pixels. */ public @Px float getLineDescent(@IntRange(from = 0) int lineIndex) { return nGetLineDescent(mPtr, lineIndex); } /** * Returns true if the line has a TAB character. * * @param lineIndex an index of the line. * @return true if the line has a TAB character */ public boolean hasLineTab(int lineIndex) { return (nGetLineFlag(mPtr, lineIndex) & TAB_MASK) != 0; } /** * Returns a packed packed hyphen edit for the line. * * @param lineIndex an index of the line. * @return a packed hyphen edit for the line. * @see android.text.Hyphenator#unpackStartHyphenEdit(int) * @see android.text.Hyphenator#unpackEndHyphenEdit(int) * @see android.text.Hyphenator#packHyphenEdit(int,int) */ public int getLineHyphenEdit(int lineIndex) { return (nGetLineFlag(mPtr, lineIndex) & HYPHEN_MASK); } } private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( Loading @@ -294,14 +370,12 @@ public class NativeLineBreaker { * @param measuredPara a result of the text measurement * @param constraints for a single paragraph * @param lineNumber a line number of this paragraph * @param out object to set line break information for the given paragraph */ public void computeLineBreaks( public Result computeLineBreaks( @NonNull NativeMeasuredParagraph measuredPara, @NonNull ParagraphConstraints constraints, @IntRange(from = 0) int lineNumber, @NonNull LineBreaks out) { out.breakCount = nComputeLineBreaks( @IntRange(from = 0) int lineNumber) { return new Result(nComputeLineBreaks( mNativePtr, // Inputs Loading @@ -313,17 +387,7 @@ public class NativeLineBreaker { constraints.mWidth, constraints.mVariableTabStops, constraints.mDefaultTabStop, lineNumber, // Outputs out, out.breaks.length, out.breaks, out.widths, out.ascents, out.descents, out.flags); lineNumber)); } @FastNative Loading @@ -341,7 +405,7 @@ public class NativeLineBreaker { // arrays do not have to be resized // The individual character widths will be returned in charWidths. The length of // charWidths must be at least the length of the text. private static native int nComputeLineBreaks( private static native long nComputeLineBreaks( /* non zero */ long nativePtr, // Inputs Loading @@ -353,14 +417,21 @@ public class NativeLineBreaker { @FloatRange(from = 0.0f) float restWidth, @Nullable int[] variableTabStops, int defaultTabStop, @IntRange(from = 0) int indentsOffset, // Outputs @NonNull LineBreaks recycle, @IntRange(from = 0) int recycleLength, @NonNull int[] recycleBreaks, @NonNull float[] recycleWidths, @NonNull float[] recycleAscents, @NonNull float[] recycleDescents, @NonNull int[] recycleFlags); @IntRange(from = 0) int indentsOffset); // Result accessors @CriticalNative private static native int nGetLineCount(long ptr); @CriticalNative private static native int nGetLineBreakOffset(long ptr, int idx); @CriticalNative private static native float nGetLineWidth(long ptr, int idx); @CriticalNative private static native float nGetLineAscent(long ptr, int idx); @CriticalNative private static native float nGetLineDescent(long ptr, int idx); @CriticalNative private static native int nGetLineFlag(long ptr, int idx); @CriticalNative private static native long nGetReleaseResultFunc(); }
core/java/android/text/StaticLayout.java +39 −18 Original line number Diff line number Diff line Loading @@ -599,7 +599,14 @@ public class StaticLayout extends Layout { float ellipsizedWidth = b.mEllipsizedWidth; TextUtils.TruncateAt ellipsize = b.mEllipsize; final boolean addLastLineSpacing = b.mAddLastLineLineSpacing; NativeLineBreaker.LineBreaks lineBreaks = new NativeLineBreaker.LineBreaks(); int lineBreakCapacity = 0; int[] breaks = null; float[] lineWidths = null; float[] ascents = null; float[] descents = null; boolean[] hasTabs = null; int[] hyphenEdits = null; mLineCount = 0; mEllipsized = false; Loading Loading @@ -732,14 +739,27 @@ public class StaticLayout extends Layout { constraints.setIndent(firstWidth, firstWidthLineCount); constraints.setTabStops(variableTabStops, TAB_INCREMENT); lineBreaker.computeLineBreaks(measuredPara.getNativeMeasuredParagraph(), constraints, mLineCount, lineBreaks); int breakCount = lineBreaks.breakCount; final int[] breaks = lineBreaks.breaks; final float[] lineWidths = lineBreaks.widths; final float[] ascents = lineBreaks.ascents; final float[] descents = lineBreaks.descents; final int[] flags = lineBreaks.flags; NativeLineBreaker.Result res = lineBreaker.computeLineBreaks( measuredPara.getNativeMeasuredParagraph(), constraints, mLineCount); int breakCount = res.getLineCount(); if (lineBreakCapacity < breakCount) { lineBreakCapacity = breakCount; breaks = new int[lineBreakCapacity]; lineWidths = new float[lineBreakCapacity]; ascents = new float[lineBreakCapacity]; descents = new float[lineBreakCapacity]; hasTabs = new boolean[lineBreakCapacity]; hyphenEdits = new int[lineBreakCapacity]; } for (int i = 0; i < breakCount; ++i) { breaks[i] = res.getLineBreakOffset(i); lineWidths[i] = res.getLineWidth(i); ascents[i] = res.getLineAscent(i); descents[i] = res.getLineDescent(i); hasTabs[i] = res.hasLineTab(i); hyphenEdits[i] = res.getLineHyphenEdit(i); } final int remainingLineCount = mMaximumVisibleLineCount - mLineCount; final boolean ellipsisMayBeApplied = ellipsize != null Loading @@ -750,7 +770,7 @@ public class StaticLayout extends Layout { && ellipsisMayBeApplied) { // Calculate width float width = 0; int flag = 0; // XXX May need to also have starting hyphen edit boolean hasTab = false; // XXX May need to also have starting hyphen edit for (int i = remainingLineCount - 1; i < breakCount; i++) { if (i == breakCount - 1) { width += lineWidths[i]; Loading @@ -759,12 +779,12 @@ public class StaticLayout extends Layout { width += measuredPara.getCharWidthAt(j - paraStart); } } flag |= flags[i] & TAB_MASK; hasTab |= hasTabs[i]; } // Treat the last line and overflowed lines as a single line. breaks[remainingLineCount - 1] = breaks[breakCount - 1]; lineWidths[remainingLineCount - 1] = width; flags[remainingLineCount - 1] = flag; hasTabs[remainingLineCount - 1] = hasTab; breakCount = remainingLineCount; } Loading Loading @@ -821,8 +841,8 @@ public class StaticLayout extends Layout { v = out(source, here, endPos, ascent, descent, fmTop, fmBottom, v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, flags[breakIndex], needMultiply, measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, chs, hasTabs[breakIndex], hyphenEdits[breakIndex], needMultiply, measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, chs, paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], paint, moreChars); Loading Loading @@ -860,7 +880,7 @@ public class StaticLayout extends Layout { fm.top, fm.bottom, v, spacingmult, spacingadd, null, null, fm, 0, null, fm, false, 0, needMultiply, measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, null, bufStart, ellipsize, Loading @@ -871,7 +891,8 @@ public class StaticLayout extends Layout { private int out(final CharSequence text, final int start, final int end, int above, int below, int top, int bottom, int v, final float spacingmult, final float spacingadd, final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm, final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured, final boolean hasTab, final int hyphenEdit, final boolean needMultiply, @NonNull final MeasuredParagraph measured, final int bufEnd, final boolean includePad, final boolean trackPad, final boolean addLastLineLineSpacing, final char[] chs, final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth, Loading Loading @@ -1005,8 +1026,8 @@ public class StaticLayout extends Layout { // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining // one bit for start field lines[off + TAB] |= flags & TAB_MASK; lines[off + HYPHEN] = flags; lines[off + TAB] |= hasTab ? TAB_MASK : 0; lines[off + HYPHEN] = hyphenEdit; lines[off + DIR] |= dir << DIR_SHIFT; mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart); Loading
core/jni/android_text_LineBreaker.cpp +50 −82 Original line number Diff line number Diff line Loading @@ -41,17 +41,6 @@ namespace android { struct JLineBreaksID { jfieldID breaks; jfieldID widths; jfieldID ascents; jfieldID descents; jfieldID flags; }; static jclass gLineBreaks_class; static JLineBreaksID gLineBreaks_fieldID; static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) { if (javaArray == nullptr) { return std::vector<float>(); Loading Loading @@ -85,34 +74,7 @@ static jlong nGetReleaseFunc() { return reinterpret_cast<jlong>(nFinish); } static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, jfloatArray recycleWidths, jfloatArray recycleAscents, jfloatArray recycleDescents, jintArray recycleFlags, jint recycleLength, const minikin::LineBreakResult& result) { const size_t nBreaks = result.breakPoints.size(); if ((size_t)recycleLength < nBreaks) { // have to reallocate buffers recycleBreaks = env->NewIntArray(nBreaks); recycleWidths = env->NewFloatArray(nBreaks); recycleAscents = env->NewFloatArray(nBreaks); recycleDescents = env->NewFloatArray(nBreaks); recycleFlags = env->NewIntArray(nBreaks); env->SetObjectField(recycle, gLineBreaks_fieldID.breaks, recycleBreaks); env->SetObjectField(recycle, gLineBreaks_fieldID.widths, recycleWidths); env->SetObjectField(recycle, gLineBreaks_fieldID.ascents, recycleAscents); env->SetObjectField(recycle, gLineBreaks_fieldID.descents, recycleDescents); env->SetObjectField(recycle, gLineBreaks_fieldID.flags, recycleFlags); } // copy data env->SetIntArrayRegion(recycleBreaks, 0, nBreaks, result.breakPoints.data()); env->SetFloatArrayRegion(recycleWidths, 0, nBreaks, result.widths.data()); env->SetFloatArrayRegion(recycleAscents, 0, nBreaks, result.ascents.data()); env->SetFloatArrayRegion(recycleDescents, 0, nBreaks, result.descents.data()); env->SetIntArrayRegion(recycleFlags, 0, nBreaks, result.flags.data()); } static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, static jlong nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, // Inputs jcharArray javaText, jlong measuredTextPtr, Loading @@ -122,18 +84,7 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, jfloat restWidth, jintArray variableTabStops, jint defaultTabStop, jint indentsOffset, // Outputs jobject recycle, jint recycleLength, jintArray recycleBreaks, jfloatArray recycleWidths, jfloatArray recycleAscents, jfloatArray recycleDescents, jintArray recycleFlags, jfloatArray charWidths) { jint indentsOffset) { minikin::android::StaticLayoutNative* builder = toNative(nativePtr); ScopedCharArrayRO text(env, javaText); Loading @@ -141,14 +92,44 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, minikin::U16StringPiece u16Text(text.get(), length); minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr); minikin::LineBreakResult result = builder->computeBreaks( std::unique_ptr<minikin::LineBreakResult> result = std::make_unique<minikin::LineBreakResult>(builder->computeBreaks( u16Text, *measuredText, firstWidth, firstWidthLineCount, restWidth, indentsOffset, tabStops.get(), tabStops.size(), defaultTabStop); tabStops.get(), tabStops.size(), defaultTabStop)); return reinterpret_cast<jlong>(result.release()); } static jint nGetLineCount(jlong ptr) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints.size(); } static jint nGetLineBreakOffset(jlong ptr, jint i) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->breakPoints[i]; } static jfloat nGetLineWidth(jlong ptr, jint i) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->widths[i]; } recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents, recycleFlags, recycleLength, result); static jfloat nGetLineAscent(jlong ptr, jint i) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->ascents[i]; } static jfloat nGetLineDescent(jlong ptr, jint i) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->descents[i]; } static jint nGetLineFlag(jlong ptr, jint i) { return reinterpret_cast<minikin::LineBreakResult*>(ptr)->flags[i]; } static void nReleaseResult(jlong ptr) { delete reinterpret_cast<minikin::LineBreakResult*>(ptr); } return static_cast<jint>(result.breakPoints.size()); static jlong nGetReleaseResultFunc() { return reinterpret_cast<jlong>(nReleaseResult); } static const JNINativeMethod gMethods[] = { Loading @@ -166,8 +147,6 @@ static const JNINativeMethod gMethods[] = { // Regular JNI {"nComputeLineBreaks", "(" "J" // nativePtr // Inputs "[C" // text "J" // MeasuredParagraph ptr. "I" // length Loading @@ -177,31 +156,20 @@ static const JNINativeMethod gMethods[] = { "[I" // variableTabStops "I" // defaultTabStop "I" // indentsOffset // Outputs "Landroid/text/NativeLineBreaker$LineBreaks;" // recycle "I" // recycleLength "[I" // recycleBreaks "[F" // recycleWidths "[F" // recycleAscents "[F" // recycleDescents "[I" // recycleFlags ")I", (void*) nComputeLineBreaks} ")J", (void*) nComputeLineBreaks}, // Result accessors, CriticalNatives {"nGetLineCount", "(J)I", (void*)nGetLineCount}, {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset}, {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth}, {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent}, {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent}, {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag}, {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc}, }; int register_android_text_LineBreaker(JNIEnv* env) { gLineBreaks_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/text/NativeLineBreaker$LineBreaks")); gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I"); gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F"); gLineBreaks_fieldID.ascents = GetFieldIDOrDie(env, gLineBreaks_class, "ascents", "[F"); gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F"); gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I"); return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", gMethods, NELEM(gMethods)); int register_android_text_LineBreaker(JNIEnv* env) { return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", gMethods, NELEM(gMethods)); } }