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