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