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

Commit e477cc4c authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Extract native methods into isolated classes"

parents b4cd7588 6f11c6e7
Loading
Loading
Loading
Loading
+41 −125
Original line number Diff line number Diff line
@@ -30,10 +30,6 @@ import android.text.style.MetricAffectingSpan;
import android.text.style.ReplacementSpan;
import android.util.Pools.SynchronizedPool;

import dalvik.annotation.optimization.CriticalNative;

import libcore.util.NativeAllocationRegistry;

import java.util.Arrays;

/**
@@ -62,9 +58,6 @@ import java.util.Arrays;
public class MeasuredParagraph {
    private static final char OBJECT_REPLACEMENT_CHARACTER = '\uFFFC';

    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
            MeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024);

    private MeasuredParagraph() {}  // Use build static functions instead.

    private static final SynchronizedPool<MeasuredParagraph> sPool = new SynchronizedPool<>(1);
@@ -128,24 +121,7 @@ public class MeasuredParagraph {
    private @Nullable IntArray mFontMetrics = new IntArray(4 * 4);

    // The native MeasuredParagraph.
    // See getNativePtr comments.
    // Do not modify these members directly. Use bindNativeObject/unbindNativeObject instead.
    private /* Maybe Zero */ long mNativePtr = 0;
    private @Nullable Runnable mNativeObjectCleaner;

    // Associate the native object to this Java object.
    private void bindNativeObject(/* Non Zero*/ long nativePtr) {
        mNativePtr = nativePtr;
        mNativeObjectCleaner = sRegistry.registerNativeAllocation(this, nativePtr);
    }

    // Decouple the native object from this Java object and release the native object.
    private void unbindNativeObject() {
        if (mNativePtr != 0) {
            mNativeObjectCleaner.run();
            mNativePtr = 0;
        }
    }
    private @Nullable NativeMeasuredParagraph mNativeMeasuredParagraph;

    // Following two objects are for avoiding object allocation.
    private @NonNull TextPaint mCachedPaint = new TextPaint();
@@ -173,7 +149,7 @@ public class MeasuredParagraph {
        mWidths.clear();
        mFontMetrics.clear();
        mSpanEndCache.clear();
        unbindNativeObject();
        mNativeMeasuredParagraph = null;
    }

    /**
@@ -267,10 +243,10 @@ public class MeasuredParagraph {
     * Returns the native ptr of the MeasuredParagraph.
     *
     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
     * Returns 0 in other cases.
     * Returns null in other cases.
     */
    public /* Maybe Zero */ long getNativePtr() {
        return mNativePtr;
    public NativeMeasuredParagraph getNativeMeasuredParagraph() {
        return mNativeMeasuredParagraph;
    }

    /**
@@ -283,7 +259,7 @@ public class MeasuredParagraph {
     * @param end the exclusive end offset of the target region in the text
     */
    public float getWidth(int start, int end) {
        if (mNativePtr == 0) {
        if (mNativeMeasuredParagraph == null) {
            // We have result in Java.
            final float[] widths = mWidths.getRawArray();
            float r = 0.0f;
@@ -293,7 +269,7 @@ public class MeasuredParagraph {
            return r;
        } else {
            // We have result in native.
            return nGetWidth(mNativePtr, start, end);
            return mNativeMeasuredParagraph.getWidth(start, end);
        }
    }

@@ -305,7 +281,16 @@ public class MeasuredParagraph {
     */
    public void getBounds(@IntRange(from = 0) int start, @IntRange(from = 0) int end,
            @NonNull Rect bounds) {
        nGetBounds(mNativePtr, mCopiedBuffer, start, end, bounds);
        mNativeMeasuredParagraph.getBounds(mCopiedBuffer, start, end, bounds);
    }

    /**
     * Returns a width of the character at the offset.
     *
     * This is available only if the MeasuredParagraph is computed with buildForStaticLayout.
     */
    public float getCharWidthAt(@IntRange(from = 0) int offset) {
        return mNativeMeasuredParagraph.getCharWidthAt(offset);
    }

    /**
@@ -364,7 +349,7 @@ public class MeasuredParagraph {
        if (mt.mSpanned == null) {
            // No style change by MetricsAffectingSpan. Just measure all text.
            mt.applyMetricsAffectingSpan(
                    paint, null /* spans */, start, end, 0 /* native static layout ptr */);
                    paint, null /* spans */, start, end, null /* native builder ptr */);
        } else {
            // There may be a MetricsAffectingSpan. Split into span transitions and apply styles.
            int spanEnd;
@@ -374,7 +359,7 @@ public class MeasuredParagraph {
                        MetricAffectingSpan.class);
                spans = TextUtils.removeEmptySpans(spans, mt.mSpanned, MetricAffectingSpan.class);
                mt.applyMetricsAffectingSpan(
                        paint, spans, spanStart, spanEnd, 0 /* native static layout ptr */);
                        paint, spans, spanStart, spanEnd, null /* native builder ptr */);
            }
        }
        return mt;
