diff options
-rw-r--r-- | api/current.txt | 4 | ||||
-rw-r--r-- | api/system-current.txt | 4 | ||||
-rw-r--r-- | core/jni/android/graphics/Paint.cpp | 76 | ||||
-rw-r--r-- | graphics/java/android/graphics/Paint.java | 168 |
4 files changed, 239 insertions, 13 deletions
diff --git a/api/current.txt b/api/current.txt index d79ccbe..51c76db 100644 --- a/api/current.txt +++ b/api/current.txt @@ -11366,8 +11366,12 @@ package android.graphics { method public int getHinting(); method public float getLetterSpacing(); method public android.graphics.MaskFilter getMaskFilter(); + method public int getOffsetForAdvance(char[], int, int, int, int, boolean, float); + method public int getOffsetForAdvance(java.lang.CharSequence, int, int, int, int, boolean, float); method public android.graphics.PathEffect getPathEffect(); method public deprecated android.graphics.Rasterizer getRasterizer(); + method public float getRunAdvance(char[], int, int, int, int, boolean, int); + method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int); method public android.graphics.Shader getShader(); method public android.graphics.Paint.Cap getStrokeCap(); method public android.graphics.Paint.Join getStrokeJoin(); diff --git a/api/system-current.txt b/api/system-current.txt index e57eee5..65dbb3d 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -11656,8 +11656,12 @@ package android.graphics { method public int getHinting(); method public float getLetterSpacing(); method public android.graphics.MaskFilter getMaskFilter(); + method public int getOffsetForAdvance(char[], int, int, int, int, boolean, float); + method public int getOffsetForAdvance(java.lang.CharSequence, int, int, int, int, boolean, float); method public android.graphics.PathEffect getPathEffect(); method public deprecated android.graphics.Rasterizer getRasterizer(); + method public float getRunAdvance(char[], int, int, int, int, boolean, int); + method public float getRunAdvance(java.lang.CharSequence, int, int, int, int, boolean, int); method public android.graphics.Shader getShader(); method public android.graphics.Paint.Cap getStrokeCap(); method public android.graphics.Paint.Join getStrokeJoin(); diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp index 873b516..ed41869 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -22,8 +22,8 @@ #include "jni.h" #include "GraphicsJNI.h" #include "core_jni_helpers.h" -#include <ScopedUtfChars.h> #include <ScopedStringChars.h> +#include <ScopedUtfChars.h> #include "SkBlurDrawLooper.h" #include "SkColorFilter.h" @@ -37,6 +37,7 @@ #include "utils/Blur.h" #include <minikin/GraphemeBreak.h> +#include <minikin/Measurement.h> #include "MinikinSkia.h" #include "MinikinUtils.h" #include "Paint.h" @@ -1037,6 +1038,48 @@ public: return nGlyphs > 0 && !layoutContainsNotdef(layout); } + static jfloat doRunAdvance(const Paint* paint, TypefaceImpl* typeface, const jchar buf[], + jint start, jint count, jint bufSize, jboolean isRtl, jint offset) { + Layout layout; + int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR; + MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, buf, start, count, bufSize); + return getRunAdvance(layout, buf, start, count, offset); + } + + static jfloat getRunAdvance___CIIIIZI_F(JNIEnv *env, jclass, jlong paintHandle, + jlong typefaceHandle, jcharArray text, jint start, jint end, jint contextStart, + jint contextEnd, jboolean isRtl, jint offset) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); + // TODO performance: optimize JNI array access + jchar* textArray = env->GetCharArrayElements(text, NULL); + jfloat result = doRunAdvance(paint, typeface, textArray + contextStart, + start - contextStart, end - start, contextEnd - contextStart, isRtl, offset); + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); + return result; + } + + static jint doOffsetForAdvance(const Paint* paint, TypefaceImpl* typeface, const jchar buf[], + jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) { + Layout layout; + int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR; + MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, buf, start, count, bufSize); + return getOffsetForAdvance(layout, buf, start, count, advance); + } + static jint getOffsetForAdvance___CIIIIZF_I(JNIEnv *env, jclass, jlong paintHandle, + jlong typefaceHandle, jcharArray text, jint start, jint end, jint contextStart, + jint contextEnd, jboolean isRtl, jfloat advance) { + const Paint* paint = reinterpret_cast<Paint*>(paintHandle); + TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle); + // TODO performance: optimize JNI array access + jchar* textArray = env->GetCharArrayElements(text, NULL); + jint result = doOffsetForAdvance(paint, typeface, textArray + contextStart, + start - contextStart, end - start, contextEnd - contextStart, isRtl, advance); + result += contextStart; + env->ReleaseCharArrayElements(text, textArray, JNI_ABORT); + return result; + } + }; static JNINativeMethod methods[] = { @@ -1093,36 +1136,43 @@ static JNINativeMethod methods[] = { {"setTextSkewX","!(F)V", (void*) PaintGlue::setTextSkewX}, {"native_getLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing}, {"native_setLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing}, - {"native_setFontFeatureSettings","(JLjava/lang/String;)V", (void*) PaintGlue::setFontFeatureSettings}, + {"native_setFontFeatureSettings","(JLjava/lang/String;)V", + (void*) PaintGlue::setFontFeatureSettings}, {"native_getHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit}, {"native_setHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit}, {"ascent","!()F", (void*) PaintGlue::ascent}, {"descent","!()F", (void*) PaintGlue::descent}, - {"getFontMetrics", "(Landroid/graphics/Paint$FontMetrics;)F", (void*)PaintGlue::getFontMetrics}, - {"getFontMetricsInt", "(Landroid/graphics/Paint$FontMetricsInt;)I", (void*)PaintGlue::getFontMetricsInt}, + {"getFontMetrics", "(Landroid/graphics/Paint$FontMetrics;)F", + (void*)PaintGlue::getFontMetrics}, + {"getFontMetricsInt", "(Landroid/graphics/Paint$FontMetricsInt;)I", + (void*)PaintGlue::getFontMetricsInt}, {"native_measureText","([CIII)F", (void*) PaintGlue::measureText_CIII}, {"native_measureText","(Ljava/lang/String;I)F", (void*) PaintGlue::measureText_StringI}, {"native_measureText","(Ljava/lang/String;III)F", (void*) PaintGlue::measureText_StringIII}, {"native_breakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC}, {"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS}, {"native_getTextWidths","(JJ[CIII[F)I", (void*) PaintGlue::getTextWidths___CIII_F}, - {"native_getTextWidths","(JJLjava/lang/String;III[F)I", (void*) PaintGlue::getTextWidths__StringIII_F}, + {"native_getTextWidths","(JJLjava/lang/String;III[F)I", + (void*) PaintGlue::getTextWidths__StringIII_F}, {"native_getTextRunAdvances","(JJ[CIIIIZ[FI)F", - (void*) PaintGlue::getTextRunAdvances___CIIIIZ_FI}, + (void*) PaintGlue::getTextRunAdvances___CIIIIZ_FI}, {"native_getTextRunAdvances","(JJLjava/lang/String;IIIIZ[FI)F", - (void*) PaintGlue::getTextRunAdvances__StringIIIIZ_FI}, + (void*) PaintGlue::getTextRunAdvances__StringIIIIZ_FI}, {"native_getTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C}, {"native_getTextRunCursor", "(JLjava/lang/String;IIIII)I", - (void*) PaintGlue::getTextRunCursor__String}, - {"native_getTextPath","(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C}, - {"native_getTextPath","(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String}, + (void*) PaintGlue::getTextRunCursor__String}, + {"native_getTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C}, + {"native_getTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String}, {"nativeGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V", - (void*) PaintGlue::getStringBounds }, + (void*) PaintGlue::getStringBounds }, {"nativeGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V", - (void*) PaintGlue::getCharArrayBounds }, - {"native_hasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph }, + (void*) PaintGlue::getCharArrayBounds }, + {"native_hasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph }, + {"native_getRunAdvance", "(JJ[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F}, + {"native_getOffsetForAdvance", "(JJ[CIIIIZF)I", + (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I}, {"native_setShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer}, {"native_hasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer} diff --git a/graphics/java/android/graphics/Paint.java b/graphics/java/android/graphics/Paint.java index cd5f59d..649d996 100644 --- a/graphics/java/android/graphics/Paint.java +++ b/graphics/java/android/graphics/Paint.java @@ -2269,6 +2269,168 @@ public class Paint { return native_hasGlyph(mNativePaint, mNativeTypeface, mBidiFlags, string); } + /** + * Measure cursor position within a run of text. + * + * <p>The run of text includes the characters from {@code start} to {@code end} in the text. In + * addition, the range {@code contextStart} to {@code contextEnd} is used as context for the + * purpose of complex text shaping, such as Arabic text potentially shaped differently based on + * the text next to it. + * + * All text outside the range {@code contextStart..contextEnd} is ignored. The text between + * {@code start} and {@code end} will be laid out to be measured. + * + * The returned width measurement is the advance from {@code start} to {@code offset}. It is + * generally a positive value, no matter the direction of the run. If {@code offset == end}, + * the return value is simply the width of the whole run from {@code start} to {@code end}. + * + * Ligatures are formed for characters in the range {@code start..end} (but not for + * {@code start..contextStart} or {@code end..contextEnd}). If {@code offset} points to a + * character in the middle of such a formed ligature, but at a grapheme cluster boundary, the + * return value will also reflect an advance in the middle of the ligature. See + * {@link #getOffsetForAdvance} for more discussion of grapheme cluster boundaries. + * + * The direction of the run is explicitly specified by {@code isRtl}. Thus, this method is + * suitable only for runs of a single direction. + * + * <p>All indices are relative to the start of {@code text}. Further, {@code 0 <= contextStart + * <= start <= offset <= end <= contextEnd <= text.length} must hold on entry. + * + * @param text the text to measure. Cannot be null. + * @param start the index of the start of the range to measure + * @param end the index + 1 of the end of the range to measure + * @param contextStart the index of the start of the shaping context + * @param contextEnd the index + 1 of the end of the range to measure + * @param isRtl whether the run is in RTL direction + * @param offset index of caret position + * @return width measurement between start and offset + */ + public float getRunAdvance(char[] text, int start, int end, int contextStart, int contextEnd, + boolean isRtl, int offset) { + if (text == null) { + throw new IllegalArgumentException("text cannot be null"); + } + if ((contextStart | start | offset | end | contextEnd + | start - contextStart | offset - start | end - offset + | contextEnd - end | text.length - contextEnd) < 0) { + throw new IndexOutOfBoundsException(); + } + if (end == start) { + return 0.0f; + } + // TODO: take mCompatScaling into account (or eliminate compat scaling)? + return native_getRunAdvance(mNativePaint, mNativeTypeface, text, start, end, + contextStart, contextEnd, isRtl, offset); + } + + /** + * @see #getRunAdvance(char[], int, int, int, int, boolean, int) + * + * @param text the text to measure. Cannot be null. + * @param start the index of the start of the range to measure + * @param end the index + 1 of the end of the range to measure + * @param contextStart the index of the start of the shaping context + * @param contextEnd the index + 1 of the end of the range to measure + * @param isRtl whether the run is in RTL direction + * @param offset index of caret position + * @return width measurement between start and offset + */ + public float getRunAdvance(CharSequence text, int start, int end, int contextStart, + int contextEnd, boolean isRtl, int offset) { + if (text == null) { + throw new IllegalArgumentException("text cannot be null"); + } + if ((contextStart | start | offset | end | contextEnd + | start - contextStart | offset - start | end - offset + | contextEnd - end | text.length() - contextEnd) < 0) { + throw new IndexOutOfBoundsException(); + } + if (end == start) { + return 0.0f; + } + // TODO performance: specialized alternatives to avoid buffer copy, if win is significant + char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart); + TextUtils.getChars(text, contextStart, contextEnd, buf, 0); + float result = getRunAdvance(buf, start - contextStart, end - contextStart, 0, + contextEnd - contextStart, isRtl, offset - contextStart); + TemporaryBuffer.recycle(buf); + return result; + } + + /** + * Get the character offset within the string whose position is closest to the specified + * horizontal position. + * + * <p>The returned value is generally the value of {@code offset} for which + * {@link #getRunAdvance} yields a result most closely approximating {@code advance}, + * and which is also on a grapheme cluster boundary. As such, it is the preferred method + * for positioning a cursor in response to a touch or pointer event. The grapheme cluster + * boundaries are based on + * <a href="http://unicode.org/reports/tr29/">Unicode Standard Annex #29</a> but with some + * tailoring for better user experience. + * + * <p>Note that {@code advance} is a (generally positive) width measurement relative to the start + * of the run. Thus, for RTL runs it the distance from the point to the right edge. + * + * <p>All indices are relative to the start of {@code text}. Further, {@code 0 <= contextStart + * <= start <= end <= contextEnd <= text.length} must hold on entry, and {@code start <= result + * <= end} will hold on return. + * + * @param text the text to measure. Cannot be null. + * @param start the index of the start of the range to measure + * @param end the index + 1 of the end of the range to measure + * @param contextStart the index of the start of the shaping context + * @param contextEnd the index + 1 of the end of the range to measure + * @param isRtl whether the run is in RTL direction + * @param advance width relative to start of run + * @return index of offset + */ + public int getOffsetForAdvance(char[] text, int start, int end, int contextStart, + int contextEnd, boolean isRtl, float advance) { + if (text == null) { + throw new IllegalArgumentException("text cannot be null"); + } + if ((contextStart | start | end | contextEnd + | start - contextStart | end - start | contextEnd - end + | text.length - contextEnd) < 0) { + throw new IndexOutOfBoundsException(); + } + // TODO: take mCompatScaling into account (or eliminate compat scaling)? + return native_getOffsetForAdvance(mNativePaint, mNativeTypeface, text, start, end, + contextStart, contextEnd, isRtl, advance); + } + + /** + * @see #getOffsetForAdvance(char[], int, int, int, int, boolean, float) + * + * @param text the text to measure. Cannot be null. + * @param start the index of the start of the range to measure + * @param end the index + 1 of the end of the range to measure + * @param contextStart the index of the start of the shaping context + * @param contextEnd the index + 1 of the end of the range to measure + * @param isRtl whether the run is in RTL direction + * @param advance width relative to start of run + * @return index of offset + */ + public int getOffsetForAdvance(CharSequence text, int start, int end, int contextStart, + int contextEnd, boolean isRtl, float advance) { + if (text == null) { + throw new IllegalArgumentException("text cannot be null"); + } + if ((contextStart | start | end | contextEnd + | start - contextStart | end - start | contextEnd - end + | text.length() - contextEnd) < 0) { + throw new IndexOutOfBoundsException(); + } + // TODO performance: specialized alternatives to avoid buffer copy, if win is significant + char[] buf = TemporaryBuffer.obtain(contextEnd - contextStart); + TextUtils.getChars(text, contextStart, contextEnd, buf, 0); + int result = getOffsetForAdvance(buf, start - contextStart, end - contextStart, 0, + contextEnd - contextStart, isRtl, advance) + contextStart; + TemporaryBuffer.recycle(buf); + return result; + } + @Override protected void finalize() throws Throwable { try { @@ -2356,4 +2518,10 @@ public class Paint { private static native void native_setHyphenEdit(long native_object, int hyphen); private static native boolean native_hasGlyph(long native_object, long native_typeface, int bidiFlags, String string); + private static native float native_getRunAdvance(long native_object, long native_typeface, + char[] text, int start, int end, int contextStart, int contextEnd, boolean isRtl, + int offset); + private static native int native_getOffsetForAdvance(long native_object, + long native_typeface, char[] text, int start, int end, int contextStart, int contextEnd, + boolean isRtl, float advance); } |