Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ab9b479d authored by Seigo Nonaka's avatar Seigo Nonaka
Browse files

Prepare NativeLineBreaker.LineBreaks to be public API

This is a ground work of making NativeLineBreaker/MeasuredParagraph
public.

This CL rename NativeLineBreaker.LineBreaks to NativeLineBreaker.Result.
This CL also moving array re-allocation logic from native code to Java in
StaticLayout. Ideally, we can remove array but this CL keeps array since
that introduces additional complexity.

Here is a raw perf test result:
android.text.StaticLayoutPerfTest:
  create
    PrecomputedText Balanced Hyphenation  :    667 ->    810: (+143, +21.4%)
    PrecomputedText Balanced NoHyphenation:    510 ->    594: ( +84, +16.5%)
    PrecomputedText Greedy Hyphenation    :    446 ->    530: ( +84, +18.8%)
    PrecomputedText Greedy NoHyphenation  :    448 ->    533: ( +85, +19.0%)
    RandomText Balanced Hyphenation       : 17,982 -> 18,176: (+194, +1.1%)
    RandomText Balanced NoHyphenation     :  7,348 ->  7,461: (+113, +1.5%)
    RandomText Greedy Hyphenation         :  7,293 ->  7,330: ( +37, +0.5%)
    RandomText Greedy NoHyphenation       :  7,289 ->  7,352: ( +63, +0.9%)
  draw
    PrecomputedText NoStyle               :    645 ->    645: (  +0, +0.0%)
    PrecomputedText Style                 :    941 ->    947: (  +6, +0.6%)
    RandomText NoStyle                    :    541 ->    543: (  +2, +0.4%)
    RandomText Style                      :    751 ->    753: (  +2, +0.3%)
android.widget.TextViewPrecomputedTextPerfTest:
  newLayout
    PrecomputedText                       :    839 ->    983: (+144, +17.2%)
    PrecomputedText Selectable            :    883 ->  1,053: (+170, +19.3%)
    RandomText                            : 17,594 -> 17,761: (+167, +0.9%)
    RandomText Selectable                 : 18,331 -> 18,695: (+364, +2.0%)
  onDraw
    PrecomputedText                       :  1,233 ->  1,385: (+152, +12.3%)
    PrecomputedText Selectable            :  1,164 ->  1,207: ( +43, +3.7%)
    RandomText                            : 18,836 -> 19,042: (+206, +1.1%)
    RandomText Selectable                 : 19,025 -> 19,092: ( +67, +0.4%)
  onMeasure
    PrecomputedText                       :    853 ->    991: (+138, +16.2%)
    PrecomputedText Selectable            :  1,077 ->  1,234: (+157, +14.6%)
    RandomText                            : 17,613 -> 17,913: (+300, +1.7%)
    RandomText Selectable                 : 18,696 -> 18,922: (+226, +1.2%)
  setText
    PrecomputedText                       :    133 ->    136: (  +3, +2.3%)
    PrecomputedText Selectable            :    190 ->    192: (  +2, +1.1%)
    RandomText                            :     10 ->     11: (  +1, +10.0%)
    RandomText Selectable                 :     49 ->     50: (  +1, +2.0%)

Bug: 112327179
Test: atest FrameworksCoreTests CtsGraphicsTestCases CtsWidgetTestCases
    CtsTextTestCases

Change-Id: I25f41dacf73e00a859f43331bc53f8d123116192
parent 912a1e9e
Loading
Loading
Loading
Loading
+108 −37
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Nullable;
import android.annotation.Px;