@@ -406,25 +391,16 @@ public class MeasuredParagraph {
            @Nullable MeasuredParagraph recycle) {
        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
        mt.resetAndAnalyzeBidi(text, start, end, textDir);
        final NativeMeasuredParagraph.Builder builder = new NativeMeasuredParagraph.Builder();
        if (mt.mTextLength == 0) {
            // Need to build empty native measured text for StaticLayout.
            // TODO: Stop creating empty measured text for empty lines.
            long nativeBuilderPtr = nInitBuilder();
            try {
                mt.bindNativeObject(
                        nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
                              computeHyphenation, computeLayout));
            } finally {
                nFreeBuilder(nativeBuilderPtr);
            }
            return mt;
        }

        long nativeBuilderPtr = nInitBuilder();
        try {
            mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
                        computeLayout);
        } else {
            if (mt.mSpanned == null) {
                // No style change by MetricsAffectingSpan. Just measure all text.
                mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, nativeBuilderPtr);
                mt.applyMetricsAffectingSpan(paint, null /* spans */, start, end, builder);
                mt.mSpanEndCache.append(end);
            } else {
                // There may be a MetricsAffectingSpan. Split into span transitions and apply
@@ -437,15 +413,12 @@ public class MeasuredParagraph {
                            MetricAffectingSpan.class);
                    spans = TextUtils.removeEmptySpans(spans, mt.mSpanned,
                                                       MetricAffectingSpan.class);
                    mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd,
                                                 nativeBuilderPtr);
                    mt.applyMetricsAffectingSpan(paint, spans, spanStart, spanEnd, builder);
                    mt.mSpanEndCache.append(spanEnd);
                }
            }
            mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
                      computeHyphenation, computeLayout));
        } finally {
            nFreeBuilder(nativeBuilderPtr);
            mt.mNativeMeasuredParagraph = builder.build(mt.mCopiedBuffer, computeHyphenation,
                    computeLayout);
        }

        return mt;
