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