diff options
-rw-r--r-- | core/java/android/text/MeasuredParagraph.java | 166 | ||||
-rw-r--r-- | core/java/android/text/NativeLineBreaker.java | 155 | ||||
-rw-r--r-- | core/java/android/text/NativeMeasuredParagraph.java | 176 | ||||
-rw-r--r-- | core/java/android/text/PrecomputedText.java | 15 | ||||
-rw-r--r-- | core/java/android/text/StaticLayout.java | 468 | ||||
-rw-r--r-- | core/java/android/view/RecordingCanvas.java | 2 | ||||
-rw-r--r-- | core/jni/Android.bp | 2 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 4 | ||||
-rw-r--r-- | core/jni/android_text_LineBreaker.cpp (renamed from core/jni/android_text_StaticLayout.cpp) | 23 | ||||
-rw-r--r-- | core/jni/android_text_MeasuredParagraph.cpp | 14 | ||||
-rw-r--r-- | core/tests/coretests/src/android/text/MeasuredParagraphTest.java | 13 | ||||
-rw-r--r-- | graphics/java/android/graphics/BaseCanvas.java | 2 |
12 files changed, 623 insertions, 417 deletions
diff --git a/core/java/android/text/MeasuredParagraph.java b/core/java/android/text/MeasuredParagraph.java index c2c3182c2abd..9bf8cd20f441 100644 --- a/core/java/android/text/MeasuredParagraph.java +++ b/core/java/android/text/MeasuredParagraph.java @@ -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); } diff --git a/core/java/android/text/NativeLineBreaker.java b/core/java/android/text/NativeLineBreaker.java new file mode 100644 index 000000000000..a31b3361d970 --- /dev/null +++ b/core/java/android/text/NativeLineBreaker.java @@ -0,0 +1,155 @@ +/* + * 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); +} diff --git a/core/java/android/text/NativeMeasuredParagraph.java b/core/java/android/text/NativeMeasuredParagraph.java new file mode 100644 index 000000000000..d03674f86109 --- /dev/null +++ b/core/java/android/text/NativeMeasuredParagraph.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2010 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.graphics.Paint; +import android.graphics.Rect; + +import dalvik.annotation.optimization.CriticalNative; + +import libcore.util.NativeAllocationRegistry; + +/** + * A native implementation of measured paragraph. + * TODO: Consider to make this class public. + * @hide + */ +public class NativeMeasuredParagraph { + private static final NativeAllocationRegistry sRegistry = new NativeAllocationRegistry( + NativeMeasuredParagraph.class.getClassLoader(), nGetReleaseFunc(), 1024); + + private long mNativePtr; + + // Use builder instead. + private NativeMeasuredParagraph(long ptr) { + mNativePtr = ptr; + } + + /** + * Returns a width of the given region + */ + public float getWidth(int start, int end) { + return nGetWidth(mNativePtr, start, end); + } + + /** + * Returns a memory usage of the native object. + */ + public int getMemoryUsage() { + return nGetMemoryUsage(mNativePtr); + } + + /** + * Fills the boundary box of the given region + */ + public void getBounds(char[] buf, int start, int end, Rect rect) { + nGetBounds(mNativePtr, buf, start, end, rect); + } + + /** + * Returns the width of the character at the given offset + */ + public float getCharWidthAt(int offset) { + return nGetCharWidthAt(mNativePtr, offset); + } + + /** + * Returns a native pointer of the underlying native object. + */ + public long getNativePtr() { + return mNativePtr; + } + + @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); + + @CriticalNative + private static native float nGetCharWidthAt(long nativePtr, int offset); + + /** + * A builder for the NativeMeasuredParagraph + */ + public static class Builder { + private final long mNativePtr; + + public Builder() { + mNativePtr = nInitBuilder(); + } + + /** + * Apply styles to given range + */ + public void addStyleRun(@NonNull Paint paint, int start, int end, boolean isRtl) { + nAddStyleRun(mNativePtr, paint.getNativeInstance(), start, end, isRtl); + } + + /** + * Tells native that the given range is replaced with the object of given width. + */ + public void addReplacementRun(@NonNull Paint paint, int start, int end, float width) { + nAddReplacementRun(mNativePtr, paint.getNativeInstance(), start, end, width); + } + + /** + * Build the NativeMeasuredParagraph + */ + public NativeMeasuredParagraph build(char[] text, boolean computeHyphenation, + boolean computeLayout) { + try { + long ptr = nBuildNativeMeasuredParagraph(mNativePtr, text, computeHyphenation, + computeLayout); + NativeMeasuredParagraph res = new NativeMeasuredParagraph(ptr); + sRegistry.registerNativeAllocation(res, ptr); + return res; + } finally { + nFreeBuilder(mNativePtr); + } + } + + 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); + } +} diff --git a/core/java/android/text/PrecomputedText.java b/core/java/android/text/PrecomputedText.java index 027ead30c9ae..b7ea0122cb06 100644 --- a/core/java/android/text/PrecomputedText.java +++ b/core/java/android/text/PrecomputedText.java @@ -518,6 +518,21 @@ public class PrecomputedText implements Spannable { } /** + * 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. * * Note that this is not guaranteed to be accurate. Must be used only for testing purposes. diff --git a/core/java/android/text/StaticLayout.java b/core/java/android/text/StaticLayout.java index 4b78aa2332d5..6dad23815bef 100644 --- a/core/java/android/text/StaticLayout.java +++ b/core/java/android/text/StaticLayout.java @@ -21,7 +21,6 @@ import android.annotation.IntRange; import android.annotation.NonNull; import android.annotation.Nullable; import android.graphics.Paint; -import android.text.AutoGrowArray.FloatArray; import android.text.style.LeadingMarginSpan; import android.text.style.LeadingMarginSpan.LeadingMarginSpan2; import android.text.style.LineHeightSpan; @@ -32,9 +31,6 @@ import android.util.Pools.SynchronizedPool; import com.android.internal.util.ArrayUtils; import com.android.internal.util.GrowingArrayUtils; -import dalvik.annotation.optimization.CriticalNative; -import dalvik.annotation.optimization.FastNative; - import java.util.Arrays; /** @@ -57,7 +53,7 @@ public class StaticLayout extends Layout { * * - Create MeasuredParagraph by MeasuredParagraph.buildForStaticLayout which measures in * native. - * - Run nComputeLineBreaks() to obtain line breaks for the paragraph. + * - Run NativeLineBreaker.computeLineBreaks() to obtain line breaks for the paragraph. * * After all paragraphs, call finish() to release expensive buffers. */ @@ -586,8 +582,7 @@ public class StaticLayout extends Layout { float ellipsizedWidth = b.mEllipsizedWidth; TextUtils.TruncateAt ellipsize = b.mEllipsize; final boolean addLastLineSpacing = b.mAddLastLineLineSpacing; - LineBreaks lineBreaks = new LineBreaks(); // TODO: move to builder to avoid allocation costs - FloatArray widths = new FloatArray(); + NativeLineBreaker.LineBreaks lineBreaks = new NativeLineBreaker.LineBreaks(); mLineCount = 0; mEllipsized = false; @@ -615,7 +610,7 @@ public class StaticLayout extends Layout { indents = null; } - final long nativePtr = nInit( + final NativeLineBreaker lineBreaker = new NativeLineBreaker( b.mBreakStrategy, b.mHyphenationFrequency, // TODO: Support more justification mode, e.g. letter spacing, stretching. b.mJustificationMode != Layout.JUSTIFICATION_MODE_NONE, @@ -639,243 +634,219 @@ public class StaticLayout extends Layout { bufEnd, false /* computeLayout */); } - try { - for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) { - final int paraStart = paraIndex == 0 - ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd; - final int paraEnd = paragraphInfo[paraIndex].paragraphEnd; - - int firstWidthLineCount = 1; - int firstWidth = outerWidth; - int restWidth = outerWidth; - - LineHeightSpan[] chooseHt = null; - - if (spanned != null) { - LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, - LeadingMarginSpan.class); - for (int i = 0; i < sp.length; i++) { - LeadingMarginSpan lms = sp[i]; - firstWidth -= sp[i].getLeadingMargin(true); - restWidth -= sp[i].getLeadingMargin(false); - - // LeadingMarginSpan2 is odd. The count affects all - // leading margin spans, not just this particular one - if (lms instanceof LeadingMarginSpan2) { - LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms; - firstWidthLineCount = Math.max(firstWidthLineCount, - lms2.getLeadingMarginLineCount()); - } + for (int paraIndex = 0; paraIndex < paragraphInfo.length; paraIndex++) { + final int paraStart = paraIndex == 0 + ? bufStart : paragraphInfo[paraIndex - 1].paragraphEnd; + final int paraEnd = paragraphInfo[paraIndex].paragraphEnd; + + int firstWidthLineCount = 1; + int firstWidth = outerWidth; + int restWidth = outerWidth; + + LineHeightSpan[] chooseHt = null; + if (spanned != null) { + LeadingMarginSpan[] sp = getParagraphSpans(spanned, paraStart, paraEnd, + LeadingMarginSpan.class); + for (int i = 0; i < sp.length; i++) { + LeadingMarginSpan lms = sp[i]; + firstWidth -= sp[i].getLeadingMargin(true); + restWidth -= sp[i].getLeadingMargin(false); + + // LeadingMarginSpan2 is odd. The count affects all + // leading margin spans, not just this particular one + if (lms instanceof LeadingMarginSpan2) { + LeadingMarginSpan2 lms2 = (LeadingMarginSpan2) lms; + firstWidthLineCount = Math.max(firstWidthLineCount, + lms2.getLeadingMarginLineCount()); } + } - chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); + chooseHt = getParagraphSpans(spanned, paraStart, paraEnd, LineHeightSpan.class); - if (chooseHt.length == 0) { - chooseHt = null; // So that out() would not assume it has any contents - } else { - if (chooseHtv == null || chooseHtv.length < chooseHt.length) { - chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length); - } + if (chooseHt.length == 0) { + chooseHt = null; // So that out() would not assume it has any contents + } else { + if (chooseHtv == null || chooseHtv.length < chooseHt.length) { + chooseHtv = ArrayUtils.newUnpaddedIntArray(chooseHt.length); + } - for (int i = 0; i < chooseHt.length; i++) { - int o = spanned.getSpanStart(chooseHt[i]); + for (int i = 0; i < chooseHt.length; i++) { + int o = spanned.getSpanStart(chooseHt[i]); - if (o < paraStart) { - // starts in this layout, before the - // current paragraph + if (o < paraStart) { + // starts in this layout, before the + // current paragraph - chooseHtv[i] = getLineTop(getLineForOffset(o)); - } else { - // starts in this paragraph + chooseHtv[i] = getLineTop(getLineForOffset(o)); + } else { + // starts in this paragraph - chooseHtv[i] = v; - } + chooseHtv[i] = v; } } } - - // tab stop locations - int[] variableTabStops = null; - if (spanned != null) { - TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, - paraEnd, TabStopSpan.class); - if (spans.length > 0) { - int[] stops = new int[spans.length]; - for (int i = 0; i < spans.length; i++) { - stops[i] = spans[i].getTabStop(); - } - Arrays.sort(stops, 0, stops.length); - variableTabStops = stops; + } + // tab stop locations + int[] variableTabStops = null; + if (spanned != null) { + TabStopSpan[] spans = getParagraphSpans(spanned, paraStart, + paraEnd, TabStopSpan.class); + if (spans.length > 0) { + int[] stops = new int[spans.length]; + for (int i = 0; i < spans.length; i++) { + stops[i] = spans[i].getTabStop(); } + Arrays.sort(stops, 0, stops.length); + variableTabStops = stops; } + } - final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured; - final char[] chs = measuredPara.getChars(); - final int[] spanEndCache = measuredPara.getSpanEndCache().getRawArray(); - final int[] fmCache = measuredPara.getFontMetrics().getRawArray(); - // TODO: Stop keeping duplicated width copy in native and Java. - widths.resize(chs.length); - - // measurement has to be done before performing line breaking - // but we don't want to recompute fontmetrics or span ranges the - // second time, so we cache those and then use those stored values - - int breakCount = nComputeLineBreaks( - nativePtr, - - // Inputs - chs, - measuredPara.getNativePtr(), - paraEnd - paraStart, - firstWidth, - firstWidthLineCount, - restWidth, - variableTabStops, - TAB_INCREMENT, - mLineCount, - - // Outputs - lineBreaks, - lineBreaks.breaks.length, - lineBreaks.breaks, - lineBreaks.widths, - lineBreaks.ascents, - lineBreaks.descents, - lineBreaks.flags, - widths.getRawArray()); - - final int[] breaks = lineBreaks.breaks; - final float[] lineWidths = lineBreaks.widths; - final float[] ascents = lineBreaks.ascents; - final float[] descents = lineBreaks.descents; - final int[] flags = lineBreaks.flags; - - final int remainingLineCount = mMaximumVisibleLineCount - mLineCount; - final boolean ellipsisMayBeApplied = ellipsize != null - && (ellipsize == TextUtils.TruncateAt.END - || (mMaximumVisibleLineCount == 1 - && ellipsize != TextUtils.TruncateAt.MARQUEE)); - if (0 < remainingLineCount && remainingLineCount < breakCount - && ellipsisMayBeApplied) { - // Calculate width and flag. - float width = 0; - int flag = 0; // XXX May need to also have starting hyphen edit - for (int i = remainingLineCount - 1; i < breakCount; i++) { - if (i == breakCount - 1) { - width += lineWidths[i]; - } else { - for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) { - width += widths.get(j); - } + final MeasuredParagraph measuredPara = paragraphInfo[paraIndex].measured; + 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); + + final int[] breaks = lineBreaks.breaks; + final float[] lineWidths = lineBreaks.widths; + final float[] ascents = lineBreaks.ascents; + final float[] descents = lineBreaks.descents; + final int[] flags = lineBreaks.flags; + + final int remainingLineCount = mMaximumVisibleLineCount - mLineCount; + final boolean ellipsisMayBeApplied = ellipsize != null + && (ellipsize == TextUtils.TruncateAt.END + || (mMaximumVisibleLineCount == 1 + && ellipsize != TextUtils.TruncateAt.MARQUEE)); + if (0 < remainingLineCount && remainingLineCount < breakCount + && ellipsisMayBeApplied) { + // Calculate width + float width = 0; + int flag = 0; // XXX May need to also have starting hyphen edit + for (int i = remainingLineCount - 1; i < breakCount; i++) { + if (i == breakCount - 1) { + width += lineWidths[i]; + } else { + for (int j = (i == 0 ? 0 : breaks[i - 1]); j < breaks[i]; j++) { + width += measuredPara.getCharWidthAt(j - paraStart); } - flag |= flags[i] & TAB_MASK; } - // Treat the last line and overflowed lines as a single line. - breaks[remainingLineCount - 1] = breaks[breakCount - 1]; - lineWidths[remainingLineCount - 1] = width; - flags[remainingLineCount - 1] = flag; + flag |= flags[i] & TAB_MASK; + } + // Treat the last line and overflowed lines as a single line. + breaks[remainingLineCount - 1] = breaks[breakCount - 1]; + lineWidths[remainingLineCount - 1] = width; + flags[remainingLineCount - 1] = flag; + + breakCount = remainingLineCount; + } + + // here is the offset of the starting character of the line we are currently + // measuring + int here = paraStart; + + int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0; + int fmCacheIndex = 0; + int spanEndCacheIndex = 0; + int breakIndex = 0; + for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { + // retrieve end of span + spanEnd = spanEndCache[spanEndCacheIndex++]; + + // retrieve cached metrics, order matches above + fm.top = fmCache[fmCacheIndex * 4 + 0]; + fm.bottom = fmCache[fmCacheIndex * 4 + 1]; + fm.ascent = fmCache[fmCacheIndex * 4 + 2]; + fm.descent = fmCache[fmCacheIndex * 4 + 3]; + fmCacheIndex++; + + if (fm.top < fmTop) { + fmTop = fm.top; + } + if (fm.ascent < fmAscent) { + fmAscent = fm.ascent; + } + if (fm.descent > fmDescent) { + fmDescent = fm.descent; + } + if (fm.bottom > fmBottom) { + fmBottom = fm.bottom; + } - breakCount = remainingLineCount; + // skip breaks ending before current span range + while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) { + breakIndex++; } - // here is the offset of the starting character of the line we are currently - // measuring - int here = paraStart; - - int fmTop = 0, fmBottom = 0, fmAscent = 0, fmDescent = 0; - int fmCacheIndex = 0; - int spanEndCacheIndex = 0; - int breakIndex = 0; - for (int spanStart = paraStart, spanEnd; spanStart < paraEnd; spanStart = spanEnd) { - // retrieve end of span - spanEnd = spanEndCache[spanEndCacheIndex++]; - - // retrieve cached metrics, order matches above - fm.top = fmCache[fmCacheIndex * 4 + 0]; - fm.bottom = fmCache[fmCacheIndex * 4 + 1]; - fm.ascent = fmCache[fmCacheIndex * 4 + 2]; - fm.descent = fmCache[fmCacheIndex * 4 + 3]; - fmCacheIndex++; - - if (fm.top < fmTop) { + while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) { + int endPos = paraStart + breaks[breakIndex]; + + boolean moreChars = (endPos < bufEnd); + + final int ascent = fallbackLineSpacing + ? Math.min(fmAscent, Math.round(ascents[breakIndex])) + : fmAscent; + final int descent = fallbackLineSpacing + ? Math.max(fmDescent, Math.round(descents[breakIndex])) + : fmDescent; + + v = out(source, here, endPos, + ascent, descent, fmTop, fmBottom, + v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, + flags[breakIndex], needMultiply, measuredPara, bufEnd, + includepad, trackpad, addLastLineSpacing, chs, + paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], + paint, moreChars); + + if (endPos < spanEnd) { + // preserve metrics for current span fmTop = fm.top; - } - if (fm.ascent < fmAscent) { + fmBottom = fm.bottom; fmAscent = fm.ascent; - } - if (fm.descent > fmDescent) { fmDescent = fm.descent; + } else { + fmTop = fmBottom = fmAscent = fmDescent = 0; } - if (fm.bottom > fmBottom) { - fmBottom = fm.bottom; - } - - // skip breaks ending before current span range - while (breakIndex < breakCount && paraStart + breaks[breakIndex] < spanStart) { - breakIndex++; - } - - while (breakIndex < breakCount && paraStart + breaks[breakIndex] <= spanEnd) { - int endPos = paraStart + breaks[breakIndex]; - - boolean moreChars = (endPos < bufEnd); - - final int ascent = fallbackLineSpacing - ? Math.min(fmAscent, Math.round(ascents[breakIndex])) - : fmAscent; - final int descent = fallbackLineSpacing - ? Math.max(fmDescent, Math.round(descents[breakIndex])) - : fmDescent; - v = out(source, here, endPos, - ascent, descent, fmTop, fmBottom, - v, spacingmult, spacingadd, chooseHt, chooseHtv, fm, - flags[breakIndex], needMultiply, measuredPara, bufEnd, - includepad, trackpad, addLastLineSpacing, chs, widths.getRawArray(), - paraStart, ellipsize, ellipsizedWidth, lineWidths[breakIndex], - paint, moreChars); - - if (endPos < spanEnd) { - // preserve metrics for current span - fmTop = fm.top; - fmBottom = fm.bottom; - fmAscent = fm.ascent; - fmDescent = fm.descent; - } else { - fmTop = fmBottom = fmAscent = fmDescent = 0; - } - here = endPos; - breakIndex++; + here = endPos; + breakIndex++; - if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) { - return; - } + if (mLineCount >= mMaximumVisibleLineCount && mEllipsized) { + return; } } - - if (paraEnd == bufEnd) { - break; - } } - if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) - && mLineCount < mMaximumVisibleLineCount) { - final MeasuredParagraph measuredPara = - MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null); - paint.getFontMetricsInt(fm); - v = out(source, - bufEnd, bufEnd, fm.ascent, fm.descent, - fm.top, fm.bottom, - v, - spacingmult, spacingadd, null, - null, fm, 0, - needMultiply, measuredPara, bufEnd, - includepad, trackpad, addLastLineSpacing, null, - null, bufStart, ellipsize, - ellipsizedWidth, 0, paint, false); + if (paraEnd == bufEnd) { + break; } - } finally { - nFinish(nativePtr); + } + + if ((bufEnd == bufStart || source.charAt(bufEnd - 1) == CHAR_NEW_LINE) + && mLineCount < mMaximumVisibleLineCount) { + final MeasuredParagraph measuredPara = + MeasuredParagraph.buildForBidi(source, bufEnd, bufEnd, textDir, null); + paint.getFontMetricsInt(fm); + v = out(source, + bufEnd, bufEnd, fm.ascent, fm.descent, + fm.top, fm.bottom, + v, + spacingmult, spacingadd, null, + null, fm, 0, + needMultiply, measuredPara, bufEnd, + includepad, trackpad, addLastLineSpacing, null, + bufStart, ellipsize, + ellipsizedWidth, 0, paint, false); } } @@ -884,7 +855,7 @@ public class StaticLayout extends Layout { final LineHeightSpan[] chooseHt, final int[] chooseHtv, final Paint.FontMetricsInt fm, final int flags, final boolean needMultiply, @NonNull final MeasuredParagraph measured, final int bufEnd, final boolean includePad, final boolean trackPad, - final boolean addLastLineLineSpacing, final char[] chs, final float[] widths, + final boolean addLastLineLineSpacing, final char[] chs, final int widthStart, final TextUtils.TruncateAt ellipsize, final float ellipsisWidth, final float textWidth, final TextPaint paint, final boolean moreChars) { final int j = mLineCount; @@ -942,7 +913,7 @@ public class StaticLayout extends Layout { (!firstLine && (currentLineIsTheLastVisibleOne || !moreChars) && ellipsize == TextUtils.TruncateAt.END); if (doEllipsis) { - calculateEllipsis(start, end, widths, widthStart, + calculateEllipsis(start, end, measured, widthStart, ellipsisWidth, ellipsize, j, textWidth, paint, forceEllipsis); } @@ -1026,7 +997,7 @@ public class StaticLayout extends Layout { } private void calculateEllipsis(int lineStart, int lineEnd, - float[] widths, int widthStart, + MeasuredParagraph measured, int widthStart, float avail, TextUtils.TruncateAt where, int line, float textWidth, TextPaint paint, boolean forceEllipsis) { @@ -1050,9 +1021,10 @@ public class StaticLayout extends Layout { int i; for (i = len; i > 0; i--) { - float w = widths[i - 1 + lineStart - widthStart]; + float w = measured.getCharWidthAt(i - 1 + lineStart - widthStart); if (w + sum + ellipsisWidth > avail) { - while (i < len && widths[i + lineStart - widthStart] == 0.0f) { + while (i < len + && measured.getCharWidthAt(i + lineStart - widthStart) == 0.0f) { i++; } break; @@ -1074,7 +1046,7 @@ public class StaticLayout extends Layout { int i; for (i = 0; i < len; i++) { - float w = widths[i + lineStart - widthStart]; + float w = measured.getCharWidthAt(i + lineStart - widthStart); if (w + sum + ellipsisWidth > avail) { break; @@ -1097,10 +1069,12 @@ public class StaticLayout extends Layout { float ravail = (avail - ellipsisWidth) / 2; for (right = len; right > 0; right--) { - float w = widths[right - 1 + lineStart - widthStart]; + float w = measured.getCharWidthAt(right - 1 + lineStart - widthStart); if (w + rsum > ravail) { - while (right < len && widths[right + lineStart - widthStart] == 0.0f) { + while (right < len + && measured.getCharWidthAt(right + lineStart - widthStart) + == 0.0f) { right++; } break; @@ -1110,7 +1084,7 @@ public class StaticLayout extends Layout { float lavail = avail - ellipsisWidth - rsum; for (left = 0; left < right; left++) { - float w = widths[left + lineStart - widthStart]; + float w = measured.getCharWidthAt(left + lineStart - widthStart); if (w + lsum > lavail) { break; @@ -1306,47 +1280,6 @@ public class StaticLayout extends Layout { ? mMaxLineHeight : super.getHeight(); } - @FastNative - private static native long nInit( - @BreakStrategy int breakStrategy, - @HyphenationFrequency int hyphenationFrequency, - boolean isJustified, - @Nullable int[] indents); - - @CriticalNative - private static native void nFinish(long nativePtr); - - // 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, - @NonNull float[] charWidths); - private int mLineCount; private int mTopPadding, mBottomPadding; private int mColumns; @@ -1396,8 +1329,7 @@ public class StaticLayout extends Layout { private static final int DEFAULT_MAX_LINE_HEIGHT = -1; - // This is used to return three arrays from a single JNI call when - // performing line breaking + // Unused, here because of gray list private API accesses. /*package*/ static class LineBreaks { private static final int INITIAL_SIZE = 16; public int[] breaks = new int[INITIAL_SIZE]; diff --git a/core/java/android/view/RecordingCanvas.java b/core/java/android/view/RecordingCanvas.java index 18cc10f5b2f2..74fa9e875076 100644 --- a/core/java/android/view/RecordingCanvas.java +++ b/core/java/android/view/RecordingCanvas.java @@ -515,7 +515,7 @@ public class RecordingCanvas extends Canvas { contextStart - paraStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(), - mp.getNativePtr()); + mp.getNativeMeasuredParagraph().getNativePtr()); return; } } diff --git a/core/jni/Android.bp b/core/jni/Android.bp index b675698a461b..3d80f3d91f59 100644 --- a/core/jni/Android.bp +++ b/core/jni/Android.bp @@ -84,8 +84,8 @@ cc_library_shared { "android_view_VelocityTracker.cpp", "android_text_AndroidCharacter.cpp", "android_text_Hyphenator.cpp", + "android_text_LineBreaker.cpp", "android_text_MeasuredParagraph.cpp", - "android_text_StaticLayout.cpp", "android_os_Debug.cpp", "android_os_GraphicsEnvironment.cpp", "android_os_HidlSupport.cpp", diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index bd2a8def2618..0c625a773df3 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -183,7 +183,7 @@ extern int register_android_net_NetworkUtils(JNIEnv* env); extern int register_android_text_AndroidCharacter(JNIEnv *env); extern int register_android_text_Hyphenator(JNIEnv *env); extern int register_android_text_MeasuredParagraph(JNIEnv* env); -extern int register_android_text_StaticLayout(JNIEnv *env); +extern int register_android_text_LineBreaker(JNIEnv *env); extern int register_android_opengl_classes(JNIEnv *env); extern int register_android_ddm_DdmHandleNativeHeap(JNIEnv *env); extern int register_android_server_NetworkManagementSocketTagger(JNIEnv* env); @@ -1334,7 +1334,7 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_text_AndroidCharacter), REG_JNI(register_android_text_Hyphenator), REG_JNI(register_android_text_MeasuredParagraph), - REG_JNI(register_android_text_StaticLayout), + REG_JNI(register_android_text_LineBreaker), REG_JNI(register_android_view_InputDevice), REG_JNI(register_android_view_KeyCharacterMap), REG_JNI(register_android_os_Process), diff --git a/core/jni/android_text_StaticLayout.cpp b/core/jni/android_text_LineBreaker.cpp index fec5b6995646..dac108ef5497 100644 --- a/core/jni/android_text_StaticLayout.cpp +++ b/core/jni/android_text_LineBreaker.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#define LOG_TAG "StaticLayout" +#define LOG_TAG "LineBreaker" #include "unicode/locid.h" #include "unicode/brkiter.h" @@ -76,11 +76,15 @@ static jlong nInit(JNIEnv* env, jclass /* unused */, jintArrayToFloatVector(env, indents))); } -// CriticalNative static void nFinish(jlong nativePtr) { delete toNative(nativePtr); } +// CriticalNative +static jlong nGetReleaseFunc() { + return reinterpret_cast<jlong>(nFinish); +} + static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks, jfloatArray recycleWidths, jfloatArray recycleAscents, jfloatArray recycleDescents, jintArray recycleFlags, @@ -144,9 +148,6 @@ static jint nComputeLineBreaks(JNIEnv* env, jclass, jlong nativePtr, recycleCopy(env, recycle, recycleBreaks, recycleWidths, recycleAscents, recycleDescents, recycleFlags, recycleLength, result); - env->SetFloatArrayRegion(charWidths, 0, measuredText->widths.size(), - measuredText->widths.data()); - return static_cast<jint>(result.breakPoints.size()); } @@ -160,7 +161,7 @@ static const JNINativeMethod gMethods[] = { ")J", (void*) nInit}, // Critical Natives - {"nFinish", "(J)V", (void*) nFinish}, + {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Regular JNI {"nComputeLineBreaks", "(" @@ -178,21 +179,20 @@ static const JNINativeMethod gMethods[] = { "I" // indentsOffset // Outputs - "Landroid/text/StaticLayout$LineBreaks;" // recycle + "Landroid/text/NativeLineBreaker$LineBreaks;" // recycle "I" // recycleLength "[I" // recycleBreaks "[F" // recycleWidths "[F" // recycleAscents "[F" // recycleDescents "[I" // recycleFlags - "[F" // charWidths ")I", (void*) nComputeLineBreaks} }; -int register_android_text_StaticLayout(JNIEnv* env) +int register_android_text_LineBreaker(JNIEnv* env) { gLineBreaks_class = MakeGlobalRefOrDie(env, - FindClassOrDie(env, "android/text/StaticLayout$LineBreaks")); + FindClassOrDie(env, "android/text/NativeLineBreaker$LineBreaks")); gLineBreaks_fieldID.breaks = GetFieldIDOrDie(env, gLineBreaks_class, "breaks", "[I"); gLineBreaks_fieldID.widths = GetFieldIDOrDie(env, gLineBreaks_class, "widths", "[F"); @@ -200,7 +200,8 @@ int register_android_text_StaticLayout(JNIEnv* env) gLineBreaks_fieldID.descents = GetFieldIDOrDie(env, gLineBreaks_class, "descents", "[F"); gLineBreaks_fieldID.flags = GetFieldIDOrDie(env, gLineBreaks_class, "flags", "[I"); - return RegisterMethodsOrDie(env, "android/text/StaticLayout", gMethods, NELEM(gMethods)); + return RegisterMethodsOrDie(env, "android/text/NativeLineBreaker", + gMethods, NELEM(gMethods)); } } diff --git a/core/jni/android_text_MeasuredParagraph.cpp b/core/jni/android_text_MeasuredParagraph.cpp index 9eb6f8d4189a..18f509c7ec60 100644 --- a/core/jni/android_text_MeasuredParagraph.cpp +++ b/core/jni/android_text_MeasuredParagraph.cpp @@ -109,6 +109,10 @@ static jfloat nGetWidth(jlong ptr, jint start, jint end) { return r; } +static jfloat nGetCharWidthAt(jlong ptr, jint offset) { + return toMeasuredParagraph(ptr)->widths[offset]; +} + // Regular JNI static void nGetBounds(JNIEnv* env, jobject, jlong ptr, jcharArray javaText, jint start, jint end, jobject bounds) { @@ -138,23 +142,29 @@ static jint nGetMemoryUsage(jlong ptr) { return static_cast<jint>(toMeasuredParagraph(ptr)->getMemoryUsage()); } -static const JNINativeMethod gMethods[] = { +static const JNINativeMethod gMTBuilderMethods[] = { // MeasuredParagraphBuilder native functions. {"nInitBuilder", "()J", (void*) nInitBuilder}, {"nAddStyleRun", "(JJIIZ)V", (void*) nAddStyleRun}, {"nAddReplacementRun", "(JJIIF)V", (void*) nAddReplacementRun}, {"nBuildNativeMeasuredParagraph", "(J[CZZ)J", (void*) nBuildNativeMeasuredParagraph}, {"nFreeBuilder", "(J)V", (void*) nFreeBuilder}, +}; +static const JNINativeMethod gMTMethods[] = { // MeasuredParagraph native functions. {"nGetWidth", "(JII)F", (void*) nGetWidth}, // Critical Natives {"nGetBounds", "(J[CIILandroid/graphics/Rect;)V", (void*) nGetBounds}, // Regular JNI {"nGetReleaseFunc", "()J", (void*) nGetReleaseFunc}, // Critical Natives {"nGetMemoryUsage", "(J)I", (void*) nGetMemoryUsage}, // Critical Native + {"nGetCharWidthAt", "(JI)F", (void*) nGetCharWidthAt}, // Critical Native }; int register_android_text_MeasuredParagraph(JNIEnv* env) { - return RegisterMethodsOrDie(env, "android/text/MeasuredParagraph", gMethods, NELEM(gMethods)); + return RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph", + gMTMethods, NELEM(gMTMethods)) + + RegisterMethodsOrDie(env, "android/text/NativeMeasuredParagraph$Builder", + gMTBuilderMethods, NELEM(gMTBuilderMethods)); } } diff --git a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java index 6f1d47d2dac2..f3d6013b8ee3 100644 --- a/core/tests/coretests/src/android/text/MeasuredParagraphTest.java +++ b/core/tests/coretests/src/android/text/MeasuredParagraphTest.java @@ -19,6 +19,7 @@ package android.text; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import android.content.Context; import android.graphics.Typeface; @@ -72,7 +73,7 @@ public class MeasuredParagraphTest { assertEquals(0, mt.getWidths().size()); assertEquals(0, mt.getSpanEndCache().size()); assertEquals(0, mt.getFontMetrics().size()); - assertEquals(0, mt.getNativePtr()); + assertNull(mt.getNativeMeasuredParagraph()); // Recycle it MeasuredParagraph mt2 = MeasuredParagraph.buildForBidi("_VVV_", 1, 4, RTL, mt); @@ -84,7 +85,7 @@ public class MeasuredParagraphTest { assertEquals(0, mt2.getWidths().size()); assertEquals(0, mt2.getSpanEndCache().size()); assertEquals(0, mt2.getFontMetrics().size()); - assertEquals(0, mt2.getNativePtr()); + assertNull(mt.getNativeMeasuredParagraph()); mt2.recycle(); } @@ -106,7 +107,7 @@ public class MeasuredParagraphTest { assertEquals(10, mt.getWidths().get(2), 0); assertEquals(0, mt.getSpanEndCache().size()); assertEquals(0, mt.getFontMetrics().size()); - assertEquals(0, mt.getNativePtr()); + assertNull(mt.getNativeMeasuredParagraph()); // Recycle it MeasuredParagraph mt2 = @@ -123,7 +124,7 @@ public class MeasuredParagraphTest { assertEquals(5, mt2.getWidths().get(2), 0); assertEquals(0, mt2.getSpanEndCache().size()); assertEquals(0, mt2.getFontMetrics().size()); - assertEquals(0, mt2.getNativePtr()); + assertNull(mt.getNativeMeasuredParagraph()); mt2.recycle(); } @@ -143,7 +144,7 @@ public class MeasuredParagraphTest { assertEquals(1, mt.getSpanEndCache().size()); assertEquals(3, mt.getSpanEndCache().get(0)); assertNotEquals(0, mt.getFontMetrics().size()); - assertNotEquals(0, mt.getNativePtr()); + assertNotNull(mt.getNativeMeasuredParagraph()); // Recycle it MeasuredParagraph mt2 = @@ -158,7 +159,7 @@ public class MeasuredParagraphTest { assertEquals(1, mt2.getSpanEndCache().size()); assertEquals(4, mt2.getSpanEndCache().get(0)); assertNotEquals(0, mt2.getFontMetrics().size()); - assertNotEquals(0, mt2.getNativePtr()); + assertNotNull(mt.getNativeMeasuredParagraph()); mt2.recycle(); } diff --git a/graphics/java/android/graphics/BaseCanvas.java b/graphics/java/android/graphics/BaseCanvas.java index 97130f166eb2..ffe6abc13aae 100644 --- a/graphics/java/android/graphics/BaseCanvas.java +++ b/graphics/java/android/graphics/BaseCanvas.java @@ -501,7 +501,7 @@ public abstract class BaseCanvas { contextStart - paraStart, contextEnd - contextStart, x, y, isRtl, paint.getNativeInstance(), - mp.getNativePtr()); + mp.getNativeMeasuredParagraph().getNativePtr()); return; } } |