Home | History | Annotate | Download | only in graphics
      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 
     52 namespace android {
     53 
     54 struct JMetricsID {
     55     jfieldID    top;
     56     jfieldID    ascent;
     57     jfieldID    descent;
     58     jfieldID    bottom;
     59     jfieldID    leading;
     60 };
     61 
     62 static jclass   gFontMetrics_class;
     63 static JMetricsID gFontMetrics_fieldID;
     64 
     65 static jclass   gFontMetricsInt_class;
     66 static JMetricsID gFontMetricsInt_fieldID;
     67 
     68 static jclass   gPaint_class;
     69 static jfieldID gPaint_nativeInstanceID;
     70 static jfieldID gPaint_nativeTypefaceID;
     71 
     72 static void defaultSettingsForAndroid(Paint* paint) {
     73     // GlyphID encoding is required because we are using Harfbuzz shaping
     74     paint->setTextEncoding(Paint::kGlyphID_TextEncoding);
     75 }
     76 
     77 struct LocaleCacheEntry {
     78     std::string javaLocale;
     79     std::string languageTag;
     80 };
     81 
     82 static thread_local LocaleCacheEntry sSingleEntryLocaleCache;
     83 
     84 class PaintGlue {
     85 public:
     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 
   1098 static 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 
   1194 int 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