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

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

Use ICU Bidi instead of own implementation

Bug: 317144801
Test: Manual
Test: atest CtsTextTestCases
Test: atest CtsGraphicsTestCases
Test: atest CtsWidgetTestCases
Change-Id: Id0dc8233a25a2d16307e5b6b266d7287e58de986
parent ba9245a7
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -54,4 +54,11 @@ public class ClientFlags {
    public static boolean fixLineHeightForLocale() {
        return TextFlags.isFeatureEnabled(Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE);
    }

    /**
     * @see Flags#icuBidiMigration()
     */
    public static boolean icuBidiMigration() {
        return TextFlags.isFeatureEnabled(Flags.FLAG_ICU_BIDI_MIGRATION);
    }
}
+103 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.text.LineBreakConfig;
import android.graphics.text.MeasuredText;
import android.icu.text.Bidi;
import android.text.AutoGrowArray.ByteArray;
import android.text.AutoGrowArray.FloatArray;
import android.text.AutoGrowArray.IntArray;
@@ -115,6 +116,11 @@ public class MeasuredParagraph {
    // This is empty if mLtrWithoutBidi is true.
    private @NonNull ByteArray mLevels = new ByteArray();

    // The bidi level for runs.
    private @NonNull ByteArray mRunLevels = new ByteArray();

    private Bidi mBidi;

    // The whole width of the text.
    // See getWholeWidth comments.
    private @FloatRange(from = 0.0f) float mWholeWidth;
@@ -148,6 +154,7 @@ public class MeasuredParagraph {
        reset();
        mLevels.clearWithReleasingLargeArray();
        mWidths.clearWithReleasingLargeArray();
        mRunLevels.clearWithReleasingLargeArray();
        mFontMetrics.clearWithReleasingLargeArray();
        mSpanEndCache.clearWithReleasingLargeArray();
    }
@@ -160,10 +167,12 @@ public class MeasuredParagraph {
        mCopiedBuffer = null;
        mWholeWidth = 0;
        mLevels.clear();
        mRunLevels.clear();
        mWidths.clear();
        mFontMetrics.clear();
        mSpanEndCache.clear();
        mMeasuredText = null;
        mBidi = null;
    }

    /**
@@ -193,6 +202,13 @@ public class MeasuredParagraph {
     * @hide
     */
    public @Layout.Direction int getParagraphDir() {
        if (ClientFlags.icuBidiMigration()) {
            if (mBidi == null) {
                return Layout.DIR_LEFT_TO_RIGHT;
            }
            return (mBidi.getParaLevel() & 0x01) == 0
                    ? Layout.DIR_LEFT_TO_RIGHT : Layout.DIR_RIGHT_TO_LEFT;
        }
        return mParaDir;
    }

@@ -204,6 +220,62 @@ public class MeasuredParagraph {
     */
    public Directions getDirections(@IntRange(from = 0) int start,  // inclusive
                                    @IntRange(from = 0) int end) {  // exclusive
        if (ClientFlags.icuBidiMigration()) {
            // Easy case: mBidi == null means the text is all LTR and no bidi suppot is needed.
            if (mBidi == null) {
                return Layout.DIRS_ALL_LEFT_TO_RIGHT;
            }

            // Easy case: If the original text only contains single directionality run, the
            // substring is only single run.
            if (start == end) {
                if ((mBidi.getParaLevel() & 0x01) == 0) {
                    return Layout.DIRS_ALL_LEFT_TO_RIGHT;
                } else {
                    return Layout.DIRS_ALL_RIGHT_TO_LEFT;
                }
            }

            // Okay, now we need to generate the line instance.
            Bidi bidi = mBidi.createLineBidi(start, end);

            // Easy case: If the line instance only contains single directionality run, no need
            // to reorder visually.
            if (bidi.getRunCount() == 1) {
                if ((bidi.getParaLevel() & 0x01) == 1) {
                    return Layout.DIRS_ALL_RIGHT_TO_LEFT;
                } else {
                    return Layout.DIRS_ALL_LEFT_TO_RIGHT;
                }
            }

            // Reorder directionality run visually.
            mRunLevels.resize(bidi.getRunCount());
            byte[] levels = mRunLevels.getRawArray();
            for (int i = 0; i < bidi.getRunCount(); ++i) {
                levels[i] = (byte) bidi.getRunLevel(i);
            }
            int[] visualOrders = Bidi.reorderVisual(levels);

            int[] dirs = new int[bidi.getRunCount() * 2];
            for (int i = 0; i < bidi.getRunCount(); ++i) {
                int vIndex;
                if ((mBidi.getBaseLevel() & 0x01) == 1) {
                    // For the historical reasons, if the base directionality is RTL, the Android
                    // draws from the right, i.e. the visually reordered run needs to be reversed.
                    vIndex = visualOrders[bidi.getRunCount() - i - 1];
                } else {
                    vIndex = visualOrders[i];
                }

                // Special packing of dire
                dirs[i * 2] = bidi.getRunStart(vIndex);
                dirs[i * 2 + 1] = bidi.getRunLevel(vIndex) << Layout.RUN_LEVEL_SHIFT
                        | (bidi.getRunLimit(vIndex) - dirs[i * 2]);
            }

            return new Directions(dirs);
        }
        if (mLtrWithoutBidi) {
            return Layout.DIRS_ALL_LEFT_TO_RIGHT;
        }
@@ -608,6 +680,37 @@ public class MeasuredParagraph {
            }
        }

        if (ClientFlags.icuBidiMigration()) {
            if ((textDir == TextDirectionHeuristics.LTR
                    || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
                    || textDir == TextDirectionHeuristics.ANYRTL_LTR)
                    && TextUtils.doesNotNeedBidi(mCopiedBuffer, 0, mTextLength)) {
                mLevels.clear();
                mLtrWithoutBidi = true;
                return;
            }
            final int bidiRequest;
            if (textDir == TextDirectionHeuristics.LTR) {
                bidiRequest = Bidi.LTR;
            } else if (textDir == TextDirectionHeuristics.RTL) {
                bidiRequest = Bidi.RTL;
            } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR) {
                bidiRequest = Bidi.LEVEL_DEFAULT_LTR;
            } else if (textDir == TextDirectionHeuristics.FIRSTSTRONG_RTL) {
                bidiRequest = Bidi.LEVEL_DEFAULT_RTL;
            } else {
                final boolean isRtl = textDir.isRtl(mCopiedBuffer, 0, mTextLength);
                bidiRequest = isRtl ? Bidi.RTL : Bidi.LTR;
            }
            mBidi = new Bidi(mCopiedBuffer, 0, null, 0, mCopiedBuffer.length, bidiRequest);
            mLevels.resize(mTextLength);
            byte[] rawArray = mLevels.getRawArray();
            for (int i = 0; i < mTextLength; ++i) {
                rawArray[i] = mBidi.getLevelAt(i);
            }
            mLtrWithoutBidi = false;
            return;
        }
        if ((textDir == TextDirectionHeuristics.LTR
                || textDir == TextDirectionHeuristics.FIRSTSTRONG_LTR
                || textDir == TextDirectionHeuristics.ANYRTL_LTR)
+2 −0
Original line number Diff line number Diff line
@@ -59,6 +59,7 @@ public final class TextFlags {
            Flags.FLAG_PHRASE_STRICT_FALLBACK,
            Flags.FLAG_USE_BOUNDS_FOR_WIDTH,
            Flags.FLAG_FIX_LINE_HEIGHT_FOR_LOCALE,
            Flags.FLAG_ICU_BIDI_MIGRATION,
    };

    /**
@@ -71,6 +72,7 @@ public final class TextFlags {
            Flags.phraseStrictFallback(),
            Flags.useBoundsForWidth(),
            Flags.fixLineHeightForLocale(),
            Flags.icuBidiMigration(),
    };

    /**
+7 −0
Original line number Diff line number Diff line
@@ -103,3 +103,10 @@ flag {
  description: "Fix that InputService#onUpdateSelection is not called when insert mode gesture is performed."
  bug: "300850862"
}

flag {
  name: "icu_bidi_migration"
  namespace: "text"
  description: "A flag for replacing AndroidBidi with android.icu.text.Bidi."
  bug: "317144801"
}