@@ -517,13 +490,13 @@ public class MeasuredParagraph {
    private void applyReplacementRun(@NonNull ReplacementSpan replacement,
                                     @IntRange(from = 0) int start,  // inclusive, in copied buffer
                                     @IntRange(from = 0) int end,  // exclusive, in copied buffer
                                     /* Maybe Zero */ long nativeBuilderPtr) {
                                     @Nullable NativeMeasuredParagraph.Builder builder) {
        // Use original text. Shouldn't matter.
        // TODO: passing uninitizlied FontMetrics to developers. Do we need to keep this for
        //       backward compatibility? or Should we initialize them for getFontMetricsInt?
        final float width = replacement.getSize(
                mCachedPaint, mSpanned, start + mTextStart, end + mTextStart, mCachedFm);
        if (nativeBuilderPtr == 0) {
        if (builder == null) {
            // Assigns all width to the first character. This is the same behavior as minikin.
            mWidths.set(start, width);
            if (end > start + 1) {
@@ -531,24 +504,22 @@ public class MeasuredParagraph {
            }
            mWholeWidth += width;
        } else {
            nAddReplacementRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
                               width);
            builder.addReplacementRun(mCachedPaint, start, end, width);
        }
    }

    private void applyStyleRun(@IntRange(from = 0) int start,  // inclusive, in copied buffer
                               @IntRange(from = 0) int end,  // exclusive, in copied buffer
                               /* Maybe Zero */ long nativeBuilderPtr) {
                               @Nullable NativeMeasuredParagraph.Builder builder) {

        if (mLtrWithoutBidi) {
            // If the whole text is LTR direction, just apply whole region.
            if (nativeBuilderPtr == 0) {
            if (builder == null) {
                mWholeWidth += mCachedPaint.getTextRunAdvances(
                        mCopiedBuffer, start, end - start, start, end - start, false /* isRtl */,
                        mWidths.getRawArray(), start);
            } else {
                nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), start, end,
                        false /* isRtl */);
                builder.addStyleRun(mCachedPaint, start, end, false /* isRtl */);
            }
        } else {
            // If there is multiple bidi levels, split into individual bidi level and apply style.
@@ -558,14 +529,13 @@ public class MeasuredParagraph {
            for (int levelStart = start, levelEnd = start + 1;; ++levelEnd) {
                if (levelEnd == end || mLevels.get(levelEnd) != level) {  // transition point
                    final boolean isRtl = (level & 0x1) != 0;
                    if (nativeBuilderPtr == 0) {
                    if (builder == null) {
                        final int levelLength = levelEnd - levelStart;
                        mWholeWidth += mCachedPaint.getTextRunAdvances(
                                mCopiedBuffer, levelStart, levelLength, levelStart, levelLength,
                                isRtl, mWidths.getRawArray(), levelStart);
                    } else {
                        nAddStyleRun(nativeBuilderPtr, mCachedPaint.getNativeInstance(), levelStart,
                                levelEnd, isRtl);
                        builder.addStyleRun(mCachedPaint, levelStart, levelEnd, isRtl);
                    }
                    if (levelEnd == end) {
                        break;
@@ -582,12 +552,12 @@ public class MeasuredParagraph {
            @Nullable MetricAffectingSpan[] spans,
            @IntRange(from = 0) int start,  // inclusive, in original text buffer
            @IntRange(from = 0) int end,  // exclusive, in original text buffer
            /* Maybe Zero */ long nativeBuilderPtr) {
            @Nullable NativeMeasuredParagraph.Builder builder) {
        mCachedPaint.set(paint);
        // XXX paint should not have a baseline shift, but...
        mCachedPaint.baselineShift = 0;

        final boolean needFontMetrics = nativeBuilderPtr != 0;
        final boolean needFontMetrics = builder != null;

        if (needFontMetrics && mCachedFm == null) {
            mCachedFm = new Paint.FontMetricsInt();
@@ -610,15 +580,14 @@ public class MeasuredParagraph {
        final int startInCopiedBuffer = start - mTextStart;
        final int endInCopiedBuffer = end - mTextStart;

        if (nativeBuilderPtr != 0) {
        if (builder != null) {
            mCachedPaint.getFontMetricsInt(mCachedFm);
        }

        if (replacement != null) {
            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer,
                                nativeBuilderPtr);
            applyReplacementRun(replacement, startInCopiedBuffer, endInCopiedBuffer, builder);
        } else {
            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, nativeBuilderPtr);
            applyStyleRun(startInCopiedBuffer, endInCopiedBuffer, builder);
        }

        if (needFontMetrics) {
@@ -689,59 +658,6 @@ public class MeasuredParagraph {
     * This only works if the MeasuredParagraph is computed with buildForStaticLayout.
     */
    public @IntRange(from = 0) int getMemoryUsage() {
        return nGetMemoryUsage(mNativePtr);
        return mNativeMeasuredParagraph.getMemoryUsage();
    }

    private static native /* Non Zero */ long nInitBuilder();

    /**
     * Apply style to make native measured text.
     *
     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
     * @param paintPtr The native paint pointer to be applied.
     * @param start The start offset in the copied buffer.
     * @param end The end offset in the copied buffer.
     * @param isRtl True if the text is RTL.
     */
    private static native void nAddStyleRun(/* Non Zero */ long nativeBuilderPtr,
                                            /* Non Zero */ long paintPtr,
                                            @IntRange(from = 0) int start,
                                            @IntRange(from = 0) int end,
                                            boolean isRtl);

    /**
     * Apply ReplacementRun to make native measured text.
     *
     * @param nativeBuilderPtr The native MeasuredParagraph builder pointer.
     * @param paintPtr The native paint pointer to be applied.
     * @param start The start offset in the copied buffer.
     * @param end The end offset in the copied buffer.
     * @param width The width of the replacement.
     */
    private static native void nAddReplacementRun(/* Non Zero */ long nativeBuilderPtr,
                                                  /* Non Zero */ long paintPtr,
                                                  @IntRange(from = 0) int start,
                                                  @IntRange(from = 0) int end,
                                                  @FloatRange(from = 0) float width);

    private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
                                                 @NonNull char[] text,
                                                 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();

    @CriticalNative
    private static native int nGetMemoryUsage(/* Non Zero */ long nativePtr);

    private static native void nGetBounds(long nativePtr, char[] buf, int start, int end,
            Rect rect);
}
+155 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.text;

import android.annotation.FloatRange;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;

import dalvik.annotation.optimization.CriticalNative;
import dalvik.annotation.optimization.FastNative;

import libcore.util.NativeAllocationRegistry;

/**
 * A native implementation of the line breaker.
 * TODO: Consider to make this class public.
 * @hide
 */
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];
        public int[] flags = new int[INITIAL_SIZE];
        // breaks, widths, and flags should all have the same length
    }

    private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry(
            NativeLineBreaker.class.getClassLoader(), nGetReleaseFunc(), 64);

    private final long mNativePtr;

    /**
     * A constructor of NativeLineBreaker
     */
    public NativeLineBreaker(@Layout.BreakStrategy int breakStrategy,
            @Layout.HyphenationFrequency int hyphenationFrequency,
            boolean justify, @Nullable int[] indents) {
        mNativePtr = nInit(breakStrategy, hyphenationFrequency, justify, indents);
        sRegistry.registerNativeAllocation(this, mNativePtr);
    }

    /**
     * Break text into lines
     *
     * @param chars an array of characters
     * @param measuredPara a result of the text measurement
     * @param length a length of the target text from the begining
     * @param firstWidth a width of the first width of the line in this paragraph
     * @param firstWidthLineCount a number of lines that has the length of the firstWidth
     * @param restWidth a width of the rest of the lines.
     * @param variableTabStops an array of tab stop widths
     * @param defaultTabStop a width of the tab stop
     * @param indentsOffset an offset of the indents to be used.
     * @param out output buffer
     * @return a number of the lines
     */
    @NonNull public int computeLineBreaks(
            @NonNull char[] chars,
            @NonNull NativeMeasuredParagraph measuredPara,
            @IntRange(from = 0) int length,
            @FloatRange(from = 0.0f) float firstWidth,
            @IntRange(from = 0) int firstWidthLineCount,
            @FloatRange(from = 0.0f) float restWidth,
            @Nullable int[] variableTabStops,
            int defaultTabStop,
            @IntRange(from = 0) int indentsOffset,
            @NonNull LineBreaks out) {
        return nComputeLineBreaks(
                mNativePtr,

                // Inputs
                chars,
                measuredPara.getNativePtr(),
                length,
                firstWidth,
                firstWidthLineCount,
                restWidth,
                variableTabStops,
                defaultTabStop,
                indentsOffset,

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

    }

    @FastNative
    private static native long nInit(
            @Layout.BreakStrategy int breakStrategy,
            @Layout.HyphenationFrequency int hyphenationFrequency,
            boolean isJustified,
            @Nullable int[] indents);

    @CriticalNative
    private static native long nGetReleaseFunc();

    // populates LineBreaks and returns the number of breaks found
    //
    // the arrays inside the LineBreaks objects are passed in as well
    // to reduce the number of JNI calls in the common case where the
    // 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(
            /* non zero */ long nativePtr,

            // Inputs
            @NonNull char[] text,
            /* Non Zero */ long measuredTextPtr,
            @IntRange(from = 0) int length,
            @FloatRange(from = 0.0f) float firstWidth,
            @IntRange(from = 0) int firstWidthLineCount,
            @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);
}
+176 −0

File added.

Preview size limit exceeded, changes collapsed.

+15 −0
Original line number Diff line number Diff line
@@ -517,6 +517,21 @@ public class PrecomputedText implements Spannable {
        getMeasuredParagraph(paraIndex).getBounds(start - paraStart, end - paraStart, bounds);
    }

    /**
     * Returns a width of a character at offset
     *
     * @param offset an offset of the text.
     * @return a width of the character.
     * @hide
     */
    public float getCharWidthAt(@IntRange(from = 0) int offset) {
        Preconditions.checkArgument(0 <= offset && offset < mText.length(), "invalid offset");
        final int paraIndex = findParaIndex(offset);
        final int paraStart = getParagraphStart(paraIndex);
        final int paraEnd = getParagraphEnd(paraIndex);
        return getMeasuredParagraph(paraIndex).getCharWidthAt(offset - paraStart);
    }

    /**
     * Returns the size of native PrecomputedText memory usage.
     *
+200 −268

File changed.

Preview size limit exceeded, changes collapsed.

Loading