import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;
import dalvik.annotation.optimization.FastNative;
@@ -258,16 +259,91 @@ public class NativeLineBreaker {
    /**
    /**
     * A result object of a line breaking
     * A result object of a line breaking
     */
     */
    public static class LineBreaks {
    public static class Result {
        public int breakCount;
        // Following two contstant must be synced with minikin's line breaker.
        private static final int INITIAL_SIZE = 16;
        private static final int TAB_MASK = 0x20000000;
        public int[] breaks = new int[INITIAL_SIZE];
        private static final int HYPHEN_MASK = 0xFF;
        public float[] widths = new float[INITIAL_SIZE];

        public float[] ascents = new float[INITIAL_SIZE];
        private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
        public float[] descents = new float[INITIAL_SIZE];
                Result.class.getClassLoader(), nGetReleaseResultFunc(), 32);
        // TODO: Introduce Hyphenator for explaining the meaning of flags.
        private final long mPtr;
        public int[] flags = new int[INITIAL_SIZE];

        // breaks, widths, and flags should all have the same length
        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(
    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
@@ -294,14 +370,12 @@ public class NativeLineBreaker {
     * @param measuredPara a result of the text measurement
     * @param measuredPara a result of the text measurement
     * @param constraints for a single paragraph
     * @param constraints for a single paragraph
     * @param lineNumber a line number of this 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 NativeMeasuredParagraph measuredPara,
            @NonNull ParagraphConstraints constraints,
            @NonNull ParagraphConstraints constraints,
            @IntRange(from = 0) int lineNumber,
            @IntRange(from = 0) int lineNumber) {
            @NonNull LineBreaks out) {
        return new Result(nComputeLineBreaks(
        out.breakCount = nComputeLineBreaks(
                mNativePtr,
                mNativePtr,


                // Inputs
                // Inputs
@@ -313,17 +387,7 @@ public class NativeLineBreaker {
                constraints.mWidth,
                constraints.mWidth,
                constraints.mVariableTabStops,
                constraints.mVariableTabStops,
                constraints.mDefaultTabStop,
                constraints.mDefaultTabStop,
                lineNumber,
                lineNumber));

                // Outputs
                out,
                out.breaks.length,
                out.breaks,
                out.widths,
                out.ascents,
                out.descents,
                out.flags);

    }
    }


    @FastNative
    @FastNative
@@ -341,7 +405,7 @@ public class NativeLineBreaker {
    // arrays do not have to be resized
    // arrays do not have to be resized
    // The individual character widths will be returned in charWidths. The length of
    // The individual character widths will be returned in charWidths. The length of
    // charWidths must be at least the length of the text.
    // charWidths must be at least the length of the text.
    private static native int nComputeLineBreaks(
    private static native long nComputeLineBreaks(
            /* non zero */ long nativePtr,
            /* non zero */ long nativePtr,


            // Inputs
            // Inputs
@@ -353,14 +417,21 @@ public class NativeLineBreaker {
            @FloatRange(from = 0.0f) float restWidth,
            @FloatRange(from = 0.0f) float restWidth,
            @Nullable int[] variableTabStops,
            @Nullable int[] variableTabStops,
            int defaultTabStop,
            int defaultTabStop,
            @IntRange(from = 0) int indentsOffset,
            @IntRange(from = 0) int indentsOffset);


            // Outputs
    // Result accessors
            @NonNull LineBreaks recycle,
    @CriticalNative
            @IntRange(from  = 0) int recycleLength,
    private static native int nGetLineCount(long ptr);
            @NonNull int[] recycleBreaks,
    @CriticalNative
            @NonNull float[] recycleWidths,
    private static native int nGetLineBreakOffset(long ptr, int idx);
            @NonNull float[] recycleAscents,
    @CriticalNative
            @NonNull float[] recycleDescents,
    private static native float nGetLineWidth(long ptr, int idx);
            @NonNull int[] recycleFlags);
    @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();
}
}
+39 −18
Original line number Original line Diff line number Diff line
@@ -599,7 +599,14 @@ public class StaticLayout extends Layout {
        float ellipsizedWidth = b.mEllipsizedWidth;
        float ellipsizedWidth = b.mEllipsizedWidth;
        TextUtils.TruncateAt ellipsize = b.mEllipsize;
        TextUtils.TruncateAt ellipsize = b.mEllipsize;
        final boolean addLastLineSpacing = b.mAddLastLineLineSpacing;
        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;
        mLineCount = 0;
        mEllipsized = false;
        mEllipsized = false;
@@ -732,14 +739,27 @@ public class StaticLayout extends Layout {
            constraints.setIndent(firstWidth, firstWidthLineCount);
            constraints.setIndent(firstWidth, firstWidthLineCount);
            constraints.setTabStops(variableTabStops, TAB_INCREMENT);
            constraints.setTabStops(variableTabStops, TAB_INCREMENT);


            lineBreaker.computeLineBreaks(measuredPara.getNativeMeasuredParagraph(),
            NativeLineBreaker.Result res = lineBreaker.computeLineBreaks(
                    constraints, mLineCount, lineBreaks);
                    measuredPara.getNativeMeasuredParagraph(), constraints, mLineCount);
            int breakCount = lineBreaks.breakCount;
            int breakCount = res.getLineCount();
            final int[] breaks = lineBreaks.breaks;
            if (lineBreakCapacity < breakCount) {
            final float[] lineWidths = lineBreaks.widths;
                lineBreakCapacity = breakCount;
            final float[] ascents = lineBreaks.ascents;
                breaks = new int[lineBreakCapacity];
            final float[] descents = lineBreaks.descents;
                lineWidths = new float[lineBreakCapacity];
            final int[] flags = lineBreaks.flags;
                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 int remainingLineCount = mMaximumVisibleLineCount - mLineCount;
            final boolean ellipsisMayBeApplied = ellipsize != null
            final boolean ellipsisMayBeApplied = ellipsize != null
@@ -750,7 +770,7 @@ public class StaticLayout extends Layout {
                    && ellipsisMayBeApplied) {
                    && ellipsisMayBeApplied) {
                // Calculate width
                // Calculate width
                float width = 0;
                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++) {
                for (int i = remainingLineCount - 1; i < breakCount; i++) {
                    if (i == breakCount - 1) {
                    if (i == breakCount - 1) {
                        width += lineWidths[i];
                        width += lineWidths[i];
@@ -759,12 +779,12 @@ public class StaticLayout extends Layout {
                            width += measuredPara.getCharWidthAt(j - paraStart);
                            width += measuredPara.getCharWidthAt(j - paraStart);
                        }
                        }
                    }
                    }
                    flag |= flags[i] & TAB_MASK;
                    hasTab |= hasTabs[i];
                }
                }
                // Treat the last line and overflowed lines as a single line.
                // Treat the last line and overflowed lines as a single line.
                breaks[remainingLineCount - 1] = breaks[breakCount - 1];
                breaks[remainingLineCount - 1] = breaks[breakCount - 1];
                lineWidths[remainingLineCount - 1] = width;
                lineWidths[remainingLineCount - 1] = width;
                flags[remainingLineCount - 1] = flag;
                hasTabs[remainingLineCount - 1] = hasTab;


                breakCount = remainingLineCount;
                breakCount = remainingLineCount;
            }
            }
@@ -821,8 +841,8 @@ public class StaticLayout extends Layout {
                    v = out(source, here, endPos,
                    v = out(source, here, endPos,
                            ascent, descent, fmTop, fmBottom,
                            ascent, descent, fmTop, fmBottom,
                            v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
                            v, spacingmult, spacingadd, chooseHt, chooseHtv, fm,
                            flags[breakIndex], needMultiply, measuredPara, bufEnd,
                            hasTabs[breakIndex], hyphenEdits[breakIndex], needMultiply,
                            includepad, trackpad, addLastLineSpacing, chs,
                            measuredPara, bufEnd, includepad, trackpad, addLastLineSpacing, chs,
                            paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
                            paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex],
                            paint, moreChars);
                            paint, moreChars);


@@ -860,7 +880,7 @@ public class StaticLayout extends Layout {
                    fm.top, fm.bottom,
                    fm.top, fm.bottom,
                    v,
                    v,
                    spacingmult, spacingadd, null,
                    spacingmult, spacingadd, null,
                    null, fm, 0,
                    null, fm, false, 0,
                    needMultiply, measuredPara, bufEnd,
                    needMultiply, measuredPara, bufEnd,
                    includepad, trackpad, addLastLineSpacing, null,
                    includepad, trackpad, addLastLineSpacing, null,
                    bufStart, ellipsize,
                    bufStart, ellipsize,
@@ -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,
    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,
            int top, int bottom, int v, final float spacingmult, final float spacingadd,
            final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm,
            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 int bufEnd, final boolean includePad, final boolean trackPad,
            final boolean addLastLineLineSpacing, final char[] chs,
            final boolean addLastLineLineSpacing, final char[] chs,
            final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
            final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth,
@@ -1005,8 +1026,8 @@ public class StaticLayout extends Layout {


        // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
        // TODO: could move TAB to share same column as HYPHEN, simplifying this code and gaining
        // one bit for start field
        // one bit for start field
        lines[off + TAB] |= flags & TAB_MASK;
        lines[off + TAB] |= hasTab ? TAB_MASK : 0;
        lines[off + HYPHEN] = flags;
        lines[off + HYPHEN] = hyphenEdit;
        lines[off + DIR] |= dir << DIR_SHIFT;
        lines[off + DIR] |= dir << DIR_SHIFT;
        mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);
        mLineDirections[j] = measured.getDirections(start - widthStart, end - widthStart);


+50 −82
Original line number Original line Diff line number Diff line
@@ -41,17 +41,6 @@


namespace android {
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) {
static inline std::vector<float> jintArrayToFloatVector(JNIEnv* env, jintArray javaArray) {
    if (javaArray == nullptr) {
    if (javaArray == nullptr) {
         return std::vector<float>();
         return std::vector<float>();
@@ -85,34 +74,7 @@ static jlong nGetReleaseFunc() {
    return reinterpret_cast<jlong>(nFinish);
    return reinterpret_cast<jlong>(nFinish);
}
}


static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
static jlong nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
                        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,
        // Inputs
        // Inputs
        jcharArray javaText,
        jcharArray javaText,
        jlong measuredTextPtr,
        jlong measuredTextPtr,
@@ -122,18 +84,7 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,
        jfloat restWidth,
        jfloat restWidth,
        jintArray variableTabStops,
        jintArray variableTabStops,
        jint defaultTabStop,
        jint defaultTabStop,
        jint indentsOffset,
        jint indentsOffset) {

        // Outputs
        jobject recycle,
        jint recycleLength,
        jintArray recycleBreaks,
        jfloatArray recycleWidths,
        jfloatArray recycleAscents,
        jfloatArray recycleDescents,
        jintArray recycleFlags,
        jfloatArray charWidths) {

    minikin::android::StaticLayoutNative* builder = toNative(nativePtr);
    minikin::android::StaticLayoutNative* builder = toNative(nativePtr);


    ScopedCharArrayRO text(env, javaText);
    ScopedCharArrayRO text(env, javaText);
@@ -141,14 +92,44 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr,


    minikin::U16StringPiece u16Text(text.get(), length);
    minikin::U16StringPiece u16Text(text.get(), length);
    minikin::MeasuredText* measuredText = reinterpret_cast<minikin::MeasuredText*>(measuredTextPtr);
    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,
                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,
static jfloat nGetLineAscent(jlong ptr, jint i) {
            recycleFlags, recycleLength, result);
    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[] = {
static const JNINativeMethod gMethods[] = {
@@ -166,8 +147,6 @@ static const JNINativeMethod gMethods[] = {
    // Regular JNI
    // Regular JNI
    {"nComputeLineBreaks", "("
    {"nComputeLineBreaks", "("
        "J"  // nativePtr
        "J"  // nativePtr

        // Inputs
        "[C"  // text
        "[C"  // text
        "J"  // MeasuredParagraph ptr.
        "J"  // MeasuredParagraph ptr.
        "I"  // length
        "I"  // length
@@ -177,31 +156,20 @@ static const JNINativeMethod gMethods[] = {
        "[I"  // variableTabStops
        "[I"  // variableTabStops
        "I"  // defaultTabStop
        "I"  // defaultTabStop
        "I"  // indentsOffset
        "I"  // indentsOffset

        ")J", (void*) nComputeLineBreaks},
        // Outputs

        "Landroid/text/NativeLineBreaker$LineBreaks;"  // recycle
    // Result accessors, CriticalNatives
        "I"  // recycleLength
    {"nGetLineCount", "(J)I", (void*)nGetLineCount},
        "[I"  // recycleBreaks
    {"nGetLineBreakOffset", "(JI)I", (void*)nGetLineBreakOffset},
        "[F"  // recycleWidths
    {"nGetLineWidth", "(JI)F", (void*)nGetLineWidth},
        "[F"  // recycleAscents
    {"nGetLineAscent", "(JI)F", (void*)nGetLineAscent},
        "[F"  // recycleDescents
    {"nGetLineDescent", "(JI)F", (void*)nGetLineDescent},
        "[I"  // recycleFlags
    {"nGetLineFlag", "(JI)I", (void*)nGetLineFlag},
        ")I", (void*) nComputeLineBreaks}
    {"nGetReleaseResultFunc", "()J", (void*)nGetReleaseResultFunc},
};
};


int register_android_text_LineBreaker(JNIEnv* env)
int register_android_text_LineBreaker(JNIEnv* env) {
{
    return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", gMethods, NELEM(gMethods));
    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));
}
}


}
}