1/* libs/android_runtime/android/graphics/Paint.cpp
2**
3** Copyright 2006, The Android Open Source Project
4**
5** Licensed under the Apache License, Version 2.0 (the "License");
6** you may not use this file except in compliance with the License.
7** You may obtain a copy of the License at
8**
9**     http://www.apache.org/licenses/LICENSE-2.0
10**
11** Unless required by applicable law or agreed to in writing, software
12** distributed under the License is distributed on an "AS IS" BASIS,
13** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14** See the License for the specific language governing permissions and
15** limitations under the License.
16*/
17
18#define LOG_TAG "Paint"
19
20#include <utils/Log.h>
21
22#include "jni.h"
23#include "GraphicsJNI.h"
24#include "core_jni_helpers.h"
25#include <ScopedStringChars.h>
26#include <ScopedUtfChars.h>
27
28#include "SkBlurDrawLooper.h"
29#include "SkColorFilter.h"
30#include "SkMaskFilter.h"
31#include "SkRasterizer.h"
32#include "SkShader.h"
33#include "SkTypeface.h"
34#include "SkXfermode.h"
35#include "unicode/uloc.h"
36#include "unicode/ushape.h"
37#include "utils/Blur.h"
38
39#include <minikin/GraphemeBreak.h>
40#include <minikin/Measurement.h>
41#include "MinikinSkia.h"
42#include "MinikinUtils.h"
43#include "Paint.h"
44#include "TypefaceImpl.h"
45
46#include <vector>
47
48// temporary for debugging
49#include <Caches.h>
50#include <utils/Log.h>
51
52namespace android {
53
54struct JMetricsID {
55    jfieldID    top;
56    jfieldID    ascent;
57    jfieldID    descent;
58    jfieldID    bottom;
59    jfieldID    leading;
60};
61
62static jclass   gFontMetrics_class;
63static JMetricsID gFontMetrics_fieldID;
64
65static jclass   gFontMetricsInt_class;
66static JMetricsID gFontMetricsInt_fieldID;
67
68static jclass   gPaint_class;
69static jfieldID gPaint_nativeInstanceID;
70static jfieldID gPaint_nativeTypefaceID;
71
72static void defaultSettingsForAndroid(Paint* paint) {
73    // GlyphID encoding is required because we are using Harfbuzz shaping
74    paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
75}
76
77struct LocaleCacheEntry {
78    std::string javaLocale;
79    std::string languageTag;
80};
81
82static thread_local LocaleCacheEntry sSingleEntryLocaleCache;
83
84class PaintGlue {
85public:
86    enum MoveOpt {
87        AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
88    };
89
90    static Paint* getNativePaint(JNIEnv* env, jobject paint) {
91        SkASSERT(env);
92        SkASSERT(paint);
93        SkASSERT(env->IsInstanceOf(paint, gPaint_class));
94        jlong paintHandle = env->GetLongField(paint, gPaint_nativeInstanceID);
95        android::Paint* p = reinterpret_cast<android::Paint*>(paintHandle);
96        SkASSERT(p);
97        return p;
98    }
99
100    static TypefaceImpl* getNativeTypeface(JNIEnv* env, jobject paint) {
101        SkASSERT(env);
102        SkASSERT(paint);
103        SkASSERT(env->IsInstanceOf(paint, gPaint_class));
104        jlong typefaceHandle = env->GetLongField(paint, gPaint_nativeTypefaceID);
105        android::TypefaceImpl* p = reinterpret_cast<android::TypefaceImpl*>(typefaceHandle);
106        return p;
107    }
108
109    static void finalizer(JNIEnv* env, jobject clazz, jlong objHandle) {
110        Paint* obj = reinterpret_cast<Paint*>(objHandle);
111        delete obj;
112    }
113
114    static jlong init(JNIEnv* env, jobject clazz) {
115        SK_COMPILE_ASSERT(1 <<  0 == SkPaint::kAntiAlias_Flag,          paint_flags_mismatch);
116        SK_COMPILE_ASSERT(1 <<  2 == SkPaint::kDither_Flag,             paint_flags_mismatch);
117        SK_COMPILE_ASSERT(1 <<  3 == SkPaint::kUnderlineText_Flag,      paint_flags_mismatch);
118        SK_COMPILE_ASSERT(1 <<  4 == SkPaint::kStrikeThruText_Flag,     paint_flags_mismatch);
119        SK_COMPILE_ASSERT(1 <<  5 == SkPaint::kFakeBoldText_Flag,       paint_flags_mismatch);
120        SK_COMPILE_ASSERT(1 <<  6 == SkPaint::kLinearText_Flag,         paint_flags_mismatch);
121        SK_COMPILE_ASSERT(1 <<  7 == SkPaint::kSubpixelText_Flag,       paint_flags_mismatch);
122        SK_COMPILE_ASSERT(1 <<  8 == SkPaint::kDevKernText_Flag,        paint_flags_mismatch);
123        SK_COMPILE_ASSERT(1 << 10 == SkPaint::kEmbeddedBitmapText_Flag, paint_flags_mismatch);
124
125        Paint* obj = new Paint();
126        defaultSettingsForAndroid(obj);
127        return reinterpret_cast<jlong>(obj);
128    }
129
130    static jlong initWithPaint(JNIEnv* env, jobject clazz, jlong paintHandle) {
131        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
132        Paint* obj = new Paint(*paint);
133        return reinterpret_cast<jlong>(obj);
134    }
135
136    static void reset(JNIEnv* env, jobject clazz, jlong objHandle) {
137        Paint* obj = reinterpret_cast<Paint*>(objHandle);
138        obj->reset();
139        defaultSettingsForAndroid(obj);
140    }
141
142    static void assign(JNIEnv* env, jobject clazz, jlong dstPaintHandle, jlong srcPaintHandle) {
143        Paint* dst = reinterpret_cast<Paint*>(dstPaintHandle);
144        const Paint* src = reinterpret_cast<Paint*>(srcPaintHandle);
145        *dst = *src;
146    }
147
148    // Equivalent to the Java Paint's FILTER_BITMAP_FLAG.
149    static const uint32_t sFilterBitmapFlag = 0x02;
150
151    static jint getFlags(JNIEnv* env, jobject paint) {
152        NPE_CHECK_RETURN_ZERO(env, paint);
153        Paint* nativePaint = getNativePaint(env, paint);
154        uint32_t result = nativePaint->getFlags();
155        result &= ~sFilterBitmapFlag; // Filtering no longer stored in this bit. Mask away.
156        if (nativePaint->getFilterQuality() != kNone_SkFilterQuality) {
157            result |= sFilterBitmapFlag;
158        }
159        return static_cast<jint>(result);
160    }
161
162    static void setFlags(JNIEnv* env, jobject paint, jint flags) {
163        NPE_CHECK_RETURN_VOID(env, paint);
164        Paint* nativePaint = getNativePaint(env, paint);
165        // Instead of modifying 0x02, change the filter level.
166        nativePaint->setFilterQuality(flags & sFilterBitmapFlag
167                ? kLow_SkFilterQuality
168                : kNone_SkFilterQuality);
169        // Don't pass through filter flag, which is no longer stored in paint's flags.
170        flags &= ~sFilterBitmapFlag;
171        // Use the existing value for 0x02.
172        const uint32_t existing0x02Flag = nativePaint->getFlags() & sFilterBitmapFlag;
173        flags |= existing0x02Flag;
174        nativePaint->setFlags(flags);
175    }
176
177    static jint getHinting(JNIEnv* env, jobject paint) {
178        NPE_CHECK_RETURN_ZERO(env, paint);
179        return getNativePaint(env, paint)->getHinting()
180                == Paint::kNo_Hinting ? 0 : 1;
181    }
182
183    static void setHinting(JNIEnv* env, jobject paint, jint mode) {
184        NPE_CHECK_RETURN_VOID(env, paint);
185        getNativePaint(env, paint)->setHinting(
186                mode == 0 ? Paint::kNo_Hinting : Paint::kNormal_Hinting);
187    }
188
189    static void setAntiAlias(JNIEnv* env, jobject paint, jboolean aa) {
190        NPE_CHECK_RETURN_VOID(env, paint);
191        getNativePaint(env, paint)->setAntiAlias(aa);
192    }
193
194    static void setLinearText(JNIEnv* env, jobject paint, jboolean linearText) {
195        NPE_CHECK_RETURN_VOID(env, paint);
196        getNativePaint(env, paint)->setLinearText(linearText);
197    }
198
199    static void setSubpixelText(JNIEnv* env, jobject paint, jboolean subpixelText) {
200        NPE_CHECK_RETURN_VOID(env, paint);
201        getNativePaint(env, paint)->setSubpixelText(subpixelText);
202    }
203
204    static void setUnderlineText(JNIEnv* env, jobject paint, jboolean underlineText) {
205        NPE_CHECK_RETURN_VOID(env, paint);
206        getNativePaint(env, paint)->setUnderlineText(underlineText);
207    }
208
209    static void setStrikeThruText(JNIEnv* env, jobject paint, jboolean strikeThruText) {
210        NPE_CHECK_RETURN_VOID(env, paint);
211        getNativePaint(env, paint)->setStrikeThruText(strikeThruText);
212    }
213
214    static void setFakeBoldText(JNIEnv* env, jobject paint, jboolean fakeBoldText) {
215        NPE_CHECK_RETURN_VOID(env, paint);
216        getNativePaint(env, paint)->setFakeBoldText(fakeBoldText);
217    }
218
219    static void setFilterBitmap(JNIEnv* env, jobject paint, jboolean filterBitmap) {
220        NPE_CHECK_RETURN_VOID(env, paint);
221        getNativePaint(env, paint)->setFilterQuality(
222                filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
223    }
224
225    static void setDither(JNIEnv* env, jobject paint, jboolean dither) {
226        NPE_CHECK_RETURN_VOID(env, paint);
227        getNativePaint(env, paint)->setDither(dither);
228    }
229
230    static jint getStyle(JNIEnv* env, jobject clazz,jlong objHandle) {
231        Paint* obj = reinterpret_cast<Paint*>(objHandle);
232        return static_cast<jint>(obj->getStyle());
233    }
234
235    static void setStyle(JNIEnv* env, jobject clazz, jlong objHandle, jint styleHandle) {
236        Paint* obj = reinterpret_cast<Paint*>(objHandle);
237        Paint::Style style = static_cast<Paint::Style>(styleHandle);
238        obj->setStyle(style);
239    }
240
241    static jint getColor(JNIEnv* env, jobject paint) {
242        NPE_CHECK_RETURN_ZERO(env, paint);
243        int color;
244        color = getNativePaint(env, paint)->getColor();
245        return static_cast<jint>(color);
246    }
247
248    static jint getAlpha(JNIEnv* env, jobject paint) {
249        NPE_CHECK_RETURN_ZERO(env, paint);
250        int alpha;
251        alpha = getNativePaint(env, paint)->getAlpha();
252        return static_cast<jint>(alpha);
253    }
254
255    static void setColor(JNIEnv* env, jobject paint, jint color) {
256        NPE_CHECK_RETURN_VOID(env, paint);
257        getNativePaint(env, paint)->setColor(color);
258    }
259
260    static void setAlpha(JNIEnv* env, jobject paint, jint a) {
261        NPE_CHECK_RETURN_VOID(env, paint);
262        getNativePaint(env, paint)->setAlpha(a);
263    }
264
265    static jfloat getStrokeWidth(JNIEnv* env, jobject paint) {
266        NPE_CHECK_RETURN_ZERO(env, paint);
267        return SkScalarToFloat(getNativePaint(env, paint)->getStrokeWidth());
268    }
269
270    static void setStrokeWidth(JNIEnv* env, jobject paint, jfloat width) {
271        NPE_CHECK_RETURN_VOID(env, paint);
272        getNativePaint(env, paint)->setStrokeWidth(width);
273    }
274
275    static jfloat getStrokeMiter(JNIEnv* env, jobject paint) {
276        NPE_CHECK_RETURN_ZERO(env, paint);
277        return SkScalarToFloat(getNativePaint(env, paint)->getStrokeMiter());
278    }
279
280    static void setStrokeMiter(JNIEnv* env, jobject paint, jfloat miter) {
281        NPE_CHECK_RETURN_VOID(env, paint);
282        getNativePaint(env, paint)->setStrokeMiter(miter);
283    }
284
285    static jint getStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle) {
286        Paint* obj = reinterpret_cast<Paint*>(objHandle);
287        return static_cast<jint>(obj->getStrokeCap());
288    }
289
290    static void setStrokeCap(JNIEnv* env, jobject clazz, jlong objHandle, jint capHandle) {
291        Paint* obj = reinterpret_cast<Paint*>(objHandle);
292        Paint::Cap cap = static_cast<Paint::Cap>(capHandle);
293        obj->setStrokeCap(cap);
294    }
295
296    static jint getStrokeJoin(JNIEnv* env, jobject clazz, jlong objHandle) {
297        Paint* obj = reinterpret_cast<Paint*>(objHandle);
298        return static_cast<jint>(obj->getStrokeJoin());
299    }
300
301    static void setStrokeJoin(JNIEnv* env, jobject clazz, jlong objHandle, jint joinHandle) {
302        Paint* obj = reinterpret_cast<Paint*>(objHandle);
303        Paint::Join join = (Paint::Join) joinHandle;
304        obj->setStrokeJoin(join);
305    }
306
307    static jboolean getFillPath(JNIEnv* env, jobject clazz, jlong objHandle, jlong srcHandle, jlong dstHandle) {
308        Paint* obj = reinterpret_cast<Paint*>(objHandle);
309        SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
310        SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
311        return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
312    }
313
314    static jlong setShader(JNIEnv* env, jobject clazz, jlong objHandle, jlong shaderHandle) {
315        Paint* obj = reinterpret_cast<Paint*>(objHandle);
316        SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
317        return reinterpret_cast<jlong>(obj->setShader(shader));
318    }
319
320    static jlong setColorFilter(JNIEnv* env, jobject clazz, jlong objHandle, jlong filterHandle) {
321        Paint* obj = reinterpret_cast<Paint *>(objHandle);
322        SkColorFilter* filter  = reinterpret_cast<SkColorFilter *>(filterHandle);
323        return reinterpret_cast<jlong>(obj->setColorFilter(filter));
324    }
325
326    static jlong setXfermode(JNIEnv* env, jobject clazz, jlong objHandle, jlong xfermodeHandle) {
327        Paint* obj = reinterpret_cast<Paint*>(objHandle);
328        SkXfermode* xfermode = reinterpret_cast<SkXfermode*>(xfermodeHandle);
329        return reinterpret_cast<jlong>(obj->setXfermode(xfermode));
330    }
331
332    static jlong setPathEffect(JNIEnv* env, jobject clazz, jlong objHandle, jlong effectHandle) {
333        Paint* obj = reinterpret_cast<Paint*>(objHandle);
334        SkPathEffect* effect  = reinterpret_cast<SkPathEffect*>(effectHandle);
335        return reinterpret_cast<jlong>(obj->setPathEffect(effect));
336    }
337
338    static jlong setMaskFilter(JNIEnv* env, jobject clazz, jlong objHandle, jlong maskfilterHandle) {
339        Paint* obj = reinterpret_cast<Paint*>(objHandle);
340        SkMaskFilter* maskfilter  = reinterpret_cast<SkMaskFilter*>(maskfilterHandle);
341        return reinterpret_cast<jlong>(obj->setMaskFilter(maskfilter));
342    }
343
344    static jlong setTypeface(JNIEnv* env, jobject clazz, jlong objHandle, jlong typefaceHandle) {
345        // TODO: in Paint refactoring, set typeface on android Paint, not Paint
346        return NULL;
347    }
348
349    static jlong setRasterizer(JNIEnv* env, jobject clazz, jlong objHandle, jlong rasterizerHandle) {
350        Paint* obj = reinterpret_cast<Paint*>(objHandle);
351        SkAutoTUnref<SkRasterizer> rasterizer(GraphicsJNI::refNativeRasterizer(rasterizerHandle));
352        return reinterpret_cast<jlong>(obj->setRasterizer(rasterizer));
353    }
354
355    static jint getTextAlign(JNIEnv* env, jobject clazz, jlong objHandle) {
356        Paint* obj = reinterpret_cast<Paint*>(objHandle);
357        return static_cast<jint>(obj->getTextAlign());
358    }
359
360    static void setTextAlign(JNIEnv* env, jobject clazz, jlong objHandle, jint alignHandle) {
361        Paint* obj = reinterpret_cast<Paint*>(objHandle);
362        Paint::Align align = static_cast<Paint::Align>(alignHandle);
363        obj->setTextAlign(align);
364    }
365
366    // generate bcp47 identifier for the supplied locale
367    static void toLanguageTag(char* output, size_t outSize,
368            const char* locale) {
369        if (output == NULL || outSize <= 0) {
370            return;
371        }
372        if (locale == NULL) {
373            output[0] = '\0';
374            return;
375        }
376        char canonicalChars[ULOC_FULLNAME_CAPACITY];
377        UErrorCode uErr = U_ZERO_ERROR;
378        uloc_canonicalize(locale, canonicalChars, ULOC_FULLNAME_CAPACITY,
379                &uErr);
380        if (U_SUCCESS(uErr)) {
381            char likelyChars[ULOC_FULLNAME_CAPACITY];
382            uErr = U_ZERO_ERROR;
383            uloc_addLikelySubtags(canonicalChars, likelyChars,
384                    ULOC_FULLNAME_CAPACITY, &uErr);
385            if (U_SUCCESS(uErr)) {
386                uErr = U_ZERO_ERROR;
387                uloc_toLanguageTag(likelyChars, output, outSize, FALSE, &uErr);
388                if (U_SUCCESS(uErr)) {
389                    return;
390                } else {
391                    ALOGD("uloc_toLanguageTag(\"%s\") failed: %s", likelyChars,
392                            u_errorName(uErr));
393                }
394            } else {
395                ALOGD("uloc_addLikelySubtags(\"%s\") failed: %s",
396                        canonicalChars, u_errorName(uErr));
397            }
398        } else {
399            ALOGD("uloc_canonicalize(\"%s\") failed: %s", locale,
400                    u_errorName(uErr));
401        }
402        // unable to build a proper language identifier
403        output[0] = '\0';
404    }
405
406    static void setTextLocale(JNIEnv* env, jobject clazz, jlong objHandle, jstring locale) {
407        Paint* obj = reinterpret_cast<Paint*>(objHandle);
408        ScopedUtfChars localeChars(env, locale);
409        if (sSingleEntryLocaleCache.javaLocale != localeChars.c_str()) {
410            sSingleEntryLocaleCache.javaLocale = localeChars.c_str();
411            char langTag[ULOC_FULLNAME_CAPACITY];
412            toLanguageTag(langTag, ULOC_FULLNAME_CAPACITY, localeChars.c_str());
413            sSingleEntryLocaleCache.languageTag = langTag;
414        }
415
416        obj->setTextLocale(sSingleEntryLocaleCache.languageTag);
417    }
418
419    static jboolean isElegantTextHeight(JNIEnv* env, jobject paint) {
420        NPE_CHECK_RETURN_ZERO(env, paint);
421        Paint* obj = getNativePaint(env, paint);
422        return obj->getFontVariant() == VARIANT_ELEGANT;
423    }
424
425    static void setElegantTextHeight(JNIEnv* env, jobject paint, jboolean aa) {
426        NPE_CHECK_RETURN_VOID(env, paint);
427        Paint* obj = getNativePaint(env, paint);
428        obj->setFontVariant(aa ? VARIANT_ELEGANT : VARIANT_DEFAULT);
429    }
430
431    static jfloat getTextSize(JNIEnv* env, jobject paint) {
432        NPE_CHECK_RETURN_ZERO(env, paint);
433        return SkScalarToFloat(getNativePaint(env, paint)->getTextSize());
434    }
435
436    static void setTextSize(JNIEnv* env, jobject paint, jfloat textSize) {
437        NPE_CHECK_RETURN_VOID(env, paint);
438        getNativePaint(env, paint)->setTextSize(textSize);
439    }
440
441    static jfloat getTextScaleX(JNIEnv* env, jobject paint) {
442        NPE_CHECK_RETURN_ZERO(env, paint);
443        return SkScalarToFloat(getNativePaint(env, paint)->getTextScaleX());
444    }
445
446    static void setTextScaleX(JNIEnv* env, jobject paint, jfloat scaleX) {
447        NPE_CHECK_RETURN_VOID(env, paint);
448        getNativePaint(env, paint)->setTextScaleX(scaleX);
449    }
450
451    static jfloat getTextSkewX(JNIEnv* env, jobject paint) {
452        NPE_CHECK_RETURN_ZERO(env, paint);
453        return SkScalarToFloat(getNativePaint(env, paint)->getTextSkewX());
454    }
455
456    static void setTextSkewX(JNIEnv* env, jobject paint, jfloat skewX) {
457        NPE_CHECK_RETURN_VOID(env, paint);
458        getNativePaint(env, paint)->setTextSkewX(skewX);
459    }
460
461    static jfloat getLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle) {
462        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
463        return paint->getLetterSpacing();
464    }
465
466    static void setLetterSpacing(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat letterSpacing) {
467        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
468        paint->setLetterSpacing(letterSpacing);
469    }
470
471    static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
472        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
473        if (!settings) {
474            paint->setFontFeatureSettings(std::string());
475        } else {
476            ScopedUtfChars settingsChars(env, settings);
477            paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
478        }
479    }
480
481    static jint getHyphenEdit(JNIEnv* env, jobject clazz, jlong paintHandle, jint hyphen) {
482        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
483        return paint->getHyphenEdit();
484    }
485
486    static void setHyphenEdit(JNIEnv* env, jobject clazz, jlong paintHandle, jint hyphen) {
487        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
488        paint->setHyphenEdit((uint32_t)hyphen);
489    }
490
491    static SkScalar getMetricsInternal(JNIEnv* env, jobject jpaint, Paint::FontMetrics *metrics) {
492        const int kElegantTop = 2500;
493        const int kElegantBottom = -1000;
494        const int kElegantAscent = 1900;
495        const int kElegantDescent = -500;
496        const int kElegantLeading = 0;
497        Paint* paint = getNativePaint(env, jpaint);
498        TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
499        typeface = TypefaceImpl_resolveDefault(typeface);
500        FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
501        float saveSkewX = paint->getTextSkewX();
502        bool savefakeBold = paint->isFakeBoldText();
503        MinikinFontSkia::populateSkPaint(paint, baseFont.font, baseFont.fakery);
504        SkScalar spacing = paint->getFontMetrics(metrics);
505        // The populateSkPaint call may have changed fake bold / text skew
506        // because we want to measure with those effects applied, so now
507        // restore the original settings.
508        paint->setTextSkewX(saveSkewX);
509        paint->setFakeBoldText(savefakeBold);
510        if (paint->getFontVariant() == VARIANT_ELEGANT) {
511            SkScalar size = paint->getTextSize();
512            metrics->fTop = -size * kElegantTop / 2048;
513            metrics->fBottom = -size * kElegantBottom / 2048;
514            metrics->fAscent = -size * kElegantAscent / 2048;
515            metrics->fDescent = -size * kElegantDescent / 2048;
516            metrics->fLeading = size * kElegantLeading / 2048;
517            spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading;
518        }
519        return spacing;
520    }
521
522    static jfloat ascent(JNIEnv* env, jobject paint) {
523        NPE_CHECK_RETURN_ZERO(env, paint);
524        Paint::FontMetrics metrics;
525        getMetricsInternal(env, paint, &metrics);
526        return SkScalarToFloat(metrics.fAscent);
527    }
528
529    static jfloat descent(JNIEnv* env, jobject paint) {
530        NPE_CHECK_RETURN_ZERO(env, paint);
531        Paint::FontMetrics metrics;
532        getMetricsInternal(env, paint, &metrics);
533        return SkScalarToFloat(metrics.fDescent);
534    }
535
536    static jfloat getFontMetrics(JNIEnv* env, jobject paint, jobject metricsObj) {
537        NPE_CHECK_RETURN_ZERO(env, paint);
538        Paint::FontMetrics metrics;
539        SkScalar spacing = getMetricsInternal(env, paint, &metrics);
540
541        if (metricsObj) {
542            SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
543            env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
544            env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
545            env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
546            env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
547            env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
548        }
549        return SkScalarToFloat(spacing);
550    }
551
552    static jint getFontMetricsInt(JNIEnv* env, jobject paint, jobject metricsObj) {
553        NPE_CHECK_RETURN_ZERO(env, paint);
554        Paint::FontMetrics metrics;
555
556        getMetricsInternal(env, paint, &metrics);
557        int ascent = SkScalarRoundToInt(metrics.fAscent);
558        int descent = SkScalarRoundToInt(metrics.fDescent);
559        int leading = SkScalarRoundToInt(metrics.fLeading);
560
561        if (metricsObj) {
562            SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
563            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
564            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
565            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
566            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
567            env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
568        }
569        return descent - ascent + leading;
570    }
571
572    static jfloat measureText_CIII(JNIEnv* env, jobject jpaint, jcharArray text, jint index, jint count,
573            jint bidiFlags) {
574        NPE_CHECK_RETURN_ZERO(env, jpaint);
575        NPE_CHECK_RETURN_ZERO(env, text);
576
577        size_t textLength = env->GetArrayLength(text);
578        if ((index | count) < 0 || (size_t)(index + count) > textLength) {
579            doThrowAIOOBE(env);
580            return 0;
581        }
582        if (count == 0) {
583            return 0;
584        }
585
586        Paint* paint = getNativePaint(env, jpaint);
587        const jchar* textArray = env->GetCharArrayElements(text, NULL);
588        jfloat result = 0;
589
590        Layout layout;
591        TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
592        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray + index, 0, count,
593                count);
594        result = layout.getAdvance();
595        env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
596        return result;
597    }
598
599    static jfloat measureText_StringIII(JNIEnv* env, jobject jpaint, jstring text, jint start, jint end,
600            jint bidiFlags) {
601        NPE_CHECK_RETURN_ZERO(env, jpaint);
602        NPE_CHECK_RETURN_ZERO(env, text);
603
604        size_t textLength = env->GetStringLength(text);
605        int count = end - start;
606        if ((start | count) < 0 || (size_t)end > textLength) {
607            doThrowAIOOBE(env);
608            return 0;
609        }
610        if (count == 0) {
611            return 0;
612        }
613
614        const jchar* textArray = env->GetStringChars(text, NULL);
615        Paint* paint = getNativePaint(env, jpaint);
616        jfloat width = 0;
617
618        Layout layout;
619        TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
620        // Only the substring is used for measurement, so no additional context is passed in. This
621        // behavior is consistent between char[] and String specializations.
622        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray + start, 0, count, count);
623        width = layout.getAdvance();
624
625        env->ReleaseStringChars(text, textArray);
626        return width;
627    }
628
629    static jfloat measureText_StringI(JNIEnv* env, jobject jpaint, jstring text, jint bidiFlags) {
630        NPE_CHECK_RETURN_ZERO(env, jpaint);
631        NPE_CHECK_RETURN_ZERO(env, text);
632
633        size_t textLength = env->GetStringLength(text);
634        if (textLength == 0) {
635            return 0;
636        }
637
638        const jchar* textArray = env->GetStringChars(text, NULL);
639        Paint* paint = getNativePaint(env, jpaint);
640        jfloat width = 0;
641
642        Layout layout;
643        TypefaceImpl* typeface = getNativeTypeface(env, jpaint);
644        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, textArray, 0, textLength, textLength);
645        width = layout.getAdvance();
646
647        env->ReleaseStringChars(text, textArray);
648        return width;
649    }
650
651    static int dotextwidths(JNIEnv* env, Paint* paint, TypefaceImpl* typeface, const jchar text[], int count,
652            jfloatArray widths, jint bidiFlags) {
653        NPE_CHECK_RETURN_ZERO(env, paint);
654        NPE_CHECK_RETURN_ZERO(env, text);
655
656        if (count < 0 || !widths) {
657            doThrowAIOOBE(env);
658            return 0;
659        }
660        if (count == 0) {
661            return 0;
662        }
663        size_t widthsLength = env->GetArrayLength(widths);
664        if ((size_t)count > widthsLength) {
665            doThrowAIOOBE(env);
666            return 0;
667        }
668
669        AutoJavaFloatArray autoWidths(env, widths, count);
670        jfloat* widthsArray = autoWidths.ptr();
671
672        Layout layout;
673        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, 0, count, count);
674        layout.getAdvances(widthsArray);
675
676        return count;
677    }
678
679    static jint getTextWidths___CIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray text,
680            jint index, jint count, jint bidiFlags, jfloatArray widths) {
681        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
682        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
683        const jchar* textArray = env->GetCharArrayElements(text, NULL);
684        count = dotextwidths(env, paint, typeface, textArray + index, count, widths, bidiFlags);
685        env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
686                                      JNI_ABORT);
687        return count;
688    }
689
690    static jint getTextWidths__StringIII_F(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring text,
691            jint start, jint end, jint bidiFlags, jfloatArray widths) {
692        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
693        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
694        const jchar* textArray = env->GetStringChars(text, NULL);
695        int count = dotextwidths(env, paint, typeface, textArray + start, end - start, widths, bidiFlags);
696        env->ReleaseStringChars(text, textArray);
697        return count;
698    }
699
700    static jfloat doTextRunAdvances(JNIEnv *env, Paint *paint, TypefaceImpl* typeface, const jchar *text,
701                                    jint start, jint count, jint contextCount, jboolean isRtl,
702                                    jfloatArray advances, jint advancesIndex) {
703        NPE_CHECK_RETURN_ZERO(env, paint);
704        NPE_CHECK_RETURN_ZERO(env, text);
705
706        if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) {
707            doThrowAIOOBE(env);
708            return 0;
709        }
710        if (count == 0) {
711            return 0;
712        }
713        if (advances) {
714            size_t advancesLength = env->GetArrayLength(advances);
715            if ((size_t)count > advancesLength) {
716                doThrowAIOOBE(env);
717                return 0;
718            }
719        }
720        jfloat* advancesArray = new jfloat[count];
721        jfloat totalAdvance = 0;
722
723        int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
724
725        Layout layout;
726        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, start, count, contextCount);
727        layout.getAdvances(advancesArray);
728        totalAdvance = layout.getAdvance();
729
730        if (advances != NULL) {
731            env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray);
732        }
733        delete [] advancesArray;
734        return totalAdvance;
735    }
736
737    static jfloat getTextRunAdvances___CIIIIZ_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
738            jlong typefaceHandle,
739            jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
740            jboolean isRtl, jfloatArray advances, jint advancesIndex) {
741        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
742        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
743        jchar* textArray = env->GetCharArrayElements(text, NULL);
744        jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextIndex,
745                index - contextIndex, count, contextCount, isRtl, advances, advancesIndex);
746        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
747        return result;
748    }
749
750    static jfloat getTextRunAdvances__StringIIIIZ_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
751            jlong typefaceHandle,
752            jstring text, jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl,
753            jfloatArray advances, jint advancesIndex) {
754        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
755        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
756        const jchar* textArray = env->GetStringChars(text, NULL);
757        jfloat result = doTextRunAdvances(env, paint, typeface, textArray + contextStart,
758                start - contextStart, end - start, contextEnd - contextStart, isRtl,
759                advances, advancesIndex);
760        env->ReleaseStringChars(text, textArray);
761        return result;
762    }
763
764    static jint doTextRunCursor(JNIEnv *env, Paint* paint, const jchar *text, jint start,
765            jint count, jint flags, jint offset, jint opt) {
766        GraphemeBreak::MoveOpt moveOpt = GraphemeBreak::MoveOpt(opt);
767        size_t result = GraphemeBreak::getTextRunCursor(text, start, count, offset, moveOpt);
768        return static_cast<jint>(result);
769    }
770
771    static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
772            jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) {
773        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
774        jchar* textArray = env->GetCharArrayElements(text, NULL);
775        jint result = doTextRunCursor(env, paint, textArray, contextStart, contextCount, dir,
776                offset, cursorOpt);
777        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
778        return result;
779    }
780
781    static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle, jstring text,
782            jint contextStart, jint contextEnd, jint dir, jint offset, jint cursorOpt) {
783        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
784        const jchar* textArray = env->GetStringChars(text, NULL);
785        jint result = doTextRunCursor(env, paint, textArray, contextStart,
786                contextEnd - contextStart, dir, offset, cursorOpt);
787        env->ReleaseStringChars(text, textArray);
788        return result;
789    }
790
791    class GetTextFunctor {
792    public:
793        GetTextFunctor(const Layout& layout, SkPath* path, jfloat x, jfloat y, Paint* paint,
794                    uint16_t* glyphs, SkPoint* pos)
795                : layout(layout), path(path), x(x), y(y), paint(paint), glyphs(glyphs), pos(pos) {
796        }
797
798        void operator()(size_t start, size_t end) {
799            for (size_t i = start; i < end; i++) {
800                glyphs[i] = layout.getGlyphId(i);
801                pos[i].fX = x + layout.getX(i);
802                pos[i].fY = y + layout.getY(i);
803            }
804            if (start == 0) {
805                paint->getPosTextPath(glyphs + start, (end - start) << 1, pos + start, path);
806            } else {
807                paint->getPosTextPath(glyphs + start, (end - start) << 1, pos + start, &tmpPath);
808                path->addPath(tmpPath);
809            }
810        }
811    private:
812        const Layout& layout;
813        SkPath* path;
814        jfloat x;
815        jfloat y;
816        Paint* paint;
817        uint16_t* glyphs;
818        SkPoint* pos;
819        SkPath tmpPath;
820    };
821
822    static void getTextPath(JNIEnv* env, Paint* paint, TypefaceImpl* typeface, const jchar* text,
823            jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) {
824        Layout layout;
825        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, text, 0, count, count);
826        size_t nGlyphs = layout.nGlyphs();
827        uint16_t* glyphs = new uint16_t[nGlyphs];
828        SkPoint* pos = new SkPoint[nGlyphs];
829
830        x += MinikinUtils::xOffsetForTextAlign(paint, layout);
831        Paint::Align align = paint->getTextAlign();
832        paint->setTextAlign(Paint::kLeft_Align);
833        paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
834        GetTextFunctor f(layout, path, x, y, paint, glyphs, pos);
835        MinikinUtils::forFontRun(layout, paint, f);
836        paint->setTextAlign(align);
837        delete[] glyphs;
838        delete[] pos;
839    }
840
841    static void getTextPath___C(JNIEnv* env, jobject clazz, jlong paintHandle,
842            jlong typefaceHandle, jint bidiFlags,
843            jcharArray text, jint index, jint count, jfloat x, jfloat y, jlong pathHandle) {
844        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
845        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
846        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
847        const jchar* textArray = env->GetCharArrayElements(text, NULL);
848        getTextPath(env, paint, typeface, textArray + index, count, bidiFlags, x, y, path);
849        env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
850    }
851
852    static void getTextPath__String(JNIEnv* env, jobject clazz, jlong paintHandle,
853            jlong typefaceHandle, jint bidiFlags,
854            jstring text, jint start, jint end, jfloat x, jfloat y, jlong pathHandle) {
855        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
856        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
857        SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
858        const jchar* textArray = env->GetStringChars(text, NULL);
859        getTextPath(env, paint, typeface, textArray + start, end - start, bidiFlags, x, y, path);
860        env->ReleaseStringChars(text, textArray);
861    }
862
863    static void setShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle, jfloat radius,
864                               jfloat dx, jfloat dy, jint color) {
865        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
866        if (radius <= 0) {
867            paint->setLooper(NULL);
868        }
869        else {
870            SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
871            paint->setLooper(SkBlurDrawLooper::Create((SkColor)color, sigma, dx, dy))->unref();
872        }
873    }
874
875    static jboolean hasShadowLayer(JNIEnv* env, jobject clazz, jlong paintHandle) {
876        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
877        return paint->getLooper() && paint->getLooper()->asABlurShadow(NULL);
878    }
879
880    static int breakText(JNIEnv* env, const Paint& paint, TypefaceImpl* typeface, const jchar text[],
881                         int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
882                         const bool forwardScan) {
883        size_t measuredCount = 0;
884        float measured = 0;
885
886        Layout layout;
887        MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, 0, count, count);
888        float* advances = new float[count];
889        layout.getAdvances(advances);
890
891        for (int i = 0; i < count; i++) {
892            // traverse in the given direction
893            int index = forwardScan ? i : (count - i - 1);
894            float width = advances[index];
895            if (measured + width > maxWidth) {
896                break;
897            }
898            // properly handle clusters when scanning backwards
899            if (forwardScan || width != 0.0f) {
900                measuredCount = i + 1;
901            }
902            measured += width;
903        }
904        delete[] advances;
905
906        if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
907            AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
908            jfloat* array = autoMeasured.ptr();
909            array[0] = measured;
910        }
911        return measuredCount;
912    }
913
914    static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jcharArray jtext,
915            jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
916        NPE_CHECK_RETURN_ZERO(env, jtext);
917
918        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
919        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
920
921        bool forwardTextDirection;
922        if (count < 0) {
923            forwardTextDirection = false;
924            count = -count;
925        }
926        else {
927            forwardTextDirection = true;
928        }
929
930        if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
931            doThrowAIOOBE(env);
932            return 0;
933        }
934
935        const jchar* text = env->GetCharArrayElements(jtext, NULL);
936        count = breakText(env, *paint, typeface, text + index, count, maxWidth,
937                          bidiFlags, jmeasuredWidth, forwardTextDirection);
938        env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
939                                      JNI_ABORT);
940        return count;
941    }
942
943    static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jlong typefaceHandle, jstring jtext,
944                jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
945        NPE_CHECK_RETURN_ZERO(env, jtext);
946
947        Paint* paint = reinterpret_cast<Paint*>(paintHandle);
948        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
949
950        int count = env->GetStringLength(jtext);
951        const jchar* text = env->GetStringChars(jtext, NULL);
952        count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, forwards);
953        env->ReleaseStringChars(jtext, text);
954        return count;
955    }
956
957    static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
958            const Paint& paint, TypefaceImpl* typeface, jint bidiFlags) {
959        SkRect  r;
960        SkIRect ir;
961
962        Layout layout;
963        MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, 0, count, count);
964        MinikinRect rect;
965        layout.getBounds(&rect);
966        r.fLeft = rect.mLeft;
967        r.fTop = rect.mTop;
968        r.fRight = rect.mRight;
969        r.fBottom = rect.mBottom;
970        r.roundOut(&ir);
971        GraphicsJNI::irect_to_jrect(ir, env, bounds);
972    }
973
974    static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
975                                jstring text, jint start, jint end, jint bidiFlags, jobject bounds) {
976        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);;
977        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
978        const jchar* textArray = env->GetStringChars(text, NULL);
979        doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
980        env->ReleaseStringChars(text, textArray);
981    }
982
983    static void getCharArrayBounds(JNIEnv* env, jobject, jlong paintHandle, jlong typefaceHandle,
984                        jcharArray text, jint index, jint count, jint bidiFlags, jobject bounds) {
985        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
986        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
987        const jchar* textArray = env->GetCharArrayElements(text, NULL);
988        doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags);
989        env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
990                                      JNI_ABORT);
991    }
992
993    static jboolean layoutContainsNotdef(const Layout& layout) {
994        for (size_t i = 0; i < layout.nGlyphs(); i++) {
995            if (layout.getGlyphId(i) == 0) {
996                return true;
997            }
998        }
999        return false;
1000    }
1001
1002    static jboolean hasGlyphVariation(const Paint* paint, TypefaceImpl* typeface, jint bidiFlags,
1003            const jchar* chars, size_t size) {
1004        // TODO: query font for whether character has variation selector; requires a corresponding
1005        // function in Minikin.
1006        return false;
1007    }
1008
1009    static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jlong typefaceHandle,
1010            jint bidiFlags, jstring string) {
1011        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
1012        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
1013        ScopedStringChars str(env, string);
1014
1015        /* start by rejecting variation selectors (not supported yet) */
1016        size_t nChars = 0;
1017        for (size_t i = 0; i < str.size(); i++) {
1018            jchar c = str[i];
1019            if (0xDC00 <= c && c <= 0xDFFF) {
1020                // invalid UTF-16, unpaired trailing surrogate
1021                return false;
1022            } else if (0xD800 <= c && c <= 0xDBFF) {
1023                if (i + 1 == str.size()) {
1024                    // invalid UTF-16, unpaired leading surrogate at end of string
1025                    return false;
1026                }
1027                i++;
1028                jchar c2 = str[i];
1029                if (!(0xDC00 <= c2 && c2 <= 0xDFFF)) {
1030                    // invalid UTF-16, unpaired leading surrogate
1031                    return false;
1032                }
1033                // UTF-16 encoding of range U+E0100..U+E01EF is DB40 DD00 .. DB40 DDEF
1034                if (c == 0xDB40 && 0xDD00 <= c2 && c2 <= 0xDDEF) {
1035                    return hasGlyphVariation(paint, typeface, bidiFlags, str.get(), str.size());
1036                }
1037            } else if (0xFE00 <= c && c <= 0xFE0F) {
1038                return hasGlyphVariation(paint, typeface, bidiFlags, str.get(), str.size());
1039            }
1040            nChars++;
1041        }
1042        Layout layout;
1043        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, str.get(), 0, str.size(),
1044                str.size());
1045        size_t nGlyphs = layout.nGlyphs();
1046        if (nGlyphs != 1 && nChars > 1) {
1047            // multiple-character input, and was not a ligature
1048            // TODO: handle ZWJ/ZWNJ characters specially so we can detect certain ligatures
1049            // in joining scripts, such as Arabic and Mongolian.
1050            return false;
1051        }
1052        return nGlyphs > 0 && !layoutContainsNotdef(layout);
1053    }
1054
1055    static jfloat doRunAdvance(const Paint* paint, TypefaceImpl* typeface, const jchar buf[],
1056            jint start, jint count, jint bufSize, jboolean isRtl, jint offset) {
1057        Layout layout;
1058        int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
1059        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, buf, start, count, bufSize);
1060        return getRunAdvance(layout, buf, start, count, offset);
1061    }
1062
1063    static jfloat getRunAdvance___CIIIIZI_F(JNIEnv *env, jclass, jlong paintHandle,
1064            jlong typefaceHandle, jcharArray text, jint start, jint end, jint contextStart,
1065            jint contextEnd, jboolean isRtl, jint offset) {
1066        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
1067        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
1068        jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, NULL);
1069        jfloat result = doRunAdvance(paint, typeface, textArray + contextStart,
1070                start - contextStart, end - start, contextEnd - contextStart, isRtl,
1071                offset - contextStart);
1072        env->ReleasePrimitiveArrayCritical(text, textArray, JNI_ABORT);
1073        return result;
1074    }
1075
1076    static jint doOffsetForAdvance(const Paint* paint, TypefaceImpl* typeface, const jchar buf[],
1077            jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) {
1078        Layout layout;
1079        int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
1080        MinikinUtils::doLayout(&layout, paint, bidiFlags, typeface, buf, start, count, bufSize);
1081        return getOffsetForAdvance(layout, buf, start, count, advance);
1082    }
1083    static jint getOffsetForAdvance___CIIIIZF_I(JNIEnv *env, jclass, jlong paintHandle,
1084            jlong typefaceHandle, jcharArray text, jint start, jint end, jint contextStart,
1085            jint contextEnd, jboolean isRtl, jfloat advance) {
1086        const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
1087        TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
1088        jchar* textArray = (jchar*) env->GetPrimitiveArrayCritical(text, NULL);
1089        jint result = doOffsetForAdvance(paint, typeface, textArray + contextStart,
1090                start - contextStart, end - start, contextEnd - contextStart, isRtl, advance);
1091        result += contextStart;
1092        env->ReleasePrimitiveArrayCritical(text, textArray, JNI_ABORT);
1093        return result;
1094    }
1095
1096};
1097
1098static JNINativeMethod methods[] = {
1099    {"finalizer", "(J)V", (void*) PaintGlue::finalizer},
1100    {"native_init","()J", (void*) PaintGlue::init},
1101    {"native_initWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
1102
1103    {"native_reset","!(J)V", (void*) PaintGlue::reset},
1104    {"native_set","!(JJ)V", (void*) PaintGlue::assign},
1105    {"getFlags","!()I", (void*) PaintGlue::getFlags},
1106    {"setFlags","!(I)V", (void*) PaintGlue::setFlags},
1107    {"getHinting","!()I", (void*) PaintGlue::getHinting},
1108    {"setHinting","!(I)V", (void*) PaintGlue::setHinting},
1109    {"setAntiAlias","!(Z)V", (void*) PaintGlue::setAntiAlias},
1110    {"setSubpixelText","!(Z)V", (void*) PaintGlue::setSubpixelText},
1111    {"setLinearText","!(Z)V", (void*) PaintGlue::setLinearText},
1112    {"setUnderlineText","!(Z)V", (void*) PaintGlue::setUnderlineText},
1113    {"setStrikeThruText","!(Z)V", (void*) PaintGlue::setStrikeThruText},
1114    {"setFakeBoldText","!(Z)V", (void*) PaintGlue::setFakeBoldText},
1115    {"setFilterBitmap","!(Z)V", (void*) PaintGlue::setFilterBitmap},
1116    {"setDither","!(Z)V", (void*) PaintGlue::setDither},
1117    {"native_getStyle","!(J)I", (void*) PaintGlue::getStyle},
1118    {"native_setStyle","!(JI)V", (void*) PaintGlue::setStyle},
1119    {"getColor","!()I", (void*) PaintGlue::getColor},
1120    {"setColor","!(I)V", (void*) PaintGlue::setColor},
1121    {"getAlpha","!()I", (void*) PaintGlue::getAlpha},
1122    {"setAlpha","!(I)V", (void*) PaintGlue::setAlpha},
1123    {"getStrokeWidth","!()F", (void*) PaintGlue::getStrokeWidth},
1124    {"setStrokeWidth","!(F)V", (void*) PaintGlue::setStrokeWidth},
1125    {"getStrokeMiter","!()F", (void*) PaintGlue::getStrokeMiter},
1126    {"setStrokeMiter","!(F)V", (void*) PaintGlue::setStrokeMiter},
1127    {"native_getStrokeCap","!(J)I", (void*) PaintGlue::getStrokeCap},
1128    {"native_setStrokeCap","!(JI)V", (void*) PaintGlue::setStrokeCap},
1129    {"native_getStrokeJoin","!(J)I", (void*) PaintGlue::getStrokeJoin},
1130    {"native_setStrokeJoin","!(JI)V", (void*) PaintGlue::setStrokeJoin},
1131    {"native_getFillPath","!(JJJ)Z", (void*) PaintGlue::getFillPath},
1132    {"native_setShader","!(JJ)J", (void*) PaintGlue::setShader},
1133    {"native_setColorFilter","!(JJ)J", (void*) PaintGlue::setColorFilter},
1134    {"native_setXfermode","!(JJ)J", (void*) PaintGlue::setXfermode},
1135    {"native_setPathEffect","!(JJ)J", (void*) PaintGlue::setPathEffect},
1136    {"native_setMaskFilter","!(JJ)J", (void*) PaintGlue::setMaskFilter},
1137    {"native_setTypeface","!(JJ)J", (void*) PaintGlue::setTypeface},
1138    {"native_setRasterizer","!(JJ)J", (void*) PaintGlue::setRasterizer},
1139    {"native_getTextAlign","!(J)I", (void*) PaintGlue::getTextAlign},
1140    {"native_setTextAlign","!(JI)V", (void*) PaintGlue::setTextAlign},
1141    {"native_setTextLocale","!(JLjava/lang/String;)V", (void*) PaintGlue::setTextLocale},
1142    {"isElegantTextHeight","!()Z", (void*) PaintGlue::isElegantTextHeight},
1143    {"setElegantTextHeight","!(Z)V", (void*) PaintGlue::setElegantTextHeight},
1144    {"getTextSize","!()F", (void*) PaintGlue::getTextSize},
1145    {"setTextSize","!(F)V", (void*) PaintGlue::setTextSize},
1146    {"getTextScaleX","!()F", (void*) PaintGlue::getTextScaleX},
1147    {"setTextScaleX","!(F)V", (void*) PaintGlue::setTextScaleX},
1148    {"getTextSkewX","!()F", (void*) PaintGlue::getTextSkewX},
1149    {"setTextSkewX","!(F)V", (void*) PaintGlue::setTextSkewX},
1150    {"native_getLetterSpacing","!(J)F", (void*) PaintGlue::getLetterSpacing},
1151    {"native_setLetterSpacing","!(JF)V", (void*) PaintGlue::setLetterSpacing},
1152    {"native_setFontFeatureSettings","(JLjava/lang/String;)V",
1153            (void*) PaintGlue::setFontFeatureSettings},
1154    {"native_getHyphenEdit", "!(J)I", (void*) PaintGlue::getHyphenEdit},
1155    {"native_setHyphenEdit", "!(JI)V", (void*) PaintGlue::setHyphenEdit},
1156    {"ascent","!()F", (void*) PaintGlue::ascent},
1157    {"descent","!()F", (void*) PaintGlue::descent},
1158
1159    {"getFontMetrics", "!(Landroid/graphics/Paint$FontMetrics;)F",
1160            (void*)PaintGlue::getFontMetrics},
1161    {"getFontMetricsInt", "!(Landroid/graphics/Paint$FontMetricsInt;)I",
1162            (void*)PaintGlue::getFontMetricsInt},
1163    {"native_measureText","([CIII)F", (void*) PaintGlue::measureText_CIII},
1164    {"native_measureText","(Ljava/lang/String;I)F", (void*) PaintGlue::measureText_StringI},
1165    {"native_measureText","(Ljava/lang/String;III)F", (void*) PaintGlue::measureText_StringIII},
1166    {"native_breakText","(JJ[CIIFI[F)I", (void*) PaintGlue::breakTextC},
1167    {"native_breakText","(JJLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
1168    {"native_getTextWidths","(JJ[CIII[F)I", (void*) PaintGlue::getTextWidths___CIII_F},
1169    {"native_getTextWidths","(JJLjava/lang/String;III[F)I",
1170            (void*) PaintGlue::getTextWidths__StringIII_F},
1171    {"native_getTextRunAdvances","(JJ[CIIIIZ[FI)F",
1172            (void*) PaintGlue::getTextRunAdvances___CIIIIZ_FI},
1173    {"native_getTextRunAdvances","(JJLjava/lang/String;IIIIZ[FI)F",
1174            (void*) PaintGlue::getTextRunAdvances__StringIIIIZ_FI},
1175
1176    {"native_getTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
1177    {"native_getTextRunCursor", "(JLjava/lang/String;IIIII)I",
1178            (void*) PaintGlue::getTextRunCursor__String},
1179    {"native_getTextPath", "(JJI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
1180    {"native_getTextPath", "(JJILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
1181    {"nativeGetStringBounds", "(JJLjava/lang/String;IIILandroid/graphics/Rect;)V",
1182            (void*) PaintGlue::getStringBounds },
1183    {"nativeGetCharArrayBounds", "(JJ[CIIILandroid/graphics/Rect;)V",
1184            (void*) PaintGlue::getCharArrayBounds },
1185    {"native_hasGlyph", "(JJILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
1186    {"native_getRunAdvance", "(JJ[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
1187    {"native_getOffsetForAdvance", "(JJ[CIIIIZF)I",
1188            (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
1189
1190    {"native_setShadowLayer", "!(JFFFI)V", (void*)PaintGlue::setShadowLayer},
1191    {"native_hasShadowLayer", "!(J)Z", (void*)PaintGlue::hasShadowLayer}
1192};
1193
1194int register_android_graphics_Paint(JNIEnv* env) {
1195    gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
1196    gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
1197
1198    gFontMetrics_fieldID.top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F");
1199    gFontMetrics_fieldID.ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F");
1200    gFontMetrics_fieldID.descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F");
1201    gFontMetrics_fieldID.bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F");
1202    gFontMetrics_fieldID.leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F");
1203
1204    gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt");
1205    gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class);
1206
1207    gFontMetricsInt_fieldID.top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I");
1208    gFontMetricsInt_fieldID.ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I");
1209    gFontMetricsInt_fieldID.descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I");
1210    gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
1211    gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
1212
1213    gPaint_class = MakeGlobalRefOrDie(env, FindClassOrDie(env, "android/graphics/Paint"));
1214    gPaint_nativeInstanceID = GetFieldIDOrDie(env, gPaint_class, "mNativePaint", "J");
1215    gPaint_nativeTypefaceID = GetFieldIDOrDie(env, gPaint_class, "mNativeTypeface", "J");
1216
1217    return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
1218}
1219
1220}
1221