diff options
| -rw-r--r-- | core/java/android/text/Layout.java | 31 | ||||
| -rw-r--r-- | core/java/android/text/NativeLineBreaker.java | 287 | ||||
| -rw-r--r-- | core/java/android/text/NativeMeasuredParagraph.java | 13 | ||||
| -rw-r--r-- | core/java/android/text/StaticLayout.java | 31 |
4 files changed, 294 insertions, 68 deletions
diff --git a/core/java/android/text/Layout.java b/core/java/android/text/Layout.java index ca952da15c72..33c977b6fe25 100644 --- a/core/java/android/text/Layout.java +++ b/core/java/android/text/Layout.java @@ -50,9 +50,9 @@ import java.util.Arrays; public abstract class Layout { /** @hide */ @IntDef(prefix = { "BREAK_STRATEGY_" }, value = { - BREAK_STRATEGY_SIMPLE, - BREAK_STRATEGY_HIGH_QUALITY, - BREAK_STRATEGY_BALANCED + NativeLineBreaker.BREAK_STRATEGY_SIMPLE, + NativeLineBreaker.BREAK_STRATEGY_HIGH_QUALITY, + NativeLineBreaker.BREAK_STRATEGY_BALANCED }) @Retention(RetentionPolicy.SOURCE) public @interface BreakStrategy {} @@ -63,19 +63,20 @@ public abstract class Layout { * before it (which yields a more consistent user experience when editing), but layout may not * be the highest quality. */ - public static final int BREAK_STRATEGY_SIMPLE = 0; + public static final int BREAK_STRATEGY_SIMPLE = NativeLineBreaker.BREAK_STRATEGY_SIMPLE; /** * Value for break strategy indicating high quality line breaking, including automatic * hyphenation and doing whole-paragraph optimization of line breaks. */ - public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; + public static final int BREAK_STRATEGY_HIGH_QUALITY = + NativeLineBreaker.BREAK_STRATEGY_HIGH_QUALITY; /** * Value for break strategy indicating balanced line breaking. The breaks are chosen to * make all lines as close to the same length as possible, including automatic hyphenation. */ - public static final int BREAK_STRATEGY_BALANCED = 2; + public static final int BREAK_STRATEGY_BALANCED = NativeLineBreaker.BREAK_STRATEGY_BALANCED; /** @hide */ @IntDef(prefix = { "HYPHENATION_FREQUENCY_" }, value = { @@ -93,29 +94,32 @@ public abstract class Layout { * layout and there is otherwise no valid break. Soft hyphens are ignored and will not be used * as suggestions for potential line breaks. */ - public static final int HYPHENATION_FREQUENCY_NONE = 0; + public static final int HYPHENATION_FREQUENCY_NONE = + NativeLineBreaker.HYPHENATION_FREQUENCY_NONE; /** * Value for hyphenation frequency indicating a light amount of automatic hyphenation, which * is a conservative default. Useful for informal cases, such as short sentences or chat * messages. */ - public static final int HYPHENATION_FREQUENCY_NORMAL = 1; + public static final int HYPHENATION_FREQUENCY_NORMAL = + NativeLineBreaker.HYPHENATION_FREQUENCY_NORMAL; /** * Value for hyphenation frequency indicating the full amount of automatic hyphenation, typical * in typography. Useful for running text and where it's important to put the maximum amount of * text in a screen with limited space. */ - public static final int HYPHENATION_FREQUENCY_FULL = 2; + public static final int HYPHENATION_FREQUENCY_FULL = + NativeLineBreaker.HYPHENATION_FREQUENCY_FULL; private static final ParagraphStyle[] NO_PARA_SPANS = ArrayUtils.emptyArray(ParagraphStyle.class); /** @hide */ @IntDef(prefix = { "JUSTIFICATION_MODE_" }, value = { - JUSTIFICATION_MODE_NONE, - JUSTIFICATION_MODE_INTER_WORD + NativeLineBreaker.JUSTIFICATION_MODE_NONE, + NativeLineBreaker.JUSTIFICATION_MODE_INTER_WORD }) @Retention(RetentionPolicy.SOURCE) public @interface JustificationMode {} @@ -123,12 +127,13 @@ public abstract class Layout { /** * Value for justification mode indicating no justification. */ - public static final int JUSTIFICATION_MODE_NONE = 0; + public static final int JUSTIFICATION_MODE_NONE = NativeLineBreaker.JUSTIFICATION_MODE_NONE; /** * Value for justification mode indicating the text is justified by stretching word spacing. */ - public static final int JUSTIFICATION_MODE_INTER_WORD = 1; + public static final int JUSTIFICATION_MODE_INTER_WORD = + NativeLineBreaker.JUSTIFICATION_MODE_INTER_WORD; /* * Line spacing multiplier for default line spacing. diff --git a/core/java/android/text/NativeLineBreaker.java b/core/java/android/text/NativeLineBreaker.java index a31b3361d970..2bcfa5fe0857 100644 --- a/core/java/android/text/NativeLineBreaker.java +++ b/core/java/android/text/NativeLineBreaker.java @@ -17,6 +17,7 @@ package android.text; import android.annotation.FloatRange; +import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; @@ -26,12 +27,233 @@ import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + /** * A native implementation of the line breaker. * TODO: Consider to make this class public. * @hide */ public class NativeLineBreaker { + @IntDef(prefix = { "BREAK_STRATEGY_" }, value = { + BREAK_STRATEGY_SIMPLE, + BREAK_STRATEGY_HIGH_QUALITY, + BREAK_STRATEGY_BALANCED + }) + @Retention(RetentionPolicy.SOURCE) + public @interface BreakStrategy {} + + /** + * Value for break strategy indicating simple line breaking. Automatic hyphens are not added + * (though soft hyphens are respected), and modifying text generally doesn't affect the layout + * before it (which yields a more consistent user experience when editing), but layout may not + * be the highest quality. + */ + public static final int BREAK_STRATEGY_SIMPLE = 0; + + /** + * Value for break strategy indicating high quality line breaking, including automatic + * hyphenation and doing whole-paragraph optimization of line breaks. + */ + public static final int BREAK_STRATEGY_HIGH_QUALITY = 1; + + /** + * Value for break strategy indicating balanced line breaking. The breaks are chosen to + * make all lines as close to the same length as possible, including automatic hyphenation. + */ + public static final int BREAK_STRATEGY_BALANCED = 2; + + @IntDef(prefix = { "HYPHENATION_FREQUENCY_" }, value = { + HYPHENATION_FREQUENCY_NORMAL, + HYPHENATION_FREQUENCY_FULL, + HYPHENATION_FREQUENCY_NONE + }) + @Retention(RetentionPolicy.SOURCE) + public @interface HyphenationFrequency {} + + /** + * Value for hyphenation frequency indicating no automatic hyphenation. Useful + * for backward compatibility, and for cases where the automatic hyphenation algorithm results + * in incorrect hyphenation. Mid-word breaks may still happen when a word is wider than the + * layout and there is otherwise no valid break. Soft hyphens are ignored and will not be used + * as suggestions for potential line breaks. + */ + public static final int HYPHENATION_FREQUENCY_NONE = 0; + + /** + * Value for hyphenation frequency indicating a light amount of automatic hyphenation, which + * is a conservative default. Useful for informal cases, such as short sentences or chat + * messages. + */ + public static final int HYPHENATION_FREQUENCY_NORMAL = 1; + + /** + * Value for hyphenation frequency indicating the full amount of automatic hyphenation, typical + * in typography. Useful for running text and where it's important to put the maximum amount of + * text in a screen with limited space. + */ + public static final int HYPHENATION_FREQUENCY_FULL = 2; + + @IntDef(prefix = { "JUSTIFICATION_MODE_" }, value = { + JUSTIFICATION_MODE_NONE, + JUSTIFICATION_MODE_INTER_WORD + }) + @Retention(RetentionPolicy.SOURCE) + public @interface JustificationMode {} + + /** + * Value for justification mode indicating no justification. + */ + public static final int JUSTIFICATION_MODE_NONE = 0; + + /** + * Value for justification mode indicating the text is justified by stretching word spacing. + */ + public static final int JUSTIFICATION_MODE_INTER_WORD = 1; + + /** + * A builder class of NativeLineBreaker. + */ + public static class Builder { + private @BreakStrategy int mBreakStrategy = BREAK_STRATEGY_SIMPLE; + private @HyphenationFrequency int mHyphenationFrequency = HYPHENATION_FREQUENCY_NONE; + private @JustificationMode int mJustified = JUSTIFICATION_MODE_NONE; + private @Nullable int[] mIndents = null; + + /** + * Construct a builder class. + */ + public Builder() {} + + /** + * Set break strategy. + */ + public Builder setBreakStrategy(@BreakStrategy int breakStrategy) { + mBreakStrategy = breakStrategy; + return this; + } + + /** + * Set hyphenation frequency. + */ + public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) { + mHyphenationFrequency = hyphenationFrequency; + return this; + } + + /** + * Set whether the text is justified. + */ + public Builder setJustified(@JustificationMode int justified) { + mJustified = justified; + return this; + } + + /** + * Set indents for entire text. + * + * Sets the total (left + right) indents in pixel per lines. + */ + public Builder setIndents(@Nullable int[] indents) { + mIndents = indents; + return this; + } + + /** + * Returns the NativeLineBreaker with given parameters. + */ + NativeLineBreaker build() { + return new NativeLineBreaker(mBreakStrategy, mHyphenationFrequency, mJustified, + mIndents); + } + } + + /** + * Line breaking constraints for single paragraph. + */ + public static class ParagraphConstraints { + private @FloatRange(from = 0.0f) float mWidth = 0; + private @FloatRange(from = 0.0f) float mFirstWidth = 0; + private @IntRange(from = 0) int mFirstWidthLineCount = 0; + private @Nullable int[] mVariableTabStops = null; + private @IntRange(from = 0) int mDefaultTabStop = 0; + + public ParagraphConstraints() {} + + /** + * Set width for this paragraph. + */ + public void setWidth(@FloatRange(from = 0.0f) float width) { + mWidth = width; + } + + /** + * Set indent for this paragraph. + * + * @param firstWidth the line width of the starting of the paragraph + * @param firstWidthLineCount the number of lines that applies the firstWidth + */ + public void setIndent(@FloatRange(from = 0.0f) float firstWidth, + @IntRange(from = 0) int firstWidthLineCount) { + mFirstWidth = firstWidth; + mFirstWidthLineCount = firstWidthLineCount; + } + + /** + * Set tab stops for this paragraph. + * + * @param tabStops the array of pixels of tap stopping position + * @param defaultTabStop pixels of the default tab stopping position + */ + public void setTabStops(@Nullable int[] tabStops, @IntRange(from = 0) int defaultTabStop) { + mVariableTabStops = tabStops; + mDefaultTabStop = defaultTabStop; + } + + /** + * Return the width for this paragraph in pixels. + */ + public @FloatRange(from = 0.0f) float getWidth() { + return mWidth; + } + + /** + * Return the first line's width for this paragraph in pixel. + * + * @see #setIndent(float, int) + */ + public @FloatRange(from = 0.0f) float getFirstWidth() { + return mFirstWidth; + } + + /** + * Return the number of lines to apply the first line's width. + * + * @see #setIndent(float, int) + */ + public @IntRange(from = 0) int getFirstWidthLineCount() { + return mFirstWidthLineCount; + } + + /** + * Returns the array of tab stops in pixels. + * + * @see #setTabStops(int[], int) + */ + public @Nullable int[] getTabStops() { + return mVariableTabStops; + } + + /** + * Returns the default tab stops in pixels. + * + * @see #setTabStop(int[], int) + */ + public @IntRange(from = 0) int getDefaultTabStop() { + return mDefaultTabStop; + } + } /** * A result object of a line breaking @@ -43,6 +265,7 @@ public class NativeLineBreaker { 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 } @@ -53,54 +276,44 @@ public class NativeLineBreaker { private final long mNativePtr; /** - * A constructor of NativeLineBreaker + * Use Builder instead. */ - public NativeLineBreaker(@Layout.BreakStrategy int breakStrategy, - @Layout.HyphenationFrequency int hyphenationFrequency, - boolean justify, @Nullable int[] indents) { - mNativePtr = nInit(breakStrategy, hyphenationFrequency, justify, indents); + private NativeLineBreaker(@BreakStrategy int breakStrategy, + @HyphenationFrequency int hyphenationFrequency, @JustificationMode int justify, + @Nullable int[] indents) { + mNativePtr = nInit(breakStrategy, hyphenationFrequency, + justify == JUSTIFICATION_MODE_INTER_WORD, indents); sRegistry.registerNativeAllocation(this, mNativePtr); } /** - * Break text into lines + * Break paragraph into lines. + * + * The result is filled to out param. * - * @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 + * @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 */ - @NonNull public int computeLineBreaks( - @NonNull char[] chars, + public void computeLineBreaks( @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 ParagraphConstraints constraints, + @IntRange(from = 0) int lineNumber, @NonNull LineBreaks out) { - return nComputeLineBreaks( + out.breakCount = nComputeLineBreaks( mNativePtr, // Inputs - chars, + measuredPara.getChars(), measuredPara.getNativePtr(), - length, - firstWidth, - firstWidthLineCount, - restWidth, - variableTabStops, - defaultTabStop, - indentsOffset, + measuredPara.getChars().length, + constraints.mFirstWidth, + constraints.mFirstWidthLineCount, + constraints.mWidth, + constraints.mVariableTabStops, + constraints.mDefaultTabStop, + lineNumber, // Outputs out, @@ -114,10 +327,8 @@ public class NativeLineBreaker { } @FastNative - private static native long nInit( - @Layout.BreakStrategy int breakStrategy, - @Layout.HyphenationFrequency int hyphenationFrequency, - boolean isJustified, + private static native long nInit(@BreakStrategy int breakStrategy, + @HyphenationFrequency int hyphenationFrequency, boolean isJustified, @Nullable int[] indents); @CriticalNative diff --git a/core/java/android/text/NativeMeasuredParagraph.java b/core/java/android/text/NativeMeasuredParagraph.java index d03674f86109..bfdccca2955b 100644 --- a/core/java/android/text/NativeMeasuredParagraph.java +++ b/core/java/android/text/NativeMeasuredParagraph.java @@ -36,10 +36,19 @@ public class NativeMeasuredParagraph { NativeMeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024); private long mNativePtr; + private @NonNull char[] mChars; // Use builder instead. - private NativeMeasuredParagraph(long ptr) { + private NativeMeasuredParagraph(long ptr, @NonNull char[] chars) { mNativePtr = ptr; + mChars = chars; + } + + /** + * Returns a characters of this paragraph. + */ + public char[] getChars() { + return mChars; } /** @@ -126,7 +135,7 @@ public class NativeMeasuredParagraph { try { long ptr = nBuildNativeMeasuredParagraph(mNativePtr, text, computeHyphenation, computeLayout); - NativeMeasuredParagraph res = new NativeMeasuredParagraph(ptr); + NativeMeasuredParagraph res = new NativeMeasuredParagraph(ptr, text); sRegistry.registerNativeAllocation(res, ptr); return res; } finally { diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index dc01178395e2..128f860d4838 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -626,11 +626,16 @@ public class StaticLayout extends Layout { indents = null; } - final NativeLineBreaker lineBreaker = new NativeLineBreaker( - b.mBreakStrategy, b.mHyphenationFrequency, + final NativeLineBreaker lineBreaker = new NativeLineBreaker.Builder() + .setBreakStrategy(b.mBreakStrategy) + .setHyphenationFrequency(b.mHyphenationFrequency) // TODO: Support more justification mode, e.g. letter spacing, stretching. - b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, - indents); + .setJustified(b.mJustificationMode) + .setIndents(indents) + .build(); + + NativeLineBreaker.ParagraphConstraints constraints = + new NativeLineBreaker.ParagraphConstraints(); PrecomputedText.ParagraphInfo[] paragraphInfo = null; final Spanned spanned = (source instanceof Spanned) ? (Spanned) source : null; @@ -721,18 +726,14 @@ public class StaticLayout extends Layout { final char[] chs = measuredPara.getChars(); final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray(); final int[] fmCache = measuredPara.getFontMetrics().getRawArray(); - int breakCount = lineBreaker.computeLineBreaks( - measuredPara.getChars(), - measuredPara.getNativeMeasuredParagraph(), - paraEnd - paraStart, - firstWidth, - firstWidthLineCount, - restWidth, - variableTabStops, - TAB_INCREMENT, - mLineCount, - lineBreaks); + constraints.setWidth(restWidth); + 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; |