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 <nativehelper/ScopedStringChars.h>
     26 #include <nativehelper/ScopedUtfChars.h>
     27 #include <nativehelper/ScopedPrimitiveArray.h>
     28 
     29 #include "SkBlurDrawLooper.h"
     30 #include "SkColorFilter.h"
     31 #include "SkFont.h"
     32 #include "SkFontMetrics.h"
     33 #include "SkFontTypes.h"
     34 #include "SkMaskFilter.h"
     35 #include "SkPath.h"
     36 #include "SkPathEffect.h"
     37 #include "SkShader.h"
     38 #include "SkBlendMode.h"
     39 #include "unicode/uloc.h"
     40 #include "unicode/ushape.h"
     41 #include "utils/Blur.h"
     42 
     43 #include <hwui/MinikinSkia.h>
     44 #include <hwui/MinikinUtils.h>
     45 #include <hwui/Paint.h>
     46 #include <hwui/Typeface.h>
     47 #include <minikin/GraphemeBreak.h>
     48 #include <minikin/LocaleList.h>
     49 #include <minikin/Measurement.h>
     50 #include <minikin/MinikinPaint.h>
     51 #include <unicode/utf16.h>
     52 
     53 #include <cassert>
     54 #include <cstring>
     55 #include <memory>
     56 #include <vector>
     57 
     58 namespace android {
     59 
     60 struct JMetricsID {
     61     jfieldID    top;
     62     jfieldID    ascent;
     63     jfieldID    descent;
     64     jfieldID    bottom;
     65     jfieldID    leading;
     66 };
     67 
     68 static jclass   gFontMetrics_class;
     69 static JMetricsID gFontMetrics_fieldID;
     70 
     71 static jclass   gFontMetricsInt_class;
     72 static JMetricsID gFontMetricsInt_fieldID;
     73 
     74 static void getPosTextPath(const SkFont& font, const uint16_t glyphs[], int count,
     75                            const SkPoint pos[], SkPath* dst) {
     76     dst->reset();
     77     struct Rec {
     78         SkPath* fDst;
     79         const SkPoint* fPos;
     80     } rec = { dst, pos };
     81     font.getPaths(glyphs, count, [](const SkPath* src, const SkMatrix& mx, void* ctx) {
     82         Rec* rec = (Rec*)ctx;
     83         if (src) {
     84             SkMatrix tmp(mx);
     85             tmp.postTranslate(rec->fPos->fX, rec->fPos->fY);
     86             rec->fDst->addPath(*src, tmp);
     87         }
     88         rec->fPos += 1;
     89     }, &rec);
     90 }
     91 
     92 namespace PaintGlue {
     93     enum MoveOpt {
     94         AFTER, AT_OR_AFTER, BEFORE, AT_OR_BEFORE, AT
     95     };
     96 
     97     static void deletePaint(Paint* paint) {
     98         delete paint;
     99     }
    100 
    101     static jlong getNativeFinalizer(JNIEnv*, jobject) {
    102         return static_cast<jlong>(reinterpret_cast<uintptr_t>(&deletePaint));
    103     }
    104 
    105     static jlong init(JNIEnv* env, jobject) {
    106         return reinterpret_cast<jlong>(new Paint);
    107     }
    108 
    109     static jlong initWithPaint(JNIEnv* env, jobject clazz, jlong paintHandle) {
    110         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    111         Paint* obj = new Paint(*paint);
    112         return reinterpret_cast<jlong>(obj);
    113     }
    114 
    115     static int breakText(JNIEnv* env, const Paint& paint, const Typeface* typeface,
    116             const jchar text[], int count, float maxWidth, jint bidiFlags, jfloatArray jmeasured,
    117             const bool forwardScan) {
    118         size_t measuredCount = 0;
    119         float measured = 0;
    120 
    121         std::unique_ptr<float[]> advancesArray(new float[count]);
    122         MinikinUtils::measureText(&paint, static_cast<minikin::Bidi>(bidiFlags), typeface, text,
    123                 0, count, count, advancesArray.get());
    124 
    125         for (int i = 0; i < count; i++) {
    126             // traverse in the given direction
    127             int index = forwardScan ? i : (count - i - 1);
    128             float width = advancesArray[index];
    129             if (measured + width > maxWidth) {
    130                 break;
    131             }
    132             // properly handle clusters when scanning backwards
    133             if (forwardScan || width != 0.0f) {
    134                 measuredCount = i + 1;
    135             }
    136             measured += width;
    137         }
    138 
    139         if (jmeasured && env->GetArrayLength(jmeasured) > 0) {
    140             AutoJavaFloatArray autoMeasured(env, jmeasured, 1);
    141             jfloat* array = autoMeasured.ptr();
    142             array[0] = measured;
    143         }
    144         return measuredCount;
    145     }
    146 
    147     static jint breakTextC(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray jtext,
    148             jint index, jint count, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
    149         NPE_CHECK_RETURN_ZERO(env, jtext);
    150 
    151         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    152         const Typeface* typeface = paint->getAndroidTypeface();
    153 
    154         bool forwardTextDirection;
    155         if (count < 0) {
    156             forwardTextDirection = false;
    157             count = -count;
    158         }
    159         else {
    160             forwardTextDirection = true;
    161         }
    162 
    163         if ((index < 0) || (index + count > env->GetArrayLength(jtext))) {
    164             doThrowAIOOBE(env);
    165             return 0;
    166         }
    167 
    168         const jchar* text = env->GetCharArrayElements(jtext, nullptr);
    169         count = breakText(env, *paint, typeface, text + index, count, maxWidth,
    170                           bidiFlags, jmeasuredWidth, forwardTextDirection);
    171         env->ReleaseCharArrayElements(jtext, const_cast<jchar*>(text),
    172                                       JNI_ABORT);
    173         return count;
    174     }
    175 
    176     static jint breakTextS(JNIEnv* env, jobject clazz, jlong paintHandle, jstring jtext,
    177             jboolean forwards, jfloat maxWidth, jint bidiFlags, jfloatArray jmeasuredWidth) {
    178         NPE_CHECK_RETURN_ZERO(env, jtext);
    179 
    180         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    181         const Typeface* typeface = paint->getAndroidTypeface();
    182 
    183         int count = env->GetStringLength(jtext);
    184         const jchar* text = env->GetStringChars(jtext, nullptr);
    185         count = breakText(env, *paint, typeface, text, count, maxWidth, bidiFlags, jmeasuredWidth, forwards);
    186         env->ReleaseStringChars(jtext, text);
    187         return count;
    188     }
    189 
    190     static jfloat doTextAdvances(JNIEnv *env, Paint *paint, const Typeface* typeface,
    191             const jchar *text, jint start, jint count, jint contextCount, jint bidiFlags,
    192             jfloatArray advances, jint advancesIndex) {
    193         NPE_CHECK_RETURN_ZERO(env, text);
    194 
    195         if ((start | count | contextCount | advancesIndex) < 0 || contextCount < count) {
    196             doThrowAIOOBE(env);
    197             return 0;
    198         }
    199         if (count == 0) {
    200             return 0;
    201         }
    202         if (advances) {
    203             size_t advancesLength = env->GetArrayLength(advances);
    204             if ((size_t)(count  + advancesIndex) > advancesLength) {
    205                 doThrowAIOOBE(env);
    206                 return 0;
    207             }
    208         }
    209         std::unique_ptr<jfloat[]> advancesArray;
    210         if (advances) {
    211             advancesArray.reset(new jfloat[count]);
    212         }
    213         const float advance = MinikinUtils::measureText(paint,
    214                 static_cast<minikin::Bidi>(bidiFlags), typeface, text, start, count, contextCount,
    215                 advancesArray.get());
    216         if (advances) {
    217             env->SetFloatArrayRegion(advances, advancesIndex, count, advancesArray.get());
    218         }
    219         return advance;
    220     }
    221 
    222     static jfloat getTextAdvances___CIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
    223             jcharArray text, jint index, jint count, jint contextIndex, jint contextCount,
    224             jint bidiFlags, jfloatArray advances, jint advancesIndex) {
    225         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    226         const Typeface* typeface = paint->getAndroidTypeface();
    227         jchar* textArray = env->GetCharArrayElements(text, nullptr);
    228         jfloat result = doTextAdvances(env, paint, typeface, textArray + contextIndex,
    229                 index - contextIndex, count, contextCount, bidiFlags, advances, advancesIndex);
    230         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
    231         return result;
    232     }
    233 
    234     static jfloat getTextAdvances__StringIIIII_FI(JNIEnv* env, jobject clazz, jlong paintHandle,
    235             jstring text, jint start, jint end, jint contextStart, jint contextEnd, jint bidiFlags,
    236             jfloatArray advances, jint advancesIndex) {
    237         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    238         const Typeface* typeface = paint->getAndroidTypeface();
    239         const jchar* textArray = env->GetStringChars(text, nullptr);
    240         jfloat result = doTextAdvances(env, paint, typeface, textArray + contextStart,
    241                 start - contextStart, end - start, contextEnd - contextStart, bidiFlags,
    242                 advances, advancesIndex);
    243         env->ReleaseStringChars(text, textArray);
    244         return result;
    245     }
    246 
    247     static jint doTextRunCursor(JNIEnv *env, Paint* paint, const Typeface* typeface,
    248             const jchar *text, jint start, jint count, jint dir, jint offset, jint opt) {
    249         minikin::GraphemeBreak::MoveOpt moveOpt = minikin::GraphemeBreak::MoveOpt(opt);
    250         minikin::Bidi bidiFlags = dir == 1 ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
    251         std::unique_ptr<float[]> advancesArray(new float[count]);
    252         MinikinUtils::measureText(paint, bidiFlags, typeface, text, start, count, start + count,
    253                 advancesArray.get());
    254         size_t result = minikin::GraphemeBreak::getTextRunCursor(advancesArray.get(), text,
    255                 start, count, offset, moveOpt);
    256         return static_cast<jint>(result);
    257     }
    258 
    259     static jint getTextRunCursor___C(JNIEnv* env, jobject clazz, jlong paintHandle, jcharArray text,
    260             jint contextStart, jint contextCount, jint dir, jint offset, jint cursorOpt) {
    261         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    262         const Typeface* typeface = paint->getAndroidTypeface();
    263         jchar* textArray = env->GetCharArrayElements(text, nullptr);
    264         jint result = doTextRunCursor(env, paint, typeface, textArray,
    265                 contextStart, contextCount, dir, offset, cursorOpt);
    266         env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
    267         return result;
    268     }
    269 
    270     static jint getTextRunCursor__String(JNIEnv* env, jobject clazz, jlong paintHandle,
    271             jstring text, jint contextStart, jint contextEnd, jint dir, jint offset,
    272             jint cursorOpt) {
    273         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    274         const Typeface* typeface = paint->getAndroidTypeface();
    275         const jchar* textArray = env->GetStringChars(text, nullptr);
    276         jint result = doTextRunCursor(env, paint, typeface, textArray,
    277                 contextStart, contextEnd - contextStart, dir, offset, cursorOpt);
    278         env->ReleaseStringChars(text, textArray);
    279         return result;
    280     }
    281 
    282     class GetTextFunctor {
    283     public:
    284         GetTextFunctor(const minikin::Layout& layout, SkPath* path, jfloat x, jfloat y,
    285                     Paint* paint, uint16_t* glyphs, SkPoint* pos)
    286                 : layout(layout), path(path), x(x), y(y), paint(paint), glyphs(glyphs), pos(pos) {
    287         }
    288 
    289         void operator()(size_t start, size_t end) {
    290             for (size_t i = start; i < end; i++) {
    291                 glyphs[i] = layout.getGlyphId(i);
    292                 pos[i].fX = x + layout.getX(i);
    293                 pos[i].fY = y + layout.getY(i);
    294             }
    295             const SkFont& font = paint->getSkFont();
    296             if (start == 0) {
    297                 getPosTextPath(font, glyphs, end, pos, path);
    298             } else {
    299                 getPosTextPath(font, glyphs + start, end - start, pos + start, &tmpPath);
    300                 path->addPath(tmpPath);
    301             }
    302         }
    303     private:
    304         const minikin::Layout& layout;
    305         SkPath* path;
    306         jfloat x;
    307         jfloat y;
    308         Paint* paint;
    309         uint16_t* glyphs;
    310         SkPoint* pos;
    311         SkPath tmpPath;
    312     };
    313 
    314     static void getTextPath(JNIEnv* env, Paint* paint, const Typeface* typeface, const jchar* text,
    315             jint count, jint bidiFlags, jfloat x, jfloat y, SkPath* path) {
    316         minikin::Layout layout = MinikinUtils::doLayout(
    317                 paint, static_cast<minikin::Bidi>(bidiFlags), typeface,
    318                 text, count,  // text buffer
    319                 0, count,  // draw range
    320                 0, count,  // context range
    321                 nullptr);
    322         size_t nGlyphs = layout.nGlyphs();
    323         uint16_t* glyphs = new uint16_t[nGlyphs];
    324         SkPoint* pos = new SkPoint[nGlyphs];
    325 
    326         x += MinikinUtils::xOffsetForTextAlign(paint, layout);
    327         Paint::Align align = paint->getTextAlign();
    328         paint->setTextAlign(Paint::kLeft_Align);
    329         GetTextFunctor f(layout, path, x, y, paint, glyphs, pos);
    330         MinikinUtils::forFontRun(layout, paint, f);
    331         paint->setTextAlign(align);
    332         delete[] glyphs;
    333         delete[] pos;
    334     }
    335 
    336     static void getTextPath___C(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags,
    337             jcharArray text, jint index, jint count, jfloat x, jfloat y, jlong pathHandle) {
    338         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    339         const Typeface* typeface = paint->getAndroidTypeface();
    340         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
    341         const jchar* textArray = env->GetCharArrayElements(text, nullptr);
    342         getTextPath(env, paint, typeface, textArray + index, count, bidiFlags, x, y, path);
    343         env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray), JNI_ABORT);
    344     }
    345 
    346     static void getTextPath__String(JNIEnv* env, jobject clazz, jlong paintHandle, jint bidiFlags,
    347             jstring text, jint start, jint end, jfloat x, jfloat y, jlong pathHandle) {
    348         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    349         const Typeface* typeface = paint->getAndroidTypeface();
    350         SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
    351         const jchar* textArray = env->GetStringChars(text, nullptr);
    352         getTextPath(env, paint, typeface, textArray + start, end - start, bidiFlags, x, y, path);
    353         env->ReleaseStringChars(text, textArray);
    354     }
    355 
    356     static void doTextBounds(JNIEnv* env, const jchar* text, int count, jobject bounds,
    357             const Paint& paint, const Typeface* typeface, jint bidiFlags) {
    358         SkRect  r;
    359         SkIRect ir;
    360 
    361         minikin::Layout layout = MinikinUtils::doLayout(&paint,
    362                 static_cast<minikin::Bidi>(bidiFlags), typeface,
    363                 text, count,  // text buffer
    364                 0, count,  // draw range
    365                 0, count,  // context range
    366                 nullptr);
    367         minikin::MinikinRect rect;
    368         layout.getBounds(&rect);
    369         r.fLeft = rect.mLeft;
    370         r.fTop = rect.mTop;
    371         r.fRight = rect.mRight;
    372         r.fBottom = rect.mBottom;
    373         r.roundOut(&ir);
    374         GraphicsJNI::irect_to_jrect(ir, env, bounds);
    375     }
    376 
    377     static void getStringBounds(JNIEnv* env, jobject, jlong paintHandle, jstring text, jint start,
    378             jint end, jint bidiFlags, jobject bounds) {
    379         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    380         const Typeface* typeface = paint->getAndroidTypeface();
    381         const jchar* textArray = env->GetStringChars(text, nullptr);
    382         doTextBounds(env, textArray + start, end - start, bounds, *paint, typeface, bidiFlags);
    383         env->ReleaseStringChars(text, textArray);
    384     }
    385 
    386     static void getCharArrayBounds(JNIEnv* env, jobject, jlong paintHandle, jcharArray text,
    387             jint index, jint count, jint bidiFlags, jobject bounds) {
    388         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    389         const Typeface* typeface = paint->getAndroidTypeface();
    390         const jchar* textArray = env->GetCharArrayElements(text, nullptr);
    391         doTextBounds(env, textArray + index, count, bounds, *paint, typeface, bidiFlags);
    392         env->ReleaseCharArrayElements(text, const_cast<jchar*>(textArray),
    393                                       JNI_ABORT);
    394     }
    395 
    396     // Returns true if the given string is exact one pair of regional indicators.
    397     static bool isFlag(const jchar* str, size_t length) {
    398         const jchar RI_LEAD_SURROGATE = 0xD83C;
    399         const jchar RI_TRAIL_SURROGATE_MIN = 0xDDE6;
    400         const jchar RI_TRAIL_SURROGATE_MAX = 0xDDFF;
    401 
    402         if (length != 4) {
    403             return false;
    404         }
    405         if (str[0] != RI_LEAD_SURROGATE || str[2] != RI_LEAD_SURROGATE) {
    406             return false;
    407         }
    408         return RI_TRAIL_SURROGATE_MIN <= str[1] && str[1] <= RI_TRAIL_SURROGATE_MAX &&
    409             RI_TRAIL_SURROGATE_MIN <= str[3] && str[3] <= RI_TRAIL_SURROGATE_MAX;
    410     }
    411 
    412     static jboolean layoutContainsNotdef(const minikin::Layout& layout) {
    413         for (size_t i = 0; i < layout.nGlyphs(); i++) {
    414             if (layout.getGlyphId(i) == 0) {
    415                 return true;
    416             }
    417         }
    418         return false;
    419     }
    420 
    421     // Don't count glyphs that are the recommended "space" glyph and are zero width.
    422     // This logic makes assumptions about HarfBuzz layout, but does correctly handle
    423     // cases where ligatures form and zero width space glyphs are left in as
    424     // placeholders.
    425     static size_t countNonSpaceGlyphs(const minikin::Layout& layout) {
    426         size_t count = 0;
    427         static unsigned int kSpaceGlyphId = 3;
    428         for (size_t i = 0; i < layout.nGlyphs(); i++) {
    429             if (layout.getGlyphId(i) != kSpaceGlyphId || layout.getCharAdvance(i) != 0.0) {
    430                 count++;
    431             }
    432         }
    433         return count;
    434     }
    435 
    436     static jboolean hasGlyph(JNIEnv *env, jclass, jlong paintHandle, jint bidiFlags,
    437             jstring string) {
    438         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    439         const Typeface* typeface = paint->getAndroidTypeface();
    440         ScopedStringChars str(env, string);
    441 
    442         /* Start by rejecting unsupported base code point and variation selector pairs. */
    443         size_t nChars = 0;
    444         const uint32_t kStartOfString = 0xFFFFFFFF;
    445         uint32_t prevCp = kStartOfString;
    446         for (size_t i = 0; i < str.size(); i++) {
    447             jchar cu = str[i];
    448             uint32_t cp = cu;
    449             if (U16_IS_TRAIL(cu)) {
    450                 // invalid UTF-16, unpaired trailing surrogate
    451                 return false;
    452             } else if (U16_IS_LEAD(cu)) {
    453                 if (i + 1 == str.size()) {
    454                     // invalid UTF-16, unpaired leading surrogate at end of string
    455                     return false;
    456                 }
    457                 i++;
    458                 jchar cu2 = str[i];
    459                 if (!U16_IS_TRAIL(cu2)) {
    460                     // invalid UTF-16, unpaired leading surrogate
    461                     return false;
    462                 }
    463                 cp = U16_GET_SUPPLEMENTARY(cu, cu2);
    464             }
    465 
    466             if (prevCp != kStartOfString &&
    467                 ((0xFE00 <= cp && cp <= 0xFE0F) || (0xE0100 <= cp && cp <= 0xE01EF))) {
    468                 bool hasVS = MinikinUtils::hasVariationSelector(typeface, prevCp, cp);
    469                 if (!hasVS) {
    470                     // No font has a glyph for the code point and variation selector pair.
    471                     return false;
    472                 } else if (nChars == 1 && i + 1 == str.size()) {
    473                     // The string is just a codepoint and a VS, we have an authoritative answer
    474                     return true;
    475                 }
    476             }
    477             nChars++;
    478             prevCp = cp;
    479         }
    480         minikin::Layout layout = MinikinUtils::doLayout(paint,
    481                 static_cast<minikin::Bidi>(bidiFlags), typeface,
    482                 str.get(), str.size(),  // text buffer
    483                 0, str.size(),  // draw range
    484                 0, str.size(),  // context range
    485                 nullptr);
    486         size_t nGlyphs = countNonSpaceGlyphs(layout);
    487         if (nGlyphs != 1 && nChars > 1) {
    488             // multiple-character input, and was not a ligature
    489             // TODO: handle ZWJ/ZWNJ characters specially so we can detect certain ligatures
    490             // in joining scripts, such as Arabic and Mongolian.
    491             return false;
    492         }
    493 
    494         if (nGlyphs == 0 || layoutContainsNotdef(layout)) {
    495             return false;  // The collection doesn't have a glyph.
    496         }
    497 
    498         if (nChars == 2 && isFlag(str.get(), str.size())) {
    499             // Some font may have a special glyph for unsupported regional indicator pairs.
    500             // To return false for this case, need to compare the glyph id with the one of ZZ
    501             // since ZZ is reserved for unknown or invalid territory.
    502             // U+1F1FF (REGIONAL INDICATOR SYMBOL LETTER Z) is \uD83C\uDDFF in UTF16.
    503             static const jchar ZZ_FLAG_STR[] = { 0xD83C, 0xDDFF, 0xD83C, 0xDDFF };
    504             minikin::Layout zzLayout = MinikinUtils::doLayout(paint,
    505                     static_cast<minikin::Bidi>(bidiFlags), typeface,
    506                     ZZ_FLAG_STR, 4,  // text buffer
    507                     0, 4,  // draw range
    508                     0, 4,  // context range
    509                     nullptr);
    510             if (zzLayout.nGlyphs() != 1 || layoutContainsNotdef(zzLayout)) {
    511                 // The font collection doesn't have a glyph for unknown flag. Just return true.
    512                 return true;
    513             }
    514             return zzLayout.getGlyphId(0) != layout.getGlyphId(0);
    515         }
    516         return true;
    517     }
    518 
    519     static jfloat doRunAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[],
    520             jint start, jint count, jint bufSize, jboolean isRtl, jint offset) {
    521         minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
    522         if (offset == start + count) {
    523             return MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count,
    524                     bufSize, nullptr);
    525         }
    526         std::unique_ptr<float[]> advancesArray(new float[count]);
    527         MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
    528                 advancesArray.get());
    529         return minikin::getRunAdvance(advancesArray.get(), buf, start, count, offset);
    530     }
    531 
    532     static jfloat getRunAdvance___CIIIIZI_F(JNIEnv *env, jclass, jlong paintHandle, jcharArray text,
    533             jint start, jint end, jint contextStart, jint contextEnd, jboolean isRtl, jint offset) {
    534         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    535         const Typeface* typeface = paint->getAndroidTypeface();
    536         ScopedCharArrayRO textArray(env, text);
    537         jfloat result = doRunAdvance(paint, typeface, textArray.get() + contextStart,
    538                 start - contextStart, end - start, contextEnd - contextStart, isRtl,
    539                 offset - contextStart);
    540         return result;
    541     }
    542 
    543     static jint doOffsetForAdvance(const Paint* paint, const Typeface* typeface, const jchar buf[],
    544             jint start, jint count, jint bufSize, jboolean isRtl, jfloat advance) {
    545         minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
    546         std::unique_ptr<float[]> advancesArray(new float[count]);
    547         MinikinUtils::measureText(paint, bidiFlags, typeface, buf, start, count, bufSize,
    548                 advancesArray.get());
    549         return minikin::getOffsetForAdvance(advancesArray.get(), buf, start, count, advance);
    550     }
    551 
    552     static jint getOffsetForAdvance___CIIIIZF_I(JNIEnv *env, jclass, jlong paintHandle,
    553             jcharArray text, jint start, jint end, jint contextStart, jint contextEnd,
    554             jboolean isRtl, jfloat advance) {
    555         const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    556         const Typeface* typeface = paint->getAndroidTypeface();
    557         ScopedCharArrayRO textArray(env, text);
    558         jint result = doOffsetForAdvance(paint, typeface, textArray.get() + contextStart,
    559                 start - contextStart, end - start, contextEnd - contextStart, isRtl, advance);
    560         result += contextStart;
    561         return result;
    562     }
    563 
    564     // ------------------ @FastNative ---------------------------
    565 
    566     static jint setTextLocales(JNIEnv* env, jobject clazz, jlong objHandle, jstring locales) {
    567         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    568         ScopedUtfChars localesChars(env, locales);
    569         jint minikinLocaleListId = minikin::registerLocaleList(localesChars.c_str());
    570         obj->setMinikinLocaleListId(minikinLocaleListId);
    571         return minikinLocaleListId;
    572     }
    573 
    574     static void setFontFeatureSettings(JNIEnv* env, jobject clazz, jlong paintHandle, jstring settings) {
    575         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    576         if (!settings) {
    577             paint->setFontFeatureSettings(std::string());
    578         } else {
    579             ScopedUtfChars settingsChars(env, settings);
    580             paint->setFontFeatureSettings(std::string(settingsChars.c_str(), settingsChars.size()));
    581         }
    582     }
    583 
    584     static SkScalar getMetricsInternal(jlong paintHandle, SkFontMetrics *metrics) {
    585         const int kElegantTop = 2500;
    586         const int kElegantBottom = -1000;
    587         const int kElegantAscent = 1900;
    588         const int kElegantDescent = -500;
    589         const int kElegantLeading = 0;
    590         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    591         SkFont* font = &paint->getSkFont();
    592         const Typeface* typeface = paint->getAndroidTypeface();
    593         typeface = Typeface::resolveDefault(typeface);
    594         minikin::FakedFont baseFont = typeface->fFontCollection->baseFontFaked(typeface->fStyle);
    595         float saveSkewX = font->getSkewX();
    596         bool savefakeBold = font->isEmbolden();
    597         MinikinFontSkia::populateSkFont(font, baseFont.font->typeface().get(), baseFont.fakery);
    598         SkScalar spacing = font->getMetrics(metrics);
    599         // The populateSkPaint call may have changed fake bold / text skew
    600         // because we want to measure with those effects applied, so now
    601         // restore the original settings.
    602         font->setSkewX(saveSkewX);
    603         font->setEmbolden(savefakeBold);
    604         if (paint->getFamilyVariant() == minikin::FamilyVariant::ELEGANT) {
    605             SkScalar size = font->getSize();
    606             metrics->fTop = -size * kElegantTop / 2048;
    607             metrics->fBottom = -size * kElegantBottom / 2048;
    608             metrics->fAscent = -size * kElegantAscent / 2048;
    609             metrics->fDescent = -size * kElegantDescent / 2048;
    610             metrics->fLeading = size * kElegantLeading / 2048;
    611             spacing = metrics->fDescent - metrics->fAscent + metrics->fLeading;
    612         }
    613         return spacing;
    614     }
    615 
    616     static jfloat getFontMetrics(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
    617         SkFontMetrics metrics;
    618         SkScalar spacing = getMetricsInternal(paintHandle, &metrics);
    619 
    620         if (metricsObj) {
    621             SkASSERT(env->IsInstanceOf(metricsObj, gFontMetrics_class));
    622             env->SetFloatField(metricsObj, gFontMetrics_fieldID.top, SkScalarToFloat(metrics.fTop));
    623             env->SetFloatField(metricsObj, gFontMetrics_fieldID.ascent, SkScalarToFloat(metrics.fAscent));
    624             env->SetFloatField(metricsObj, gFontMetrics_fieldID.descent, SkScalarToFloat(metrics.fDescent));
    625             env->SetFloatField(metricsObj, gFontMetrics_fieldID.bottom, SkScalarToFloat(metrics.fBottom));
    626             env->SetFloatField(metricsObj, gFontMetrics_fieldID.leading, SkScalarToFloat(metrics.fLeading));
    627         }
    628         return SkScalarToFloat(spacing);
    629     }
    630 
    631     static jint getFontMetricsInt(JNIEnv* env, jobject, jlong paintHandle, jobject metricsObj) {
    632         SkFontMetrics metrics;
    633 
    634         getMetricsInternal(paintHandle, &metrics);
    635         int ascent = SkScalarRoundToInt(metrics.fAscent);
    636         int descent = SkScalarRoundToInt(metrics.fDescent);
    637         int leading = SkScalarRoundToInt(metrics.fLeading);
    638 
    639         if (metricsObj) {
    640             SkASSERT(env->IsInstanceOf(metricsObj, gFontMetricsInt_class));
    641             env->SetIntField(metricsObj, gFontMetricsInt_fieldID.top, SkScalarFloorToInt(metrics.fTop));
    642             env->SetIntField(metricsObj, gFontMetricsInt_fieldID.ascent, ascent);
    643             env->SetIntField(metricsObj, gFontMetricsInt_fieldID.descent, descent);
    644             env->SetIntField(metricsObj, gFontMetricsInt_fieldID.bottom, SkScalarCeilToInt(metrics.fBottom));
    645             env->SetIntField(metricsObj, gFontMetricsInt_fieldID.leading, leading);
    646         }
    647         return descent - ascent + leading;
    648     }
    649 
    650 
    651     // ------------------ @CriticalNative ---------------------------
    652 
    653     static void reset(jlong objHandle) {
    654         reinterpret_cast<Paint*>(objHandle)->reset();
    655     }
    656 
    657     static void assign(jlong dstPaintHandle, jlong srcPaintHandle) {
    658         Paint* dst = reinterpret_cast<Paint*>(dstPaintHandle);
    659         const Paint* src = reinterpret_cast<Paint*>(srcPaintHandle);
    660         *dst = *src;
    661     }
    662 
    663     static jint getFlags(jlong paintHandle) {
    664         uint32_t flags = reinterpret_cast<Paint*>(paintHandle)->getJavaFlags();
    665         return static_cast<jint>(flags);
    666     }
    667 
    668     static void setFlags(jlong paintHandle, jint flags) {
    669         reinterpret_cast<Paint*>(paintHandle)->setJavaFlags(flags);
    670     }
    671 
    672     static jint getHinting(jlong paintHandle) {
    673         return (SkFontHinting)reinterpret_cast<Paint*>(paintHandle)->getSkFont().getHinting()
    674                 == kNo_SkFontHinting ? 0 : 1;
    675     }
    676 
    677     static void setHinting(jlong paintHandle, jint mode) {
    678         reinterpret_cast<Paint*>(paintHandle)->getSkFont().setHinting(
    679                 mode == 0 ? kNo_SkFontHinting : kNormal_SkFontHinting);
    680     }
    681 
    682     static void setAntiAlias(jlong paintHandle, jboolean aa) {
    683         reinterpret_cast<Paint*>(paintHandle)->setAntiAlias(aa);
    684     }
    685 
    686     static void setLinearText(jlong paintHandle, jboolean linearText) {
    687         reinterpret_cast<Paint*>(paintHandle)->getSkFont().setLinearMetrics(linearText);
    688     }
    689 
    690     static void setSubpixelText(jlong paintHandle, jboolean subpixelText) {
    691         reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSubpixel(subpixelText);
    692     }
    693 
    694     static void setUnderlineText(jlong paintHandle, jboolean underlineText) {
    695         reinterpret_cast<Paint*>(paintHandle)->setUnderline(underlineText);
    696     }
    697 
    698     static void setStrikeThruText(jlong paintHandle, jboolean strikeThruText) {
    699         reinterpret_cast<Paint*>(paintHandle)->setStrikeThru(strikeThruText);
    700     }
    701 
    702     static void setFakeBoldText(jlong paintHandle, jboolean fakeBoldText) {
    703         reinterpret_cast<Paint*>(paintHandle)->getSkFont().setEmbolden(fakeBoldText);
    704     }
    705 
    706     static void setFilterBitmap(jlong paintHandle, jboolean filterBitmap) {
    707         reinterpret_cast<Paint*>(paintHandle)->setFilterQuality(
    708                 filterBitmap ? kLow_SkFilterQuality : kNone_SkFilterQuality);
    709     }
    710 
    711     static void setDither(jlong paintHandle, jboolean dither) {
    712         reinterpret_cast<Paint*>(paintHandle)->setDither(dither);
    713     }
    714 
    715     static jint getStyle(jlong objHandle) {
    716         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    717         return static_cast<jint>(obj->getStyle());
    718     }
    719 
    720     static void setStyle(jlong objHandle, jint styleHandle) {
    721         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    722         Paint::Style style = static_cast<Paint::Style>(styleHandle);
    723         obj->setStyle(style);
    724     }
    725 
    726     static void setColorLong(jlong paintHandle, jlong colorSpaceHandle,
    727             jlong colorLong) {
    728         SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
    729         sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
    730         reinterpret_cast<Paint*>(paintHandle)->setColor4f(color, cs.get());
    731     }
    732 
    733     static void setColor(jlong paintHandle, jint color) {
    734         reinterpret_cast<Paint*>(paintHandle)->setColor(color);
    735     }
    736 
    737     static void setAlpha(jlong paintHandle, jint a) {
    738         reinterpret_cast<Paint*>(paintHandle)->setAlpha(a);
    739     }
    740 
    741     static jfloat getStrokeWidth(jlong paintHandle) {
    742         return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeWidth());
    743     }
    744 
    745     static void setStrokeWidth(jlong paintHandle, jfloat width) {
    746         reinterpret_cast<Paint*>(paintHandle)->setStrokeWidth(width);
    747     }
    748 
    749     static jfloat getStrokeMiter(jlong paintHandle) {
    750         return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getStrokeMiter());
    751     }
    752 
    753     static void setStrokeMiter(jlong paintHandle, jfloat miter) {
    754         reinterpret_cast<Paint*>(paintHandle)->setStrokeMiter(miter);
    755     }
    756 
    757     static jint getStrokeCap(jlong objHandle) {
    758         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    759         return static_cast<jint>(obj->getStrokeCap());
    760     }
    761 
    762     static void setStrokeCap(jlong objHandle, jint capHandle) {
    763         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    764         Paint::Cap cap = static_cast<Paint::Cap>(capHandle);
    765         obj->setStrokeCap(cap);
    766     }
    767 
    768     static jint getStrokeJoin(jlong objHandle) {
    769         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    770         return static_cast<jint>(obj->getStrokeJoin());
    771     }
    772 
    773     static void setStrokeJoin(jlong objHandle, jint joinHandle) {
    774         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    775         Paint::Join join = (Paint::Join) joinHandle;
    776         obj->setStrokeJoin(join);
    777     }
    778 
    779     static jboolean getFillPath(jlong objHandle, jlong srcHandle, jlong dstHandle) {
    780         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    781         SkPath* src = reinterpret_cast<SkPath*>(srcHandle);
    782         SkPath* dst = reinterpret_cast<SkPath*>(dstHandle);
    783         return obj->getFillPath(*src, dst) ? JNI_TRUE : JNI_FALSE;
    784     }
    785 
    786     static jlong setShader(jlong objHandle, jlong shaderHandle) {
    787         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    788         SkShader* shader = reinterpret_cast<SkShader*>(shaderHandle);
    789         obj->setShader(sk_ref_sp(shader));
    790         return reinterpret_cast<jlong>(obj->getShader());
    791     }
    792 
    793     static jlong setColorFilter(jlong objHandle, jlong filterHandle) {
    794         Paint* obj = reinterpret_cast<Paint *>(objHandle);
    795         SkColorFilter* filter  = reinterpret_cast<SkColorFilter *>(filterHandle);
    796         obj->setColorFilter(sk_ref_sp(filter));
    797         return reinterpret_cast<jlong>(obj->getColorFilter());
    798     }
    799 
    800     static void setXfermode(jlong paintHandle, jint xfermodeHandle) {
    801         // validate that the Java enum values match our expectations
    802         static_assert(0 == static_cast<int>(SkBlendMode::kClear), "xfermode_mismatch");
    803         static_assert(1 == static_cast<int>(SkBlendMode::kSrc), "xfermode_mismatch");
    804         static_assert(2 == static_cast<int>(SkBlendMode::kDst), "xfermode_mismatch");
    805         static_assert(3 == static_cast<int>(SkBlendMode::kSrcOver), "xfermode_mismatch");
    806         static_assert(4 == static_cast<int>(SkBlendMode::kDstOver), "xfermode_mismatch");
    807         static_assert(5 == static_cast<int>(SkBlendMode::kSrcIn), "xfermode_mismatch");
    808         static_assert(6 == static_cast<int>(SkBlendMode::kDstIn), "xfermode_mismatch");
    809         static_assert(7 == static_cast<int>(SkBlendMode::kSrcOut), "xfermode_mismatch");
    810         static_assert(8 == static_cast<int>(SkBlendMode::kDstOut), "xfermode_mismatch");
    811         static_assert(9 == static_cast<int>(SkBlendMode::kSrcATop), "xfermode_mismatch");
    812         static_assert(10 == static_cast<int>(SkBlendMode::kDstATop), "xfermode_mismatch");
    813         static_assert(11 == static_cast<int>(SkBlendMode::kXor), "xfermode_mismatch");
    814         static_assert(12 == static_cast<int>(SkBlendMode::kPlus), "xfermode_mismatch");
    815         static_assert(13 == static_cast<int>(SkBlendMode::kModulate), "xfermode_mismatch");
    816         static_assert(14 == static_cast<int>(SkBlendMode::kScreen), "xfermode_mismatch");
    817         static_assert(15 == static_cast<int>(SkBlendMode::kOverlay), "xfermode_mismatch");
    818         static_assert(16 == static_cast<int>(SkBlendMode::kDarken), "xfermode_mismatch");
    819         static_assert(17 == static_cast<int>(SkBlendMode::kLighten), "xfermode_mismatch");
    820         static_assert(18 == static_cast<int>(SkBlendMode::kColorDodge), "xfermode mismatch");
    821         static_assert(19 == static_cast<int>(SkBlendMode::kColorBurn), "xfermode mismatch");
    822         static_assert(20 == static_cast<int>(SkBlendMode::kHardLight), "xfermode mismatch");
    823         static_assert(21 == static_cast<int>(SkBlendMode::kSoftLight), "xfermode mismatch");
    824         static_assert(22 == static_cast<int>(SkBlendMode::kDifference), "xfermode mismatch");
    825         static_assert(23 == static_cast<int>(SkBlendMode::kExclusion), "xfermode mismatch");
    826         static_assert(24 == static_cast<int>(SkBlendMode::kMultiply), "xfermode mismatch");
    827         static_assert(25 == static_cast<int>(SkBlendMode::kHue), "xfermode mismatch");
    828         static_assert(26 == static_cast<int>(SkBlendMode::kSaturation), "xfermode mismatch");
    829         static_assert(27 == static_cast<int>(SkBlendMode::kColor), "xfermode mismatch");
    830         static_assert(28 == static_cast<int>(SkBlendMode::kLuminosity), "xfermode mismatch");
    831 
    832         SkBlendMode mode = static_cast<SkBlendMode>(xfermodeHandle);
    833         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    834         paint->setBlendMode(mode);
    835     }
    836 
    837     static jlong setPathEffect(jlong objHandle, jlong effectHandle) {
    838         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    839         SkPathEffect* effect  = reinterpret_cast<SkPathEffect*>(effectHandle);
    840         obj->setPathEffect(sk_ref_sp(effect));
    841         return reinterpret_cast<jlong>(obj->getPathEffect());
    842     }
    843 
    844     static jlong setMaskFilter(jlong objHandle, jlong maskfilterHandle) {
    845         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    846         SkMaskFilter* maskfilter  = reinterpret_cast<SkMaskFilter*>(maskfilterHandle);
    847         obj->setMaskFilter(sk_ref_sp(maskfilter));
    848         return reinterpret_cast<jlong>(obj->getMaskFilter());
    849     }
    850 
    851     static void setTypeface(jlong objHandle, jlong typefaceHandle) {
    852         Paint* paint = reinterpret_cast<Paint*>(objHandle);
    853         paint->setAndroidTypeface(reinterpret_cast<Typeface*>(typefaceHandle));
    854     }
    855 
    856     static jint getTextAlign(jlong objHandle) {
    857         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    858         return static_cast<jint>(obj->getTextAlign());
    859     }
    860 
    861     static void setTextAlign(jlong objHandle, jint alignHandle) {
    862         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    863         Paint::Align align = static_cast<Paint::Align>(alignHandle);
    864         obj->setTextAlign(align);
    865     }
    866 
    867     static void setTextLocalesByMinikinLocaleListId(jlong objHandle,
    868             jint minikinLocaleListId) {
    869         Paint* obj = reinterpret_cast<Paint*>(objHandle);
    870         obj->setMinikinLocaleListId(minikinLocaleListId);
    871     }
    872 
    873     static jboolean isElegantTextHeight(jlong paintHandle) {
    874         Paint* obj = reinterpret_cast<Paint*>(paintHandle);
    875         return obj->getFamilyVariant() == minikin::FamilyVariant::ELEGANT;
    876     }
    877 
    878     static void setElegantTextHeight(jlong paintHandle, jboolean aa) {
    879         Paint* obj = reinterpret_cast<Paint*>(paintHandle);
    880         obj->setFamilyVariant(
    881                 aa ? minikin::FamilyVariant::ELEGANT : minikin::FamilyVariant::DEFAULT);
    882     }
    883 
    884     static jfloat getTextSize(jlong paintHandle) {
    885         return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize());
    886     }
    887 
    888     static void setTextSize(jlong paintHandle, jfloat textSize) {
    889         if (textSize >= 0) {
    890             reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSize(textSize);
    891         }
    892     }
    893 
    894     static jfloat getTextScaleX(jlong paintHandle) {
    895         return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getScaleX());
    896     }
    897 
    898     static void setTextScaleX(jlong paintHandle, jfloat scaleX) {
    899         reinterpret_cast<Paint*>(paintHandle)->getSkFont().setScaleX(scaleX);
    900     }
    901 
    902     static jfloat getTextSkewX(jlong paintHandle) {
    903         return SkScalarToFloat(reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSkewX());
    904     }
    905 
    906     static void setTextSkewX(jlong paintHandle, jfloat skewX) {
    907         reinterpret_cast<Paint*>(paintHandle)->getSkFont().setSkewX(skewX);
    908     }
    909 
    910     static jfloat getLetterSpacing(jlong paintHandle) {
    911         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    912         return paint->getLetterSpacing();
    913     }
    914 
    915     static void setLetterSpacing(jlong paintHandle, jfloat letterSpacing) {
    916         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    917         paint->setLetterSpacing(letterSpacing);
    918     }
    919 
    920     static jfloat getWordSpacing(jlong paintHandle) {
    921         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    922         return paint->getWordSpacing();
    923     }
    924 
    925     static void setWordSpacing(jlong paintHandle, jfloat wordSpacing) {
    926         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    927         paint->setWordSpacing(wordSpacing);
    928     }
    929 
    930     static jint getStartHyphenEdit(jlong paintHandle, jint hyphen) {
    931         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    932         return static_cast<jint>(paint->getStartHyphenEdit());
    933     }
    934 
    935     static jint getEndHyphenEdit(jlong paintHandle, jint hyphen) {
    936         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    937         return static_cast<jint>(paint->getEndHyphenEdit());
    938     }
    939 
    940     static void setStartHyphenEdit(jlong paintHandle, jint hyphen) {
    941         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    942         paint->setStartHyphenEdit((uint32_t)hyphen);
    943     }
    944 
    945     static void setEndHyphenEdit(jlong paintHandle, jint hyphen) {
    946         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
    947         paint->setEndHyphenEdit((uint32_t)hyphen);
    948     }
    949 
    950     static jfloat ascent(jlong paintHandle) {
    951         SkFontMetrics metrics;
    952         getMetricsInternal(paintHandle, &metrics);
    953         return SkScalarToFloat(metrics.fAscent);
    954     }
    955 
    956     static jfloat descent(jlong paintHandle) {
    957         SkFontMetrics metrics;
    958         getMetricsInternal(paintHandle, &metrics);
    959         return SkScalarToFloat(metrics.fDescent);
    960     }
    961 
    962     static jfloat getUnderlinePosition(jlong paintHandle) {
    963         SkFontMetrics metrics;
    964         getMetricsInternal(paintHandle, &metrics);
    965         SkScalar position;
    966         if (metrics.hasUnderlinePosition(&position)) {
    967             return SkScalarToFloat(position);
    968         } else {
    969             const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
    970             return SkScalarToFloat(Paint::kStdUnderline_Top * textSize);
    971         }
    972     }
    973 
    974     static jfloat getUnderlineThickness(jlong paintHandle) {
    975         SkFontMetrics metrics;
    976         getMetricsInternal(paintHandle, &metrics);
    977         SkScalar thickness;
    978         if (metrics.hasUnderlineThickness(&thickness)) {
    979             return SkScalarToFloat(thickness);
    980         } else {
    981             const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
    982             return SkScalarToFloat(Paint::kStdUnderline_Thickness * textSize);
    983         }
    984     }
    985 
    986     static jfloat getStrikeThruPosition(jlong paintHandle) {
    987         const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
    988         return SkScalarToFloat(Paint::kStdStrikeThru_Top * textSize);
    989     }
    990 
    991     static jfloat getStrikeThruThickness(jlong paintHandle) {
    992         const SkScalar textSize = reinterpret_cast<Paint*>(paintHandle)->getSkFont().getSize();
    993         return SkScalarToFloat(Paint::kStdStrikeThru_Thickness * textSize);
    994     }
    995 
    996     static void setShadowLayer(jlong paintHandle, jfloat radius,
    997                                jfloat dx, jfloat dy, jlong colorSpaceHandle,
    998                                jlong colorLong) {
    999         SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
   1000         sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
   1001 
   1002         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
   1003         if (radius <= 0) {
   1004             paint->setLooper(nullptr);
   1005         }
   1006         else {
   1007             SkScalar sigma = android::uirenderer::Blur::convertRadiusToSigma(radius);
   1008             paint->setLooper(SkBlurDrawLooper::Make(color, cs.get(), sigma, dx, dy));
   1009         }
   1010     }
   1011 
   1012     static jboolean hasShadowLayer(jlong paintHandle) {
   1013         Paint* paint = reinterpret_cast<Paint*>(paintHandle);
   1014         return paint->getLooper() && paint->getLooper()->asABlurShadow(nullptr);
   1015     }
   1016 
   1017     static jboolean equalsForTextMeasurement(jlong lPaint, jlong rPaint) {
   1018         if (lPaint == rPaint) {
   1019             return true;
   1020         }
   1021         Paint* leftPaint = reinterpret_cast<Paint*>(lPaint);
   1022         Paint* rightPaint = reinterpret_cast<Paint*>(rPaint);
   1023 
   1024         const Typeface* leftTypeface = Typeface::resolveDefault(leftPaint->getAndroidTypeface());
   1025         const Typeface* rightTypeface = Typeface::resolveDefault(rightPaint->getAndroidTypeface());
   1026         minikin::MinikinPaint leftMinikinPaint
   1027                 = MinikinUtils::prepareMinikinPaint(leftPaint, leftTypeface);
   1028         minikin::MinikinPaint rightMinikinPaint
   1029                 = MinikinUtils::prepareMinikinPaint(rightPaint, rightTypeface);
   1030 
   1031         return leftMinikinPaint == rightMinikinPaint;
   1032     }
   1033 
   1034 }; // namespace PaintGlue
   1035 
   1036 static const JNINativeMethod methods[] = {
   1037     {"nGetNativeFinalizer", "()J", (void*) PaintGlue::getNativeFinalizer},
   1038     {"nInit","()J", (void*) PaintGlue::init},
   1039     {"nInitWithPaint","(J)J", (void*) PaintGlue::initWithPaint},
   1040     {"nBreakText","(J[CIIFI[F)I", (void*) PaintGlue::breakTextC},
   1041     {"nBreakText","(JLjava/lang/String;ZFI[F)I", (void*) PaintGlue::breakTextS},
   1042     {"nGetTextAdvances","(J[CIIIII[FI)F",
   1043             (void*) PaintGlue::getTextAdvances___CIIIII_FI},
   1044     {"nGetTextAdvances","(JLjava/lang/String;IIIII[FI)F",
   1045             (void*) PaintGlue::getTextAdvances__StringIIIII_FI},
   1046 
   1047     {"nGetTextRunCursor", "(J[CIIIII)I", (void*) PaintGlue::getTextRunCursor___C},
   1048     {"nGetTextRunCursor", "(JLjava/lang/String;IIIII)I",
   1049             (void*) PaintGlue::getTextRunCursor__String},
   1050     {"nGetTextPath", "(JI[CIIFFJ)V", (void*) PaintGlue::getTextPath___C},
   1051     {"nGetTextPath", "(JILjava/lang/String;IIFFJ)V", (void*) PaintGlue::getTextPath__String},
   1052     {"nGetStringBounds", "(JLjava/lang/String;IIILandroid/graphics/Rect;)V",
   1053             (void*) PaintGlue::getStringBounds },
   1054     {"nGetCharArrayBounds", "(J[CIIILandroid/graphics/Rect;)V",
   1055             (void*) PaintGlue::getCharArrayBounds },
   1056     {"nHasGlyph", "(JILjava/lang/String;)Z", (void*) PaintGlue::hasGlyph },
   1057     {"nGetRunAdvance", "(J[CIIIIZI)F", (void*) PaintGlue::getRunAdvance___CIIIIZI_F},
   1058     {"nGetOffsetForAdvance", "(J[CIIIIZF)I",
   1059             (void*) PaintGlue::getOffsetForAdvance___CIIIIZF_I},
   1060 
   1061     // --------------- @FastNative ----------------------
   1062 
   1063     {"nSetTextLocales","(JLjava/lang/String;)I", (void*) PaintGlue::setTextLocales},
   1064     {"nSetFontFeatureSettings","(JLjava/lang/String;)V",
   1065                 (void*) PaintGlue::setFontFeatureSettings},
   1066     {"nGetFontMetrics", "(JLandroid/graphics/Paint$FontMetrics;)F",
   1067                 (void*)PaintGlue::getFontMetrics},
   1068     {"nGetFontMetricsInt", "(JLandroid/graphics/Paint$FontMetricsInt;)I",
   1069             (void*)PaintGlue::getFontMetricsInt},
   1070 
   1071     // --------------- @CriticalNative ------------------
   1072 
   1073     {"nReset","(J)V", (void*) PaintGlue::reset},
   1074     {"nSet","(JJ)V", (void*) PaintGlue::assign},
   1075     {"nGetFlags","(J)I", (void*) PaintGlue::getFlags},
   1076     {"nSetFlags","(JI)V", (void*) PaintGlue::setFlags},
   1077     {"nGetHinting","(J)I", (void*) PaintGlue::getHinting},
   1078     {"nSetHinting","(JI)V", (void*) PaintGlue::setHinting},
   1079     {"nSetAntiAlias","(JZ)V", (void*) PaintGlue::setAntiAlias},
   1080     {"nSetSubpixelText","(JZ)V", (void*) PaintGlue::setSubpixelText},
   1081     {"nSetLinearText","(JZ)V", (void*) PaintGlue::setLinearText},
   1082     {"nSetUnderlineText","(JZ)V", (void*) PaintGlue::setUnderlineText},
   1083     {"nSetStrikeThruText","(JZ)V", (void*) PaintGlue::setStrikeThruText},
   1084     {"nSetFakeBoldText","(JZ)V", (void*) PaintGlue::setFakeBoldText},
   1085     {"nSetFilterBitmap","(JZ)V", (void*) PaintGlue::setFilterBitmap},
   1086     {"nSetDither","(JZ)V", (void*) PaintGlue::setDither},
   1087     {"nGetStyle","(J)I", (void*) PaintGlue::getStyle},
   1088     {"nSetStyle","(JI)V", (void*) PaintGlue::setStyle},
   1089     {"nSetColor","(JI)V", (void*) PaintGlue::setColor},
   1090     {"nSetColor","(JJJ)V", (void*) PaintGlue::setColorLong},
   1091     {"nSetAlpha","(JI)V", (void*) PaintGlue::setAlpha},
   1092     {"nGetStrokeWidth","(J)F", (void*) PaintGlue::getStrokeWidth},
   1093     {"nSetStrokeWidth","(JF)V", (void*) PaintGlue::setStrokeWidth},
   1094     {"nGetStrokeMiter","(J)F", (void*) PaintGlue::getStrokeMiter},
   1095     {"nSetStrokeMiter","(JF)V", (void*) PaintGlue::setStrokeMiter},
   1096     {"nGetStrokeCap","(J)I", (void*) PaintGlue::getStrokeCap},
   1097     {"nSetStrokeCap","(JI)V", (void*) PaintGlue::setStrokeCap},
   1098     {"nGetStrokeJoin","(J)I", (void*) PaintGlue::getStrokeJoin},
   1099     {"nSetStrokeJoin","(JI)V", (void*) PaintGlue::setStrokeJoin},
   1100     {"nGetFillPath","(JJJ)Z", (void*) PaintGlue::getFillPath},
   1101     {"nSetShader","(JJ)J", (void*) PaintGlue::setShader},
   1102     {"nSetColorFilter","(JJ)J", (void*) PaintGlue::setColorFilter},
   1103     {"nSetXfermode","(JI)V", (void*) PaintGlue::setXfermode},
   1104     {"nSetPathEffect","(JJ)J", (void*) PaintGlue::setPathEffect},
   1105     {"nSetMaskFilter","(JJ)J", (void*) PaintGlue::setMaskFilter},
   1106     {"nSetTypeface","(JJ)V", (void*) PaintGlue::setTypeface},
   1107     {"nGetTextAlign","(J)I", (void*) PaintGlue::getTextAlign},
   1108     {"nSetTextAlign","(JI)V", (void*) PaintGlue::setTextAlign},
   1109     {"nSetTextLocalesByMinikinLocaleListId","(JI)V",
   1110             (void*) PaintGlue::setTextLocalesByMinikinLocaleListId},
   1111     {"nIsElegantTextHeight","(J)Z", (void*) PaintGlue::isElegantTextHeight},
   1112     {"nSetElegantTextHeight","(JZ)V", (void*) PaintGlue::setElegantTextHeight},
   1113     {"nGetTextSize","(J)F", (void*) PaintGlue::getTextSize},
   1114     {"nSetTextSize","(JF)V", (void*) PaintGlue::setTextSize},
   1115     {"nGetTextScaleX","(J)F", (void*) PaintGlue::getTextScaleX},
   1116     {"nSetTextScaleX","(JF)V", (void*) PaintGlue::setTextScaleX},
   1117     {"nGetTextSkewX","(J)F", (void*) PaintGlue::getTextSkewX},
   1118     {"nSetTextSkewX","(JF)V", (void*) PaintGlue::setTextSkewX},
   1119     {"nGetLetterSpacing","(J)F", (void*) PaintGlue::getLetterSpacing},
   1120     {"nSetLetterSpacing","(JF)V", (void*) PaintGlue::setLetterSpacing},
   1121     {"nGetWordSpacing","(J)F", (void*) PaintGlue::getWordSpacing},
   1122     {"nSetWordSpacing","(JF)V", (void*) PaintGlue::setWordSpacing},
   1123     {"nGetStartHyphenEdit", "(J)I", (void*) PaintGlue::getStartHyphenEdit},
   1124     {"nGetEndHyphenEdit", "(J)I", (void*) PaintGlue::getEndHyphenEdit},
   1125     {"nSetStartHyphenEdit", "(JI)V", (void*) PaintGlue::setStartHyphenEdit},
   1126     {"nSetEndHyphenEdit", "(JI)V", (void*) PaintGlue::setEndHyphenEdit},
   1127     {"nAscent","(J)F", (void*) PaintGlue::ascent},
   1128     {"nDescent","(J)F", (void*) PaintGlue::descent},
   1129     {"nGetUnderlinePosition","(J)F", (void*) PaintGlue::getUnderlinePosition},
   1130     {"nGetUnderlineThickness","(J)F", (void*) PaintGlue::getUnderlineThickness},
   1131     {"nGetStrikeThruPosition","(J)F", (void*) PaintGlue::getStrikeThruPosition},
   1132     {"nGetStrikeThruThickness","(J)F", (void*) PaintGlue::getStrikeThruThickness},
   1133     {"nSetShadowLayer", "(JFFFJJ)V", (void*)PaintGlue::setShadowLayer},
   1134     {"nHasShadowLayer", "(J)Z", (void*)PaintGlue::hasShadowLayer},
   1135     {"nEqualsForTextMeasurement", "(JJ)Z", (void*)PaintGlue::equalsForTextMeasurement},
   1136 };
   1137 
   1138 int register_android_graphics_Paint(JNIEnv* env) {
   1139     gFontMetrics_class = FindClassOrDie(env, "android/graphics/Paint$FontMetrics");
   1140     gFontMetrics_class = MakeGlobalRefOrDie(env, gFontMetrics_class);
   1141 
   1142     gFontMetrics_fieldID.top = GetFieldIDOrDie(env, gFontMetrics_class, "top", "F");
   1143     gFontMetrics_fieldID.ascent = GetFieldIDOrDie(env, gFontMetrics_class, "ascent", "F");
   1144     gFontMetrics_fieldID.descent = GetFieldIDOrDie(env, gFontMetrics_class, "descent", "F");
   1145     gFontMetrics_fieldID.bottom = GetFieldIDOrDie(env, gFontMetrics_class, "bottom", "F");
   1146     gFontMetrics_fieldID.leading = GetFieldIDOrDie(env, gFontMetrics_class, "leading", "F");
   1147 
   1148     gFontMetricsInt_class = FindClassOrDie(env, "android/graphics/Paint$FontMetricsInt");
   1149     gFontMetricsInt_class = MakeGlobalRefOrDie(env, gFontMetricsInt_class);
   1150 
   1151     gFontMetricsInt_fieldID.top = GetFieldIDOrDie(env, gFontMetricsInt_class, "top", "I");
   1152     gFontMetricsInt_fieldID.ascent = GetFieldIDOrDie(env, gFontMetricsInt_class, "ascent", "I");
   1153     gFontMetricsInt_fieldID.descent = GetFieldIDOrDie(env, gFontMetricsInt_class, "descent", "I");
   1154     gFontMetricsInt_fieldID.bottom = GetFieldIDOrDie(env, gFontMetricsInt_class, "bottom", "I");
   1155     gFontMetricsInt_fieldID.leading = GetFieldIDOrDie(env, gFontMetricsInt_class, "leading", "I");
   1156 
   1157     return RegisterMethodsOrDie(env, "android/graphics/Paint", methods, NELEM(methods));
   1158 }
   1159 
   1160 }
   1161