1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "GraphicsJNI.h"
18 
19 #ifdef __ANDROID_
20 #include <android/api-level.h>
21 #else
22 #define __ANDROID_API_P__ 28
23 #endif
24 #include <androidfw/ResourceTypes.h>
25 #include <hwui/Canvas.h>
26 #include <hwui/Paint.h>
27 #include <hwui/PaintFilter.h>
28 #include <hwui/Typeface.h>
29 #include <minikin/Layout.h>
30 #include <nativehelper/ScopedPrimitiveArray.h>
31 #include <nativehelper/ScopedStringChars.h>
32 
33 #include "Bitmap.h"
34 #include "FontUtils.h"
35 #include "SkBitmap.h"
36 #include "SkBlendMode.h"
37 #include "SkClipOp.h"
38 #include "SkColor.h"
39 #include "SkColorSpace.h"
40 #include "SkGraphics.h"
41 #include "SkImageInfo.h"
42 #include "SkMatrix.h"
43 #include "SkPath.h"
44 #include "SkPoint.h"
45 #include "SkRRect.h"
46 #include "SkRect.h"
47 #include "SkRefCnt.h"
48 #include "SkRegion.h"
49 #include "SkScalar.h"
50 #include "SkVertices.h"
51 
52 namespace minikin {
53 class MeasuredText;
54 }  // namespace minikin
55 
56 namespace android {
57 
58 namespace CanvasJNI {
59 
get_canvas(jlong canvasHandle)60 static Canvas* get_canvas(jlong canvasHandle) {
61     return reinterpret_cast<Canvas*>(canvasHandle);
62 }
63 
delete_canvas(Canvas * canvas)64 static void delete_canvas(Canvas* canvas) {
65     delete canvas;
66 }
67 
getNativeFinalizer(JNIEnv * env,jobject clazz)68 static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) {
69     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&delete_canvas));
70 }
71 
72 // Native wrapper constructor used by Canvas(Bitmap)
initRaster(JNIEnv * env,jobject,jlong bitmapHandle)73 static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
74     SkBitmap bitmap;
75     if (bitmapHandle != 0) {
76         bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap);
77     }
78     return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
79 }
80 
81 // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
82 // optionally copying canvas matrix & clip state.
setBitmap(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle)83 static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle) {
84     SkBitmap bitmap;
85     if (bitmapHandle != 0) {
86         bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap);
87     }
88     get_canvas(canvasHandle)->setBitmap(bitmap);
89 }
90 
isHighContrastText(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)91 static jboolean isHighContrastText(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
92     return get_canvas(canvasHandle)->isHighContrastText() ? JNI_TRUE : JNI_FALSE;
93 }
94 
isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)95 static jboolean isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
96     return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
97 }
98 
getWidth(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)99 static jint getWidth(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
100     return static_cast<jint>(get_canvas(canvasHandle)->width());
101 }
102 
getHeight(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)103 static jint getHeight(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
104     return static_cast<jint>(get_canvas(canvasHandle)->height());
105 }
106 
save(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jint flagsHandle)107 static jint save(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint flagsHandle) {
108     SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
109     return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
110 }
111 
saveLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat l,jfloat t,jfloat r,jfloat b,jlong paintHandle)112 static jint saveLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
113                       jfloat r, jfloat b, jlong paintHandle) {
114     Paint* paint  = reinterpret_cast<Paint*>(paintHandle);
115     return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint));
116 }
117 
saveLayerAlpha(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat l,jfloat t,jfloat r,jfloat b,jint alpha)118 static jint saveLayerAlpha(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
119                            jfloat r, jfloat b, jint alpha) {
120     return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha));
121 }
122 
saveUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jint l,jint t,jint r,jint b)123 static jint saveUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint l, jint t, jint r, jint b) {
124     return reinterpret_cast<jint>(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b));
125 }
126 
restoreUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jint saveCount,jlong paintHandle)127 static void restoreUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint saveCount, jlong paintHandle) {
128     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
129     get_canvas(canvasHandle)->restoreUnclippedLayer(saveCount, *paint);
130 }
131 
restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)132 static jboolean restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
133     Canvas* canvas = get_canvas(canvasHandle);
134     if (canvas->getSaveCount() <= 1) {
135         return false; // cannot restore anymore
136     }
137     canvas->restore();
138     return true; // success
139 }
140 
restoreToCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jint saveCount)141 static void restoreToCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint saveCount) {
142     Canvas* canvas = get_canvas(canvasHandle);
143     canvas->restoreToCount(saveCount);
144 }
145 
getSaveCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)146 static jint getSaveCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
147     return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
148 }
149 
getMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong matrixHandle)150 static void getMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) {
151     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
152     get_canvas(canvasHandle)->getMatrix(matrix);
153 }
154 
setMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong matrixHandle)155 static void setMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) {
156     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
157     get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
158 }
159 
concat(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong matrixHandle)160 static void concat(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) {
161     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
162     get_canvas(canvasHandle)->concat(*matrix);
163 }
164 
concat44(JNIEnv * env,jobject obj,jlong canvasHandle,jfloatArray arr)165 static void concat44(JNIEnv* env, jobject obj, jlong canvasHandle, jfloatArray arr) {
166     jfloat* matVals = env->GetFloatArrayElements(arr, 0);
167     const SkM44 matrix = SkM44::RowMajor(matVals);
168     get_canvas(canvasHandle)->concat(matrix);
169     env->ReleaseFloatArrayElements(arr, matVals, 0);
170 }
171 
rotate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat degrees)172 static void rotate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat degrees) {
173     get_canvas(canvasHandle)->rotate(degrees);
174 }
175 
scale(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat sx,jfloat sy)176 static void scale(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat sx, jfloat sy) {
177     get_canvas(canvasHandle)->scale(sx, sy);
178 }
179 
skew(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat sx,jfloat sy)180 static void skew(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat sx, jfloat sy) {
181     get_canvas(canvasHandle)->skew(sx, sy);
182 }
183 
translate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat dx,jfloat dy)184 static void translate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat dx, jfloat dy) {
185     get_canvas(canvasHandle)->translate(dx, dy);
186 }
187 
getClipBounds(JNIEnv * env,jobject,jlong canvasHandle,jobject bounds)188 static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
189     SkRect   r;
190     SkIRect ir;
191     bool result = get_canvas(canvasHandle)->getClipBounds(&r);
192 
193     if (!result) {
194         r.setEmpty();
195     }
196     r.round(&ir);
197 
198     (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
199     return result ? JNI_TRUE : JNI_FALSE;
200 }
201 
quickRejectRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom)202 static jboolean quickRejectRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,
203                                 jfloat left, jfloat top, jfloat right, jfloat bottom) {
204     bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
205     return result ? JNI_TRUE : JNI_FALSE;
206 }
207 
quickRejectPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong pathHandle)208 static jboolean quickRejectPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle) {
209     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
210     bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
211     return result ? JNI_TRUE : JNI_FALSE;
212 }
213 
214 // SkClipOp is a strict subset of SkRegion::Op and is castable back and forth for their
215 // shared operations (intersect and difference).
216 static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kDifference), "");
217 static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(SkClipOp::kIntersect), "");
218 
clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat l,jfloat t,jfloat r,jfloat b,jint opHandle)219 static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
220                          jfloat r, jfloat b, jint opHandle) {
221     // The opHandle is defined in Canvas.java to be Region::Op
222     SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
223     bool nonEmptyClip;
224     switch (rgnOp) {
225         case SkRegion::Op::kIntersect_Op:
226         case SkRegion::Op::kDifference_Op:
227             // Intersect and difference are supported clip operations
228             nonEmptyClip =
229                     get_canvas(canvasHandle)->clipRect(l, t, r, b, static_cast<SkClipOp>(rgnOp));
230             break;
231         case SkRegion::Op::kReplace_Op:
232             // Replace is emulated to support legacy apps older than P
233             nonEmptyClip = get_canvas(canvasHandle)->replaceClipRect_deprecated(l, t, r, b);
234             break;
235         default:
236             // All other operations would expand the clip and are no longer supported,
237             // so log and skip (to avoid breaking legacy apps).
238             ALOGW("Ignoring unsupported clip operation %d", opHandle);
239             SkRect clipBounds;  // ignored
240             nonEmptyClip = get_canvas(canvasHandle)->getClipBounds(&clipBounds);
241             break;
242     }
243     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
244 }
245 
clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong pathHandle,jint opHandle)246 static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle,
247                          jint opHandle) {
248     SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
249     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
250     bool nonEmptyClip;
251     switch (rgnOp) {
252         case SkRegion::Op::kIntersect_Op:
253         case SkRegion::Op::kDifference_Op:
254             nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, static_cast<SkClipOp>(rgnOp));
255             break;
256         case SkRegion::Op::kReplace_Op:
257             nonEmptyClip = get_canvas(canvasHandle)->replaceClipPath_deprecated(path);
258             break;
259         default:
260             ALOGW("Ignoring unsupported clip operation %d", opHandle);
261             SkRect clipBounds;  // ignored
262             nonEmptyClip = get_canvas(canvasHandle)->getClipBounds(&clipBounds);
263             break;
264     }
265     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
266 }
267 
clipShader(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong shaderHandle,jint opHandle)268 static void clipShader(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong shaderHandle,
269                        jint opHandle) {
270     SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
271     sk_sp<SkShader> shader = sk_ref_sp(reinterpret_cast<SkShader*>(shaderHandle));
272     switch (rgnOp) {
273         case SkRegion::Op::kIntersect_Op:
274         case SkRegion::Op::kDifference_Op:
275             get_canvas(canvasHandle)->clipShader(shader, static_cast<SkClipOp>(rgnOp));
276             break;
277         default:
278             ALOGW("Ignoring unsupported clip operation %d", opHandle);
279             SkRect clipBounds;  // ignored
280             get_canvas(canvasHandle)->getClipBounds(&clipBounds);
281             break;
282     }
283 }
284 
drawColor(JNIEnv * env,jobject,jlong canvasHandle,jint color,jint modeHandle)285 static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
286     SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
287     get_canvas(canvasHandle)->drawColor(color, mode);
288 }
289 
drawColorLong(JNIEnv * env,jobject,jlong canvasHandle,jlong colorSpaceHandle,jlong colorLong,jint modeHandle)290 static void drawColorLong(JNIEnv* env, jobject, jlong canvasHandle, jlong colorSpaceHandle,
291         jlong colorLong, jint modeHandle) {
292     SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
293     sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
294     Paint p;
295     p.setColor4f(color, cs.get());
296 
297     SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
298     p.setBlendMode(mode);
299     get_canvas(canvasHandle)->drawPaint(p);
300 }
301 
drawPaint(JNIEnv * env,jobject,jlong canvasHandle,jlong paintHandle)302 static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
303     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
304     get_canvas(canvasHandle)->drawPaint(*paint);
305 }
306 
drawPoint(JNIEnv *,jobject,jlong canvasHandle,jfloat x,jfloat y,jlong paintHandle)307 static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
308                       jlong paintHandle) {
309     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
310     get_canvas(canvasHandle)->drawPoint(x, y, *paint);
311 }
312 
drawPoints(JNIEnv * env,jobject,jlong canvasHandle,jfloatArray jptsArray,jint offset,jint count,jlong paintHandle)313 static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
314                        jint offset, jint count, jlong paintHandle) {
315     NPE_CHECK_RETURN_VOID(env, jptsArray);
316     AutoJavaFloatArray autoPts(env, jptsArray);
317     float* floats = autoPts.ptr();
318     const int length = autoPts.length();
319 
320     if ((offset | count) < 0 || offset + count > length) {
321         doThrowAIOOBE(env);
322         return;
323     }
324 
325     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
326     get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
327 }
328 
drawLine(JNIEnv * env,jobject,jlong canvasHandle,jfloat startX,jfloat startY,jfloat stopX,jfloat stopY,jlong paintHandle)329 static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
330                      jfloat stopX, jfloat stopY, jlong paintHandle) {
331     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
332     get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
333 }
334 
drawLines(JNIEnv * env,jobject,jlong canvasHandle,jfloatArray jptsArray,jint offset,jint count,jlong paintHandle)335 static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
336                       jint offset, jint count, jlong paintHandle) {
337     NPE_CHECK_RETURN_VOID(env, jptsArray);
338     AutoJavaFloatArray autoPts(env, jptsArray);
339     float* floats = autoPts.ptr();
340     const int length = autoPts.length();
341 
342     if ((offset | count) < 0 || offset + count > length) {
343         doThrowAIOOBE(env);
344         return;
345     }
346 
347     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
348     get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
349 }
350 
drawRect(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jlong paintHandle)351 static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
352                      jfloat right, jfloat bottom, jlong paintHandle) {
353     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
354     get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
355 }
356 
drawDoubleRoundRectXY(JNIEnv * env,jobject,jlong canvasHandle,jfloat outerLeft,jfloat outerTop,jfloat outerRight,jfloat outerBottom,jfloat outerRx,jfloat outerRy,jfloat innerLeft,jfloat innerTop,jfloat innerRight,jfloat innerBottom,jfloat innerRx,jfloat innerRy,jlong paintHandle)357 static void drawDoubleRoundRectXY(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
358                     jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloat outerRx,
359                     jfloat outerRy, jfloat innerLeft, jfloat innerTop, jfloat innerRight,
360                     jfloat innerBottom, jfloat innerRx, jfloat innerRy, jlong paintHandle) {
361     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
362     get_canvas(canvasHandle)->drawDoubleRoundRectXY(
363                     outerLeft, outerTop, outerRight, outerBottom, outerRx, outerRy,
364                     innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy, *paint);
365 }
366 
drawDoubleRoundRectRadii(JNIEnv * env,jobject,jlong canvasHandle,jfloat outerLeft,jfloat outerTop,jfloat outerRight,jfloat outerBottom,jfloatArray jouterRadii,jfloat innerLeft,jfloat innerTop,jfloat innerRight,jfloat innerBottom,jfloatArray jinnerRadii,jlong paintHandle)367 static void drawDoubleRoundRectRadii(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
368                      jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloatArray jouterRadii,
369                      jfloat innerLeft, jfloat innerTop, jfloat innerRight,
370                      jfloat innerBottom, jfloatArray jinnerRadii, jlong paintHandle) {
371     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
372 
373     float outerRadii[8];
374     float innerRadii[8];
375     env->GetFloatArrayRegion(jouterRadii, 0, 8, outerRadii);
376     env->GetFloatArrayRegion(jinnerRadii, 0, 8, innerRadii);
377     get_canvas(canvasHandle)->drawDoubleRoundRectRadii(
378                     outerLeft, outerTop, outerRight, outerBottom, outerRadii,
379                     innerLeft, innerTop, innerRight, innerBottom, innerRadii, *paint);
380 
381 }
382 
drawRegion(JNIEnv * env,jobject,jlong canvasHandle,jlong regionHandle,jlong paintHandle)383 static void drawRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong regionHandle,
384                        jlong paintHandle) {
385     const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
386     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
387     get_canvas(canvasHandle)->drawRegion(*region, *paint);
388 }
389 
drawRoundRect(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloat rx,jfloat ry,jlong paintHandle)390 static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
391                           jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
392     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
393     get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
394 }
395 
drawCircle(JNIEnv * env,jobject,jlong canvasHandle,jfloat cx,jfloat cy,jfloat radius,jlong paintHandle)396 static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
397                        jfloat radius, jlong paintHandle) {
398     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
399     get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
400 }
401 
drawOval(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jlong paintHandle)402 static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
403                      jfloat right, jfloat bottom, jlong paintHandle) {
404     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
405     get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
406 }
407 
drawArc(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloat startAngle,jfloat sweepAngle,jboolean useCenter,jlong paintHandle)408 static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
409                     jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
410                     jboolean useCenter, jlong paintHandle) {
411     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
412     get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
413                                        useCenter, *paint);
414 }
415 
drawPath(JNIEnv * env,jobject,jlong canvasHandle,jlong pathHandle,jlong paintHandle)416 static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
417                      jlong paintHandle) {
418     const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
419     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
420     get_canvas(canvasHandle)->drawPath(*path, *paint);
421 }
422 
drawVertices(JNIEnv * env,jobject,jlong canvasHandle,jint modeHandle,jint floatCount,jfloatArray jverts,jint vertIndex,jfloatArray jtexs,jint texIndex,jintArray jcolors,jint colorIndex,jshortArray jindices,jint indexIndex,jint indexCount,jlong paintHandle)423 static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
424                          jint modeHandle, jint floatCount,
425                          jfloatArray jverts, jint vertIndex,
426                          jfloatArray jtexs, jint texIndex,
427                          jintArray jcolors, jint colorIndex,
428                          jshortArray jindices, jint indexIndex,
429                          jint indexCount, jlong paintHandle) {
430 
431     const int vertexCount = floatCount >> 1;  // 2 floats per SkPoint
432 
433     AutoJavaFloatArray  vertA(env, jverts, vertIndex + floatCount);
434     AutoJavaFloatArray  texA(env, jtexs, texIndex + floatCount);
435     AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
436     AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
437 
438     const float* verts = vertA.ptr() + vertIndex;
439     const float* texs = texA.ptr() + vertIndex;
440     const int* colors = NULL;
441     const uint16_t* indices = NULL;
442 
443     if (jcolors != NULL) {
444         colors = colorA.ptr() + colorIndex;
445     }
446     if (jindices != NULL) {
447         indices = (const uint16_t*)(indexA.ptr() + indexIndex);
448     }
449 
450     SkVertices::VertexMode vertexMode = static_cast<SkVertices::VertexMode>(modeHandle);
451     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
452 
453     // Preserve legacy Skia behavior: ignore the shader if there are no texs set.
454     Paint noShaderPaint;
455     if (jtexs == NULL) {
456         noShaderPaint = Paint(*paint);
457         noShaderPaint.setShader(nullptr);
458         paint = &noShaderPaint;
459     }
460     // Since https://skia-review.googlesource.com/c/skia/+/473676, Skia will blend paint and vertex
461     // colors when no shader is provided. This ternary uses kDst to mimic the old behavior of
462     // ignoring the paint and using the vertex colors directly when no shader is provided.
463     SkBlendMode blendMode = paint->getShader() ? SkBlendMode::kModulate : SkBlendMode::kDst;
464 
465     get_canvas(canvasHandle)
466             ->drawVertices(SkVertices::MakeCopy(
467                                    vertexMode, vertexCount, reinterpret_cast<const SkPoint*>(verts),
468                                    reinterpret_cast<const SkPoint*>(texs),
469                                    reinterpret_cast<const SkColor*>(colors), indexCount, indices)
470                                    .get(),
471                            blendMode, *paint);
472 }
473 
drawMesh(JNIEnv * env,jobject,jlong canvasHandle,jlong meshHandle,jint modeHandle,jlong paintHandle)474 static void drawMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong meshHandle, jint modeHandle,
475                      jlong paintHandle) {
476     const Mesh* mesh = reinterpret_cast<Mesh*>(meshHandle);
477     SkBlendMode blendMode = static_cast<SkBlendMode>(modeHandle);
478     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
479     get_canvas(canvasHandle)->drawMesh(*mesh, SkBlender::Mode(blendMode), *paint);
480 }
481 
drawNinePatch(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle,jlong chunkHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jlong paintHandle,jint dstDensity,jint srcDensity)482 static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
483         jlong chunkHandle, jfloat left, jfloat top, jfloat right, jfloat bottom,
484         jlong paintHandle, jint dstDensity, jint srcDensity) {
485 
486     Canvas* canvas = get_canvas(canvasHandle);
487     Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
488     const android::Res_png_9patch* chunk = reinterpret_cast<android::Res_png_9patch*>(chunkHandle);
489     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
490 
491     if (CC_LIKELY(dstDensity == srcDensity || dstDensity == 0 || srcDensity == 0)) {
492         canvas->drawNinePatch(bitmap, *chunk, left, top, right, bottom, paint);
493     } else {
494         canvas->save(SaveFlags::MatrixClip);
495 
496         SkScalar scale = dstDensity / (float)srcDensity;
497         canvas->translate(left, top);
498         canvas->scale(scale, scale);
499 
500         Paint filteredPaint;
501         if (paint) {
502             filteredPaint = *paint;
503         }
504         filteredPaint.setFilterBitmap(true);
505 
506         canvas->drawNinePatch(bitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale,
507                 &filteredPaint);
508 
509         canvas->restore();
510     }
511 }
512 
drawBitmap(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle,jfloat left,jfloat top,jlong paintHandle,jint canvasDensity,jint screenDensity,jint bitmapDensity)513 static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
514                        jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
515                        jint screenDensity, jint bitmapDensity) {
516     Canvas* canvas = get_canvas(canvasHandle);
517     Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
518     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
519 
520     if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
521         if (screenDensity != 0 && screenDensity != bitmapDensity) {
522             Paint filteredPaint;
523             if (paint) {
524                 filteredPaint = *paint;
525             }
526             filteredPaint.setFilterBitmap(true);
527             canvas->drawBitmap(bitmap, left, top, &filteredPaint);
528         } else {
529             canvas->drawBitmap(bitmap, left, top, paint);
530         }
531     } else {
532         canvas->save(SaveFlags::MatrixClip);
533         SkScalar scale = canvasDensity / (float)bitmapDensity;
534         canvas->translate(left, top);
535         canvas->scale(scale, scale);
536 
537         Paint filteredPaint;
538         if (paint) {
539             filteredPaint = *paint;
540         }
541         filteredPaint.setFilterBitmap(true);
542 
543         canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
544         canvas->restore();
545     }
546 }
547 
drawBitmapMatrix(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle,jlong matrixHandle,jlong paintHandle)548 static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
549                              jlong matrixHandle, jlong paintHandle) {
550     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
551     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
552     Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
553     get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint);
554 }
555 
drawBitmapRect(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle,float srcLeft,float srcTop,float srcRight,float srcBottom,float dstLeft,float dstTop,float dstRight,float dstBottom,jlong paintHandle,jint screenDensity,jint bitmapDensity)556 static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
557                            float srcLeft, float srcTop, float srcRight, float srcBottom,
558                            float dstLeft, float dstTop, float dstRight, float dstBottom,
559                            jlong paintHandle, jint screenDensity, jint bitmapDensity) {
560     Canvas* canvas = get_canvas(canvasHandle);
561     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
562 
563     Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
564     if (screenDensity != 0 && screenDensity != bitmapDensity) {
565         Paint filteredPaint;
566         if (paint) {
567             filteredPaint = *paint;
568         }
569         filteredPaint.setFilterBitmap(true);
570         canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
571                            dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
572     } else {
573         canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
574                            dstLeft, dstTop, dstRight, dstBottom, paint);
575     }
576 }
577 
drawBitmapArray(JNIEnv * env,jobject,jlong canvasHandle,jintArray jcolors,jint offset,jint stride,jfloat x,jfloat y,jint width,jint height,jboolean hasAlpha,jlong paintHandle)578 static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
579                             jintArray jcolors, jint offset, jint stride,
580                             jfloat x, jfloat y, jint width, jint height,
581                             jboolean hasAlpha, jlong paintHandle) {
582     // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
583     // correct the alphaType to kOpaque_SkAlphaType.
584     SkImageInfo info = SkImageInfo::Make(width, height,
585                            hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
586                            kPremul_SkAlphaType);
587     SkBitmap bitmap;
588     bitmap.setInfo(info);
589     sk_sp<Bitmap> androidBitmap = Bitmap::allocateHeapBitmap(&bitmap);
590     if (!androidBitmap) {
591         return;
592     }
593 
594     if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, &bitmap)) {
595         return;
596     }
597 
598     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
599     get_canvas(canvasHandle)->drawBitmap(*androidBitmap, x, y, paint);
600 }
601 
drawBitmapMesh(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle,jint meshWidth,jint meshHeight,jfloatArray jverts,jint vertIndex,jintArray jcolors,jint colorIndex,jlong paintHandle)602 static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
603                            jint meshWidth, jint meshHeight, jfloatArray jverts,
604                            jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
605     if (Canvas::GetApiLevel() < __ANDROID_API_P__) {
606         // Before P we forgot to respect these. Now that we do respect them, explicitly
607         // zero them for backward compatibility.
608         vertIndex = 0;
609         colorIndex = 0;
610     }
611 
612     const int ptCount = (meshWidth + 1) * (meshHeight + 1);
613     AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
614     AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
615 
616     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
617     Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
618     get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
619                                              vertA.ptr() + vertIndex*2,
620                                              colorA.ptr() + colorIndex, paint);
621 }
622 
drawGlyphs(JNIEnv * env,jobject,jlong canvasHandle,jintArray glyphIds,jfloatArray positions,jint glyphOffset,jint positionOffset,jint glyphCount,jlong fontHandle,jlong paintHandle)623 static void drawGlyphs(JNIEnv* env, jobject, jlong canvasHandle, jintArray glyphIds,
624                        jfloatArray positions, jint glyphOffset, jint positionOffset,
625                        jint glyphCount, jlong fontHandle, jlong paintHandle) {
626     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
627     FontWrapper* font = reinterpret_cast<FontWrapper*>(fontHandle);
628     AutoJavaIntArray glyphIdArray(env, glyphIds);
629     AutoJavaFloatArray positionArray(env, positions);
630     get_canvas(canvasHandle)->drawGlyphs(
631         *font->font.get(),
632         glyphIdArray.ptr() + glyphOffset,
633         positionArray.ptr() + positionOffset,
634         glyphCount,
635         *paint);
636 }
637 
drawTextChars(JNIEnv * env,jobject,jlong canvasHandle,jcharArray charArray,jint index,jint count,jfloat x,jfloat y,jint bidiFlags,jlong paintHandle)638 static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray,
639                           jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
640                           jlong paintHandle) {
641     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
642     const Typeface* typeface = paint->getAndroidTypeface();
643     ScopedCharArrayRO text(env, charArray);
644 
645     // The drawText API is designed to draw entire line, so ignore the text run flag and draw the
646     // text as entire line mode.
647     const minikin::RunFlag originalRunFlag = paint->getRunFlag();
648     paint->setRunFlag(minikin::RunFlag::WHOLE_LINE);
649 
650     // drawTextString and drawTextChars doesn't use context info
651     get_canvas(canvasHandle)->drawText(
652             text.get() + index, count,  // text buffer
653             0, count,  // draw range
654             0, count,  // context range
655             x, y,  // draw position
656             static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */);
657     paint->setRunFlag(originalRunFlag);
658 }
659 
drawTextString(JNIEnv * env,jobject,jlong canvasHandle,jstring strObj,jint start,jint end,jfloat x,jfloat y,jint bidiFlags,jlong paintHandle)660 static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring strObj,
661                            jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
662                            jlong paintHandle) {
663     ScopedStringChars text(env, strObj);
664     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
665     const Typeface* typeface = paint->getAndroidTypeface();
666     const int count = end - start;
667 
668     // The drawText API is designed to draw entire line, so ignore the text run flag and draw the
669     // text as entire line mode.
670     const minikin::RunFlag originalRunFlag = paint->getRunFlag();
671     paint->setRunFlag(minikin::RunFlag::WHOLE_LINE);
672 
673     // drawTextString and drawTextChars doesn't use context info
674     get_canvas(canvasHandle)->drawText(
675             text.get() + start, count,  // text buffer
676             0, count,  // draw range
677             0, count,  // context range
678             x, y,  // draw position
679             static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */);
680     paint->setRunFlag(originalRunFlag);
681 }
682 
drawTextRunChars(JNIEnv * env,jobject,jlong canvasHandle,jcharArray charArray,jint index,jint count,jint contextIndex,jint contextCount,jfloat x,jfloat y,jboolean isRtl,jlong paintHandle,jlong mtHandle)683 static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray,
684                              jint index, jint count, jint contextIndex, jint contextCount,
685                              jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
686                              jlong mtHandle) {
687     minikin::MeasuredText* mt = reinterpret_cast<minikin::MeasuredText*>(mtHandle);
688     const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
689 
690     ScopedCharArrayRO text(env, charArray);
691     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
692     const Typeface* typeface = paint->getAndroidTypeface();
693     get_canvas(canvasHandle)->drawText(
694             text.get(), text.size(),  // text buffer
695             index, count,  // draw range
696             contextIndex, contextCount,  // context range,
697             x, y,  // draw position
698             bidiFlags, *paint, typeface, mt);
699 }
700 
drawTextRunString(JNIEnv * env,jobject obj,jlong canvasHandle,jstring strObj,jint start,jint end,jint contextStart,jint contextEnd,jfloat x,jfloat y,jboolean isRtl,jlong paintHandle)701 static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring strObj,
702                               jint start, jint end, jint contextStart, jint contextEnd,
703                               jfloat x, jfloat y, jboolean isRtl, jlong paintHandle) {
704     const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
705 
706     ScopedStringChars text(env, strObj);
707     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
708     const Typeface* typeface = paint->getAndroidTypeface();
709     get_canvas(canvasHandle)->drawText(
710             text.get(), text.size(),  // text buffer
711             start, end - start,  // draw range
712             contextStart, contextEnd - contextStart,  // context range
713             x, y,  // draw position
714             bidiFlags, *paint, typeface, nullptr /* measured text */);
715 }
716 
drawTextOnPathChars(JNIEnv * env,jobject,jlong canvasHandle,jcharArray text,jint index,jint count,jlong pathHandle,jfloat hOffset,jfloat vOffset,jint bidiFlags,jlong paintHandle)717 static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
718                                 jint index, jint count, jlong pathHandle, jfloat hOffset,
719                                 jfloat vOffset, jint bidiFlags, jlong paintHandle) {
720     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
721     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
722     const Typeface* typeface = paint->getAndroidTypeface();
723 
724     jchar* jchars = env->GetCharArrayElements(text, NULL);
725 
726     // The drawText API is designed to draw entire line, so ignore the text run flag and draw the
727     // text as entire line mode.
728     const minikin::RunFlag originalRunFlag = paint->getRunFlag();
729     paint->setRunFlag(minikin::RunFlag::WHOLE_LINE);
730 
731     get_canvas(canvasHandle)->drawTextOnPath(jchars + index, count,
732             static_cast<minikin::Bidi>(bidiFlags), *path, hOffset, vOffset, *paint, typeface);
733 
734     paint->setRunFlag(originalRunFlag);
735     env->ReleaseCharArrayElements(text, jchars, 0);
736 }
737 
drawTextOnPathString(JNIEnv * env,jobject,jlong canvasHandle,jstring text,jlong pathHandle,jfloat hOffset,jfloat vOffset,jint bidiFlags,jlong paintHandle)738 static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
739                                  jlong pathHandle, jfloat hOffset, jfloat vOffset,
740                                  jint bidiFlags, jlong paintHandle) {
741     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
742     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
743     const Typeface* typeface = paint->getAndroidTypeface();
744 
745     const jchar* jchars = env->GetStringChars(text, NULL);
746     int count = env->GetStringLength(text);
747 
748     // The drawText API is designed to draw entire line, so ignore the text run flag and draw the
749     // text as entire line mode.
750     const minikin::RunFlag originalRunFlag = paint->getRunFlag();
751     paint->setRunFlag(minikin::RunFlag::WHOLE_LINE);
752 
753     get_canvas(canvasHandle)->drawTextOnPath(jchars, count, static_cast<minikin::Bidi>(bidiFlags),
754             *path, hOffset, vOffset, *paint, typeface);
755 
756     paint->setRunFlag(originalRunFlag);
757     env->ReleaseStringChars(text, jchars);
758 }
759 
setPaintFilter(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong filterHandle)760 static void setPaintFilter(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong filterHandle) {
761     PaintFilter* paintFilter = reinterpret_cast<PaintFilter*>(filterHandle);
762     get_canvas(canvasHandle)->setPaintFilter(sk_ref_sp(paintFilter));
763 }
764 
freeCaches(JNIEnv * env,jobject)765 static void freeCaches(JNIEnv* env, jobject) {
766     SkGraphics::PurgeFontCache();
767 }
768 
freeTextLayoutCaches(JNIEnv * env,jobject)769 static void freeTextLayoutCaches(JNIEnv* env, jobject) {
770     minikin::Layout::purgeCaches();
771 }
772 
setCompatibilityVersion(JNIEnv * env,jobject,jint apiLevel)773 static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) {
774     Canvas::setCompatibilityVersion(apiLevel);
775 }
776 
punchHole(JNIEnv * env,jobject,jlong canvasPtr,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloat rx,jfloat ry,jfloat alpha)777 static void punchHole(JNIEnv* env, jobject, jlong canvasPtr, jfloat left, jfloat top, jfloat right,
778         jfloat bottom, jfloat rx, jfloat ry, jfloat alpha) {
779     auto canvas = reinterpret_cast<Canvas*>(canvasPtr);
780     canvas->punchHole(SkRRect::MakeRectXY(SkRect::MakeLTRB(left, top, right, bottom), rx, ry),
781                       alpha);
782 }
783 
784 }; // namespace CanvasJNI
785 
786 static const JNINativeMethod gMethods[] = {
787         {"nGetNativeFinalizer", "()J", (void*)CanvasJNI::getNativeFinalizer},
788         {"nFreeCaches", "()V", (void*)CanvasJNI::freeCaches},
789         {"nFreeTextLayoutCaches", "()V", (void*)CanvasJNI::freeTextLayoutCaches},
790         {"nSetCompatibilityVersion", "(I)V", (void*)CanvasJNI::setCompatibilityVersion},
791 
792         // ------------ @FastNative ----------------
793         {"nInitRaster", "(J)J", (void*)CanvasJNI::initRaster},
794         {"nSetBitmap", "(JJ)V", (void*)CanvasJNI::setBitmap},
795         {"nGetClipBounds", "(JLandroid/graphics/Rect;)Z", (void*)CanvasJNI::getClipBounds},
796 
797         // ------------ @CriticalNative ----------------
798         {"nIsOpaque", "(J)Z", (void*)CanvasJNI::isOpaque},
799         {"nIsHighContrastText", "(J)Z", (void*)CanvasJNI::isHighContrastText},
800         {"nGetWidth", "(J)I", (void*)CanvasJNI::getWidth},
801         {"nGetHeight", "(J)I", (void*)CanvasJNI::getHeight},
802         {"nSave", "(JI)I", (void*)CanvasJNI::save},
803         {"nSaveLayer", "(JFFFFJ)I", (void*)CanvasJNI::saveLayer},
804         {"nSaveLayerAlpha", "(JFFFFI)I", (void*)CanvasJNI::saveLayerAlpha},
805         {"nSaveUnclippedLayer", "(JIIII)I", (void*)CanvasJNI::saveUnclippedLayer},
806         {"nRestoreUnclippedLayer", "(JIJ)V", (void*)CanvasJNI::restoreUnclippedLayer},
807         {"nGetSaveCount", "(J)I", (void*)CanvasJNI::getSaveCount},
808         {"nRestore", "(J)Z", (void*)CanvasJNI::restore},
809         {"nRestoreToCount", "(JI)V", (void*)CanvasJNI::restoreToCount},
810         {"nGetMatrix", "(JJ)V", (void*)CanvasJNI::getMatrix},
811         {"nSetMatrix", "(JJ)V", (void*)CanvasJNI::setMatrix},
812         {"nConcat", "(JJ)V", (void*)CanvasJNI::concat},
813         {"nConcat", "(J[F)V", (void*)CanvasJNI::concat44},
814         {"nRotate", "(JF)V", (void*)CanvasJNI::rotate},
815         {"nScale", "(JFF)V", (void*)CanvasJNI::scale},
816         {"nSkew", "(JFF)V", (void*)CanvasJNI::skew},
817         {"nTranslate", "(JFF)V", (void*)CanvasJNI::translate},
818         {"nQuickReject", "(JJ)Z", (void*)CanvasJNI::quickRejectPath},
819         {"nQuickReject", "(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
820         {"nClipRect", "(JFFFFI)Z", (void*)CanvasJNI::clipRect},
821         {"nClipPath", "(JJI)Z", (void*)CanvasJNI::clipPath},
822         {"nClipShader", "(JJI)V", (void*)CanvasJNI::clipShader},
823         {"nSetDrawFilter", "(JJ)V", (void*)CanvasJNI::setPaintFilter},
824 };
825 
826 // If called from Canvas these are regular JNI
827 // If called from DisplayListCanvas they are @FastNative
828 static const JNINativeMethod gDrawMethods[] = {
829         {"nDrawColor", "(JII)V", (void*)CanvasJNI::drawColor},
830         {"nDrawColor", "(JJJI)V", (void*)CanvasJNI::drawColorLong},
831         {"nDrawPaint", "(JJ)V", (void*)CanvasJNI::drawPaint},
832         {"nDrawPoint", "(JFFJ)V", (void*)CanvasJNI::drawPoint},
833         {"nDrawPoints", "(J[FIIJ)V", (void*)CanvasJNI::drawPoints},
834         {"nDrawLine", "(JFFFFJ)V", (void*)CanvasJNI::drawLine},
835         {"nDrawLines", "(J[FIIJ)V", (void*)CanvasJNI::drawLines},
836         {"nDrawRect", "(JFFFFJ)V", (void*)CanvasJNI::drawRect},
837         {"nDrawRegion", "(JJJ)V", (void*)CanvasJNI::drawRegion},
838         {"nDrawRoundRect", "(JFFFFFFJ)V", (void*)CanvasJNI::drawRoundRect},
839         {"nDrawDoubleRoundRect", "(JFFFFFFFFFFFFJ)V", (void*)CanvasJNI::drawDoubleRoundRectXY},
840         {"nDrawDoubleRoundRect", "(JFFFF[FFFFF[FJ)V", (void*)CanvasJNI::drawDoubleRoundRectRadii},
841         {"nDrawCircle", "(JFFFJ)V", (void*)CanvasJNI::drawCircle},
842         {"nDrawOval", "(JFFFFJ)V", (void*)CanvasJNI::drawOval},
843         {"nDrawArc", "(JFFFFFFZJ)V", (void*)CanvasJNI::drawArc},
844         {"nDrawPath", "(JJJ)V", (void*)CanvasJNI::drawPath},
845         {"nDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
846         {"nDrawMesh", "(JJIJ)V", (void*)CanvasJNI::drawMesh},
847         {"nDrawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
848         {"nDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
849         {"nDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
850         {"nDrawBitmap", "(JJFFJIII)V", (void*)CanvasJNI::drawBitmap},
851         {"nDrawBitmap", "(JJFFFFFFFFJII)V", (void*)CanvasJNI::drawBitmapRect},
852         {"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
853         {"nDrawGlyphs", "(J[I[FIIIJJ)V", (void*)CanvasJNI::drawGlyphs},
854         {"nDrawText", "(J[CIIFFIJ)V", (void*)CanvasJNI::drawTextChars},
855         {"nDrawText", "(JLjava/lang/String;IIFFIJ)V", (void*)CanvasJNI::drawTextString},
856         {"nDrawTextRun", "(J[CIIIIFFZJJ)V", (void*)CanvasJNI::drawTextRunChars},
857         {"nDrawTextRun", "(JLjava/lang/String;IIIIFFZJ)V", (void*)CanvasJNI::drawTextRunString},
858         {"nDrawTextOnPath", "(J[CIIJFFIJ)V", (void*)CanvasJNI::drawTextOnPathChars},
859         {"nDrawTextOnPath", "(JLjava/lang/String;JFFIJ)V", (void*)CanvasJNI::drawTextOnPathString},
860         {"nPunchHole", "(JFFFFFFF)V", (void*)CanvasJNI::punchHole}};
861 
register_android_graphics_Canvas(JNIEnv * env)862 int register_android_graphics_Canvas(JNIEnv* env) {
863     int ret = 0;
864     ret |= RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
865     ret |= RegisterMethodsOrDie(env, "android/graphics/BaseCanvas", gDrawMethods, NELEM(gDrawMethods));
866     ret |= RegisterMethodsOrDie(env, "android/graphics/BaseRecordingCanvas", gDrawMethods, NELEM(gDrawMethods));
867     return ret;
868 
869 }
870 
871 }; // namespace android
872