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

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

Merge "Compute hyphenated word pieces in MeasuredText"

parents 67462296 87b1547c
Loading
Loading
Loading
Loading
+25 −10
Original line number Diff line number Diff line
@@ -190,8 +190,11 @@ public class StaticLayoutPerfTest {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            state.pauseTiming();
            final MeasuredText text = MeasuredText.build(
                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
            final MeasuredText text = new MeasuredText.Builder(
                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                    .build();
            state.resumeTiming();

            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -206,8 +209,11 @@ public class StaticLayoutPerfTest {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            state.pauseTiming();
            final MeasuredText text = MeasuredText.build(
                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
            final MeasuredText text = new MeasuredText.Builder(
                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                    .build();
            state.resumeTiming();

            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -222,8 +228,11 @@ public class StaticLayoutPerfTest {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            state.pauseTiming();
            final MeasuredText text = MeasuredText.build(
                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
            final MeasuredText text = new MeasuredText.Builder(
                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                    .build();
            state.resumeTiming();

            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -238,8 +247,11 @@ public class StaticLayoutPerfTest {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            state.pauseTiming();
            final MeasuredText text = MeasuredText.build(
                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT, LTR);
            final MeasuredText text = new MeasuredText.Builder(
                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                    .setBreakStrategy(Layout.BREAK_STRATEGY_BALANCED)
                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NORMAL)
                    .build();
            state.resumeTiming();

            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
@@ -254,8 +266,11 @@ public class StaticLayoutPerfTest {
        final BenchmarkState state = mPerfStatusReporter.getBenchmarkState();
        while (state.keepRunning()) {
            state.pauseTiming();
            final MeasuredText text = MeasuredText.build(
                    generateRandomParagraph(WORD_LENGTH, STYLE_TEXT), PAINT, LTR);
            final MeasuredText text = new MeasuredText.Builder(
                    generateRandomParagraph(WORD_LENGTH, NO_STYLE_TEXT), PAINT)
                    .setBreakStrategy(Layout.BREAK_STRATEGY_SIMPLE)
                    .setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_NONE)
                    .build();
            state.resumeTiming();

            StaticLayout.Builder.obtain(text, 0, text.length(), PAINT, TEXT_WIDTH)
+11 −2
Original line number Diff line number Diff line
@@ -42320,10 +42320,10 @@ package android.text {
  }
  public class MeasuredText implements android.text.Spanned {
    method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic);
    method public static android.text.MeasuredText build(java.lang.CharSequence, android.text.TextPaint, android.text.TextDirectionHeuristic, int, int);
    method public char charAt(int);
    method public int getBreakStrategy();
    method public int getEnd();
    method public int getHyphenationFrequency();
    method public android.text.TextPaint getPaint();
    method public int getParagraphCount();
    method public int getParagraphEnd(int);
@@ -42340,6 +42340,15 @@ package android.text {
    method public java.lang.CharSequence subSequence(int, int);
  }
  public static final class MeasuredText.Builder {
    ctor public MeasuredText.Builder(java.lang.CharSequence, android.text.TextPaint);
    method public android.text.MeasuredText build();
    method public android.text.MeasuredText.Builder setBreakStrategy(int);
    method public android.text.MeasuredText.Builder setHyphenationFrequency(int);
    method public android.text.MeasuredText.Builder setRange(int, int);
    method public android.text.MeasuredText.Builder setTextDirection(android.text.TextDirectionHeuristic);
  }
  public abstract interface NoCopySpan {
  }
+7 −3
Original line number Diff line number Diff line
@@ -356,6 +356,7 @@ public class MeasuredParagraph {
            @IntRange(from = 0) int start,
            @IntRange(from = 0) int end,
            @NonNull TextDirectionHeuristic textDir,
            boolean computeHyphenation,
            @Nullable MeasuredParagraph recycle) {
        final MeasuredParagraph mt = recycle == null ? obtain() : recycle;
        mt.resetAndAnalyzeBidi(text, start, end, textDir);
@@ -365,7 +366,8 @@ public class MeasuredParagraph {
            long nativeBuilderPtr = nInitBuilder();
            try {
                mt.bindNativeObject(
                        nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer));
                        nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
                              computeHyphenation));
            } finally {
                nFreeBuilder(nativeBuilderPtr);
            }
@@ -394,7 +396,8 @@ public class MeasuredParagraph {
                    mt.mSpanEndCache.append(spanEnd);
                }
            }
            mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer));
            mt.bindNativeObject(nBuildNativeMeasuredParagraph(nativeBuilderPtr, mt.mCopiedBuffer,
                      computeHyphenation));
        } finally {
            nFreeBuilder(nativeBuilderPtr);
        }
@@ -668,7 +671,8 @@ public class MeasuredParagraph {
                                                  @FloatRange(from = 0) float width);

    private static native long nBuildNativeMeasuredParagraph(/* Non Zero */ long nativeBuilderPtr,
                                                 @NonNull char[] text);
                                                 @NonNull char[] text,
                                                 boolean computeHyphenation);

    private static native void nFreeBuilder(/* Non Zero */ long nativeBuilderPtr);

+145 −52
Original line number Diff line number Diff line
@@ -52,70 +52,147 @@ public class MeasuredText implements Spanned {
    // The sorted paragraph end offsets.
    private final @NonNull int[] mParagraphBreakPoints;

    // The break strategy for this measured text.
    private final @Layout.BreakStrategy int mBreakStrategy;

    // The hyphenation frequency for this measured text.
    private final @Layout.HyphenationFrequency int mHyphenationFrequency;

    /**
     * Build MeasuredText from the text.
     * A Builder for MeasuredText
     */
    public static final class Builder {
        // Mandatory parameters.
        private final @NonNull CharSequence mText;
        private final @NonNull TextPaint mPaint;

        // Members to be updated by setters.
        private @IntRange(from = 0) int mStart;
        private @IntRange(from = 0) int mEnd;
        private TextDirectionHeuristic mTextDir = TextDirectionHeuristics.FIRSTSTRONG_LTR;
        private @Layout.BreakStrategy int mBreakStrategy = Layout.BREAK_STRATEGY_HIGH_QUALITY;
        private @Layout.HyphenationFrequency int mHyphenationFrequency =
                Layout.HYPHENATION_FREQUENCY_NORMAL;


        /**
         * Builder constructor
         *
         * @param text The text to be measured.
         * @param paint The paint to be used for drawing.
     * @param textDir The text direction.
     * @return The measured text.
         */
    public static @NonNull MeasuredText build(@NonNull CharSequence text,
                                              @NonNull TextPaint paint,
                                              @NonNull TextDirectionHeuristic textDir) {
        return MeasuredText.build(text, paint, textDir, 0, text.length());
        public Builder(@NonNull CharSequence text, @NonNull TextPaint paint) {
            Preconditions.checkNotNull(text);
            Preconditions.checkNotNull(paint);

            mText = text;
            mPaint = paint;
            mStart = 0;
            mEnd = text.length();
        }

        /**
     * Build MeasuredText from the specific range of the text..
         * Set the range of measuring target.
         *
     * @param text The text to be measured.
     * @param paint The paint to be used for drawing.
     * @param textDir The text direction.
     * @param start The inclusive start offset of the text.
     * @param end The exclusive start offset of the text.
     * @return The measured text.
         * @param start The measuring target start offset in the text.
         * @param end The measuring target end offset in the text.
         */
    public static @NonNull MeasuredText build(@NonNull CharSequence text,
                                              @NonNull TextPaint paint,
                                              @NonNull TextDirectionHeuristic textDir,
                                              @IntRange(from = 0) int start,
        public @NonNull Builder setRange(@IntRange(from = 0) int start,
                                         @IntRange(from = 0) int end) {
        Preconditions.checkNotNull(text);
        Preconditions.checkNotNull(paint);
            Preconditions.checkArgumentInRange(start, 0, mText.length(), "start");
            Preconditions.checkArgumentInRange(end, 0, mText.length(), "end");
            Preconditions.checkArgument(start <= end, "The range is reversed.");

            mStart = start;
            mEnd = end;
            return this;
        }

        /**
         * Set the text direction heuristic
         *
         * The default value is {@link TextDirectionHeuristics#FIRSTSTRONG_LTR}.
         *
         * @param textDir The text direction heuristic for resolving bidi behavior.
         * @return this builder, useful for chaining.
         */
        public @NonNull Builder setTextDirection(@NonNull TextDirectionHeuristic textDir) {
            Preconditions.checkNotNull(textDir);
        Preconditions.checkArgumentInRange(start, 0, text.length(), "start");
        Preconditions.checkArgumentInRange(end, 0, text.length(), "end");
            mTextDir = textDir;
            return this;
        }

        /**
         * Set the break strategy
         *
         * The default value is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}.
         *
         * @param breakStrategy The break strategy.
         * @return this builder, useful for chaining.
         */
        public @NonNull Builder setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
            mBreakStrategy = breakStrategy;
            return this;
        }

        /**
         * Set the hyphenation frequency
         *
         * The default value is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
         *
         * @param hyphenationFrequency The hyphenation frequency.
         * @return this builder, useful for chaining.
         */
        public @NonNull Builder setHyphenationFrequency(
                @Layout.HyphenationFrequency int hyphenationFrequency) {
            mHyphenationFrequency = hyphenationFrequency;
            return this;
        }

        /**
         * Build the measured text
         *
         * @return the measured text.
         */
        public @NonNull MeasuredText build() {
            final boolean needHyphenation = mBreakStrategy != Layout.BREAK_STRATEGY_SIMPLE
                    && mHyphenationFrequency != Layout.HYPHENATION_FREQUENCY_NONE;

            final IntArray paragraphEnds = new IntArray();
            final ArrayList<MeasuredParagraph> measuredTexts = new ArrayList<>();

            int paraEnd = 0;
        for (int paraStart = start; paraStart < end; paraStart = paraEnd) {
            paraEnd = TextUtils.indexOf(text, LINE_FEED, paraStart, end);
            for (int paraStart = mStart; paraStart < mEnd; paraStart = paraEnd) {
                paraEnd = TextUtils.indexOf(mText, LINE_FEED, paraStart, mEnd);
                if (paraEnd < 0) {
                // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph end.
                paraEnd = end;
                    // No LINE_FEED(U+000A) character found. Use end of the text as the paragraph
                    // end.
                    paraEnd = mEnd;
                } else {
                    paraEnd++;  // Includes LINE_FEED(U+000A) to the prev paragraph.
                }

                paragraphEnds.add(paraEnd);
                measuredTexts.add(MeasuredParagraph.buildForStaticLayout(
                    paint, text, paraStart, paraEnd, textDir, null /* no recycle */));
                        mPaint, mText, paraStart, paraEnd, mTextDir, needHyphenation,
                        null /* no recycle */));
            }

        return new MeasuredText(text, start, end, paint, textDir,
                                measuredTexts.toArray(new MeasuredParagraph[measuredTexts.size()]),
            return new MeasuredText(mText, mStart, mEnd, mPaint, mTextDir, mBreakStrategy,
                                    mHyphenationFrequency, measuredTexts.toArray(
                                            new MeasuredParagraph[measuredTexts.size()]),
                                    paragraphEnds.toArray());
        }
    };

    // Use MeasuredText.build instead.
    // Use MeasuredText.Builder instead.
    private MeasuredText(@NonNull CharSequence text,
                         @IntRange(from = 0) int start,
                         @IntRange(from = 0) int end,
                         @NonNull TextPaint paint,
                         @NonNull TextDirectionHeuristic textDir,
                         @Layout.BreakStrategy int breakStrategy,
                         @Layout.HyphenationFrequency int frequency,
                         @NonNull MeasuredParagraph[] measuredTexts,
                         @NonNull int[] paragraphBreakPoints) {
        mText = text;
@@ -125,6 +202,8 @@ public class MeasuredText implements Spanned {
        mMeasuredParagraphs = measuredTexts;
        mParagraphBreakPoints = paragraphBreakPoints;
        mTextDir = textDir;
        mBreakStrategy = breakStrategy;
        mHyphenationFrequency = frequency;
    }

    /**
@@ -190,6 +269,20 @@ public class MeasuredText implements Spanned {
        return mMeasuredParagraphs[paraIndex];
    }

    /**
     * Returns the break strategy for this text.
     */
    public @Layout.BreakStrategy int getBreakStrategy() {
        return mBreakStrategy;
    }

    /**
     * Returns the hyphenation frequency for this text.
     */
    public @Layout.HyphenationFrequency int getHyphenationFrequency() {
        return mHyphenationFrequency;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////
    // Spanned overrides
    //
+24 −10
Original line number Diff line number Diff line
@@ -653,27 +653,41 @@ public class StaticLayout extends Layout {

        MeasuredText measured = null;
        final Spanned spanned;
        final boolean canUseMeasuredText;
        if (source instanceof MeasuredText) {
            measured = (MeasuredText) source;

            final CharSequence original = measured.getText();
            spanned = (original instanceof Spanned) ? (Spanned) original : null;

            if (bufStart != measured.getStart() || bufEnd != measured.getEnd()) {
                // The buffer position has changed. Re-measure here.
                measured = MeasuredText.build(original, paint, textDir, bufStart, bufEnd);
                canUseMeasuredText = false;
            } else if (b.mBreakStrategy != measured.getBreakStrategy()
                    || b.mHyphenationFrequency != measured.getHyphenationFrequency()) {
                // The computed hyphenation pieces may not be able to used. Re-measure it.
                canUseMeasuredText = false;
            } else {
                // We can use measured information.
                canUseMeasuredText = true;
            }
        } else {
            canUseMeasuredText = false;
        }

                // Overwrite with the one when emeasured.
        if (!canUseMeasuredText) {
            measured = new MeasuredText.Builder(source, paint)
                    .setRange(bufStart, bufEnd)
                    .setTextDirection(textDir)
                    .setBreakStrategy(b.mBreakStrategy)
                    .setHyphenationFrequency(b.mHyphenationFrequency)
                    .build();
            spanned = (source instanceof Spanned) ? (Spanned) source : null;
        } else {
            final CharSequence original = measured.getText();
            spanned = (original instanceof Spanned) ? (Spanned) original : null;
            // Overwrite with the one when measured.
            // TODO: Give an option for developer not to overwrite and measure again here?
            textDir = measured.getTextDir();
            paint = measured.getPaint();
        }
        } else {
            measured = MeasuredText.build(source, paint, textDir, bufStart, bufEnd);
            spanned = (source instanceof Spanned) ? (Spanned) source : null;
        }

        try {
            for (int paraIndex = 0; paraIndex < measured.getParagraphCount(); paraIndex++) {
Loading