summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--api/current.txt4
-rw-r--r--api/system-current.txt4
-rw-r--r--core/jni/android/graphics/Paint.cpp76
-rw-r--r--graphics/java/android/graphics/Paint.java168
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);
}