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 "SkGraphics.h"
35 #include "SkRegion.h"
36 #include "SkVertices.h"
37 
38 namespace minikin {
39 class MeasuredText;
40 }  // namespace minikin
41 
42 namespace android {
43 
44 namespace CanvasJNI {
45 
get_canvas(jlong canvasHandle)46 static Canvas* get_canvas(jlong canvasHandle) {
47     return reinterpret_cast<Canvas*>(canvasHandle);
48 }
49 
delete_canvas(Canvas * canvas)50 static void delete_canvas(Canvas* canvas) {
51     delete canvas;
52 }
53 
getNativeFinalizer(JNIEnv * env,jobject clazz)54 static jlong getNativeFinalizer(JNIEnv* env, jobject clazz) {
55     return static_cast<jlong>(reinterpret_cast<uintptr_t>(&delete_canvas));
56 }
57 
58 // Native wrapper constructor used by Canvas(Bitmap)
initRaster(JNIEnv * env,jobject,jlong bitmapHandle)59 static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
60     SkBitmap bitmap;
61     if (bitmapHandle != 0) {
62         bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap);
63     }
64     return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
65 }
66 
67 // Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
68 // optionally copying canvas matrix & clip state.
setBitmap(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle)69 static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle) {
70     SkBitmap bitmap;
71     if (bitmapHandle != 0) {
72         bitmap::toBitmap(bitmapHandle).getSkBitmap(&bitmap);
73     }
74     get_canvas(canvasHandle)->setBitmap(bitmap);
75 }
76 
isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)77 static jboolean isOpaque(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
78     return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
79 }
80 
getWidth(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)81 static jint getWidth(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
82     return static_cast<jint>(get_canvas(canvasHandle)->width());
83 }
84 
getHeight(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)85 static jint getHeight(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
86     return static_cast<jint>(get_canvas(canvasHandle)->height());
87 }
88 
save(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jint flagsHandle)89 static jint save(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint flagsHandle) {
90     SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
91     return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
92 }
93 
saveLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat l,jfloat t,jfloat r,jfloat b,jlong paintHandle,jint flagsHandle)94 static jint saveLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
95                       jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
96     Paint* paint  = reinterpret_cast<Paint*>(paintHandle);
97     SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
98     return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
99 }
100 
saveLayerAlpha(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat l,jfloat t,jfloat r,jfloat b,jint alpha,jint flagsHandle)101 static jint saveLayerAlpha(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
102                            jfloat r, jfloat b, jint alpha, jint flagsHandle) {
103     SaveFlags::Flags flags = static_cast<SaveFlags::Flags>(flagsHandle);
104     return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
105 }
106 
saveUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jint l,jint t,jint r,jint b)107 static jint saveUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint l, jint t, jint r, jint b) {
108     return reinterpret_cast<jint>(get_canvas(canvasHandle)->saveUnclippedLayer(l, t, r, b));
109 }
110 
restoreUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jint saveCount,jlong paintHandle)111 static void restoreUnclippedLayer(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint saveCount, jlong paintHandle) {
112     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
113     get_canvas(canvasHandle)->restoreUnclippedLayer(saveCount, *paint);
114 }
115 
restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)116 static jboolean restore(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
117     Canvas* canvas = get_canvas(canvasHandle);
118     if (canvas->getSaveCount() <= 1) {
119         return false; // cannot restore anymore
120     }
121     canvas->restore();
122     return true; // success
123 }
124 
restoreToCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jint saveCount)125 static void restoreToCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jint saveCount) {
126     Canvas* canvas = get_canvas(canvasHandle);
127     canvas->restoreToCount(saveCount);
128 }
129 
getSaveCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle)130 static jint getSaveCount(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle) {
131     return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
132 }
133 
getMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong matrixHandle)134 static void getMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) {
135     SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
136     get_canvas(canvasHandle)->getMatrix(matrix);
137 }
138 
setMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong matrixHandle)139 static void setMatrix(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) {
140     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
141     get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
142 }
143 
concat(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong matrixHandle)144 static void concat(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong matrixHandle) {
145     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
146     get_canvas(canvasHandle)->concat(*matrix);
147 }
148 
rotate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat degrees)149 static void rotate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat degrees) {
150     get_canvas(canvasHandle)->rotate(degrees);
151 }
152 
scale(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat sx,jfloat sy)153 static void scale(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat sx, jfloat sy) {
154     get_canvas(canvasHandle)->scale(sx, sy);
155 }
156 
skew(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat sx,jfloat sy)157 static void skew(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat sx, jfloat sy) {
158     get_canvas(canvasHandle)->skew(sx, sy);
159 }
160 
translate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat dx,jfloat dy)161 static void translate(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat dx, jfloat dy) {
162     get_canvas(canvasHandle)->translate(dx, dy);
163 }
164 
getClipBounds(JNIEnv * env,jobject,jlong canvasHandle,jobject bounds)165 static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
166     SkRect   r;
167     SkIRect ir;
168     bool result = get_canvas(canvasHandle)->getClipBounds(&r);
169 
170     if (!result) {
171         r.setEmpty();
172     }
173     r.round(&ir);
174 
175     (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
176     return result ? JNI_TRUE : JNI_FALSE;
177 }
178 
quickRejectRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom)179 static jboolean quickRejectRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,
180                                 jfloat left, jfloat top, jfloat right, jfloat bottom) {
181     bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
182     return result ? JNI_TRUE : JNI_FALSE;
183 }
184 
quickRejectPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong pathHandle)185 static jboolean quickRejectPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle) {
186     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
187     bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
188     return result ? JNI_TRUE : JNI_FALSE;
189 }
190 
191 // SkRegion::Op and SkClipOp are numerically identical, so we can freely cast
192 // from one to the other (though SkClipOp is destined to become a strict subset)
193 static_assert(SkRegion::kDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kDifference), "");
194 static_assert(SkRegion::kIntersect_Op == static_cast<SkRegion::Op>(SkClipOp::kIntersect), "");
195 static_assert(SkRegion::kUnion_Op == static_cast<SkRegion::Op>(SkClipOp::kUnion_deprecated), "");
196 static_assert(SkRegion::kXOR_Op == static_cast<SkRegion::Op>(SkClipOp::kXOR_deprecated), "");
197 static_assert(SkRegion::kReverseDifference_Op == static_cast<SkRegion::Op>(SkClipOp::kReverseDifference_deprecated), "");
198 static_assert(SkRegion::kReplace_Op == static_cast<SkRegion::Op>(SkClipOp::kReplace_deprecated), "");
199 
opHandleToClipOp(jint opHandle)200 static SkClipOp opHandleToClipOp(jint opHandle) {
201     // The opHandle is defined in Canvas.java to be Region::Op
202     SkRegion::Op rgnOp = static_cast<SkRegion::Op>(opHandle);
203 
204     // In the future, when we no longer support the wide range of ops (e.g. Union, Xor)
205     // this function can perform a range check and throw an unsupported-exception.
206     // e.g. if (rgnOp != kIntersect && rgnOp != kDifference) throw...
207 
208     // Skia now takes a different type, SkClipOp, as the parameter to clipping calls
209     // This type is binary compatible with SkRegion::Op, so a static_cast<> is safe.
210     return static_cast<SkClipOp>(rgnOp);
211 }
212 
clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jfloat l,jfloat t,jfloat r,jfloat b,jint opHandle)213 static jboolean clipRect(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jfloat l, jfloat t,
214                          jfloat r, jfloat b, jint opHandle) {
215     bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b,
216             opHandleToClipOp(opHandle));
217     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
218 }
219 
clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong pathHandle,jint opHandle)220 static jboolean clipPath(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong pathHandle,
221                          jint opHandle) {
222     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
223     bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, opHandleToClipOp(opHandle));
224     return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
225 }
226 
drawColor(JNIEnv * env,jobject,jlong canvasHandle,jint color,jint modeHandle)227 static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
228     SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
229     get_canvas(canvasHandle)->drawColor(color, mode);
230 }
231 
drawColorLong(JNIEnv * env,jobject,jlong canvasHandle,jlong colorSpaceHandle,jlong colorLong,jint modeHandle)232 static void drawColorLong(JNIEnv* env, jobject, jlong canvasHandle, jlong colorSpaceHandle,
233         jlong colorLong, jint modeHandle) {
234     SkColor4f color = GraphicsJNI::convertColorLong(colorLong);
235     sk_sp<SkColorSpace> cs = GraphicsJNI::getNativeColorSpace(colorSpaceHandle);
236     SkPaint p;
237     p.setColor4f(color, cs.get());
238 
239     SkBlendMode mode = static_cast<SkBlendMode>(modeHandle);
240     p.setBlendMode(mode);
241     get_canvas(canvasHandle)->drawPaint(p);
242 }
243 
drawPaint(JNIEnv * env,jobject,jlong canvasHandle,jlong paintHandle)244 static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
245     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
246     get_canvas(canvasHandle)->drawPaint(*paint);
247 }
248 
drawPoint(JNIEnv *,jobject,jlong canvasHandle,jfloat x,jfloat y,jlong paintHandle)249 static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
250                       jlong paintHandle) {
251     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
252     get_canvas(canvasHandle)->drawPoint(x, y, *paint);
253 }
254 
drawPoints(JNIEnv * env,jobject,jlong canvasHandle,jfloatArray jptsArray,jint offset,jint count,jlong paintHandle)255 static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
256                        jint offset, jint count, jlong paintHandle) {
257     NPE_CHECK_RETURN_VOID(env, jptsArray);
258     AutoJavaFloatArray autoPts(env, jptsArray);
259     float* floats = autoPts.ptr();
260     const int length = autoPts.length();
261 
262     if ((offset | count) < 0 || offset + count > length) {
263         doThrowAIOOBE(env);
264         return;
265     }
266 
267     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
268     get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
269 }
270 
drawLine(JNIEnv * env,jobject,jlong canvasHandle,jfloat startX,jfloat startY,jfloat stopX,jfloat stopY,jlong paintHandle)271 static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
272                      jfloat stopX, jfloat stopY, jlong paintHandle) {
273     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
274     get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
275 }
276 
drawLines(JNIEnv * env,jobject,jlong canvasHandle,jfloatArray jptsArray,jint offset,jint count,jlong paintHandle)277 static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
278                       jint offset, jint count, jlong paintHandle) {
279     NPE_CHECK_RETURN_VOID(env, jptsArray);
280     AutoJavaFloatArray autoPts(env, jptsArray);
281     float* floats = autoPts.ptr();
282     const int length = autoPts.length();
283 
284     if ((offset | count) < 0 || offset + count > length) {
285         doThrowAIOOBE(env);
286         return;
287     }
288 
289     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
290     get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
291 }
292 
drawRect(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jlong paintHandle)293 static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
294                      jfloat right, jfloat bottom, jlong paintHandle) {
295     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
296     get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
297 }
298 
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)299 static void drawDoubleRoundRectXY(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
300                     jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloat outerRx,
301                     jfloat outerRy, jfloat innerLeft, jfloat innerTop, jfloat innerRight,
302                     jfloat innerBottom, jfloat innerRx, jfloat innerRy, jlong paintHandle) {
303     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
304     get_canvas(canvasHandle)->drawDoubleRoundRectXY(
305                     outerLeft, outerTop, outerRight, outerBottom, outerRx, outerRy,
306                     innerLeft, innerTop, innerRight, innerBottom, innerRx, innerRy, *paint);
307 }
308 
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)309 static void drawDoubleRoundRectRadii(JNIEnv* env, jobject, jlong canvasHandle, jfloat outerLeft,
310                      jfloat outerTop, jfloat outerRight, jfloat outerBottom, jfloatArray jouterRadii,
311                      jfloat innerLeft, jfloat innerTop, jfloat innerRight,
312                      jfloat innerBottom, jfloatArray jinnerRadii, jlong paintHandle) {
313     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
314 
315     float outerRadii[8];
316     float innerRadii[8];
317     env->GetFloatArrayRegion(jouterRadii, 0, 8, outerRadii);
318     env->GetFloatArrayRegion(jinnerRadii, 0, 8, innerRadii);
319     get_canvas(canvasHandle)->drawDoubleRoundRectRadii(
320                     outerLeft, outerTop, outerRight, outerBottom, outerRadii,
321                     innerLeft, innerTop, innerRight, innerBottom, innerRadii, *paint);
322 
323 }
324 
drawRegion(JNIEnv * env,jobject,jlong canvasHandle,jlong regionHandle,jlong paintHandle)325 static void drawRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong regionHandle,
326                        jlong paintHandle) {
327     const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
328     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
329     get_canvas(canvasHandle)->drawRegion(*region, *paint);
330 }
331 
drawRoundRect(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloat rx,jfloat ry,jlong paintHandle)332 static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
333                           jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
334     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
335     get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
336 }
337 
drawCircle(JNIEnv * env,jobject,jlong canvasHandle,jfloat cx,jfloat cy,jfloat radius,jlong paintHandle)338 static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
339                        jfloat radius, jlong paintHandle) {
340     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
341     get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
342 }
343 
drawOval(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jlong paintHandle)344 static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
345                      jfloat right, jfloat bottom, jlong paintHandle) {
346     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
347     get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
348 }
349 
drawArc(JNIEnv * env,jobject,jlong canvasHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jfloat startAngle,jfloat sweepAngle,jboolean useCenter,jlong paintHandle)350 static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
351                     jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
352                     jboolean useCenter, jlong paintHandle) {
353     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
354     get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
355                                        useCenter, *paint);
356 }
357 
drawPath(JNIEnv * env,jobject,jlong canvasHandle,jlong pathHandle,jlong paintHandle)358 static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
359                      jlong paintHandle) {
360     const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
361     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
362     get_canvas(canvasHandle)->drawPath(*path, *paint);
363 }
364 
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)365 static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
366                          jint modeHandle, jint floatCount,
367                          jfloatArray jverts, jint vertIndex,
368                          jfloatArray jtexs, jint texIndex,
369                          jintArray jcolors, jint colorIndex,
370                          jshortArray jindices, jint indexIndex,
371                          jint indexCount, jlong paintHandle) {
372 
373     const int vertexCount = floatCount >> 1;  // 2 floats per SkPoint
374 
375     AutoJavaFloatArray  vertA(env, jverts, vertIndex + floatCount);
376     AutoJavaFloatArray  texA(env, jtexs, texIndex + floatCount);
377     AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
378     AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
379 
380     const float* verts = vertA.ptr() + vertIndex;
381     const float* texs = texA.ptr() + vertIndex;
382     const int* colors = NULL;
383     const uint16_t* indices = NULL;
384 
385     if (jcolors != NULL) {
386         colors = colorA.ptr() + colorIndex;
387     }
388     if (jindices != NULL) {
389         indices = (const uint16_t*)(indexA.ptr() + indexIndex);
390     }
391 
392     SkVertices::VertexMode mode = static_cast<SkVertices::VertexMode>(modeHandle);
393     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
394     get_canvas(canvasHandle)->drawVertices(SkVertices::MakeCopy(mode, vertexCount,
395                                            reinterpret_cast<const SkPoint*>(verts),
396                                            reinterpret_cast<const SkPoint*>(texs),
397                                            reinterpret_cast<const SkColor*>(colors),
398                                            indexCount, indices).get(),
399                                            SkBlendMode::kModulate, *paint);
400 }
401 
drawNinePatch(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle,jlong chunkHandle,jfloat left,jfloat top,jfloat right,jfloat bottom,jlong paintHandle,jint dstDensity,jint srcDensity)402 static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
403         jlong chunkHandle, jfloat left, jfloat top, jfloat right, jfloat bottom,
404         jlong paintHandle, jint dstDensity, jint srcDensity) {
405 
406     Canvas* canvas = get_canvas(canvasHandle);
407     Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
408     const android::Res_png_9patch* chunk = reinterpret_cast<android::Res_png_9patch*>(chunkHandle);
409     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
410 
411     if (CC_LIKELY(dstDensity == srcDensity || dstDensity == 0 || srcDensity == 0)) {
412         canvas->drawNinePatch(bitmap, *chunk, left, top, right, bottom, paint);
413     } else {
414         canvas->save(SaveFlags::MatrixClip);
415 
416         SkScalar scale = dstDensity / (float)srcDensity;
417         canvas->translate(left, top);
418         canvas->scale(scale, scale);
419 
420         Paint filteredPaint;
421         if (paint) {
422             filteredPaint = *paint;
423         }
424         filteredPaint.setFilterQuality(kLow_SkFilterQuality);
425 
426         canvas->drawNinePatch(bitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale,
427                 &filteredPaint);
428 
429         canvas->restore();
430     }
431 }
432 
drawBitmap(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle,jfloat left,jfloat top,jlong paintHandle,jint canvasDensity,jint screenDensity,jint bitmapDensity)433 static void drawBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
434                        jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
435                        jint screenDensity, jint bitmapDensity) {
436     Canvas* canvas = get_canvas(canvasHandle);
437     Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
438     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
439 
440     if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
441         if (screenDensity != 0 && screenDensity != bitmapDensity) {
442             Paint filteredPaint;
443             if (paint) {
444                 filteredPaint = *paint;
445             }
446             filteredPaint.setFilterQuality(kLow_SkFilterQuality);
447             canvas->drawBitmap(bitmap, left, top, &filteredPaint);
448         } else {
449             canvas->drawBitmap(bitmap, left, top, paint);
450         }
451     } else {
452         canvas->save(SaveFlags::MatrixClip);
453         SkScalar scale = canvasDensity / (float)bitmapDensity;
454         canvas->translate(left, top);
455         canvas->scale(scale, scale);
456 
457         Paint filteredPaint;
458         if (paint) {
459             filteredPaint = *paint;
460         }
461         filteredPaint.setFilterQuality(kLow_SkFilterQuality);
462 
463         canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
464         canvas->restore();
465     }
466 }
467 
drawBitmapMatrix(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle,jlong matrixHandle,jlong paintHandle)468 static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
469                              jlong matrixHandle, jlong paintHandle) {
470     const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
471     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
472     Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
473     get_canvas(canvasHandle)->drawBitmap(bitmap, *matrix, paint);
474 }
475 
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)476 static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
477                            float srcLeft, float srcTop, float srcRight, float srcBottom,
478                            float dstLeft, float dstTop, float dstRight, float dstBottom,
479                            jlong paintHandle, jint screenDensity, jint bitmapDensity) {
480     Canvas* canvas = get_canvas(canvasHandle);
481     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
482 
483     Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
484     if (screenDensity != 0 && screenDensity != bitmapDensity) {
485         Paint filteredPaint;
486         if (paint) {
487             filteredPaint = *paint;
488         }
489         filteredPaint.setFilterQuality(kLow_SkFilterQuality);
490         canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
491                            dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
492     } else {
493         canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
494                            dstLeft, dstTop, dstRight, dstBottom, paint);
495     }
496 }
497 
drawBitmapArray(JNIEnv * env,jobject,jlong canvasHandle,jintArray jcolors,jint offset,jint stride,jfloat x,jfloat y,jint width,jint height,jboolean hasAlpha,jlong paintHandle)498 static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
499                             jintArray jcolors, jint offset, jint stride,
500                             jfloat x, jfloat y, jint width, jint height,
501                             jboolean hasAlpha, jlong paintHandle) {
502     // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
503     // correct the alphaType to kOpaque_SkAlphaType.
504     SkImageInfo info = SkImageInfo::Make(width, height,
505                            hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
506                            kPremul_SkAlphaType);
507     SkBitmap bitmap;
508     bitmap.setInfo(info);
509     sk_sp<Bitmap> androidBitmap = Bitmap::allocateHeapBitmap(&bitmap);
510     if (!androidBitmap) {
511         return;
512     }
513 
514     if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, &bitmap)) {
515         return;
516     }
517 
518     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
519     get_canvas(canvasHandle)->drawBitmap(*androidBitmap, x, y, paint);
520 }
521 
drawBitmapMesh(JNIEnv * env,jobject,jlong canvasHandle,jlong bitmapHandle,jint meshWidth,jint meshHeight,jfloatArray jverts,jint vertIndex,jintArray jcolors,jint colorIndex,jlong paintHandle)522 static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
523                            jint meshWidth, jint meshHeight, jfloatArray jverts,
524                            jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
525     if (Canvas::GetApiLevel() < __ANDROID_API_P__) {
526         // Before P we forgot to respect these. Now that we do respect them, explicitly
527         // zero them for backward compatibility.
528         vertIndex = 0;
529         colorIndex = 0;
530     }
531 
532     const int ptCount = (meshWidth + 1) * (meshHeight + 1);
533     AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
534     AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
535 
536     const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
537     Bitmap& bitmap = android::bitmap::toBitmap(bitmapHandle);
538     get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
539                                              vertA.ptr() + vertIndex*2,
540                                              colorA.ptr() + colorIndex, paint);
541 }
542 
drawTextChars(JNIEnv * env,jobject,jlong canvasHandle,jcharArray charArray,jint index,jint count,jfloat x,jfloat y,jint bidiFlags,jlong paintHandle)543 static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray,
544                           jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
545                           jlong paintHandle) {
546     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
547     const Typeface* typeface = paint->getAndroidTypeface();
548     ScopedCharArrayRO text(env, charArray);
549     // drawTextString and drawTextChars doesn't use context info
550     get_canvas(canvasHandle)->drawText(
551             text.get() + index, count,  // text buffer
552             0, count,  // draw range
553             0, count,  // context range
554             x, y,  // draw position
555             static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */);
556 }
557 
drawTextString(JNIEnv * env,jobject,jlong canvasHandle,jstring strObj,jint start,jint end,jfloat x,jfloat y,jint bidiFlags,jlong paintHandle)558 static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring strObj,
559                            jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
560                            jlong paintHandle) {
561     ScopedStringChars text(env, strObj);
562     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
563     const Typeface* typeface = paint->getAndroidTypeface();
564     const int count = end - start;
565     // drawTextString and drawTextChars doesn't use context info
566     get_canvas(canvasHandle)->drawText(
567             text.get() + start, count,  // text buffer
568             0, count,  // draw range
569             0, count,  // context range
570             x, y,  // draw position
571             static_cast<minikin::Bidi>(bidiFlags), *paint, typeface, nullptr /* measured text */);
572 }
573 
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)574 static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray charArray,
575                              jint index, jint count, jint contextIndex, jint contextCount,
576                              jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
577                              jlong mtHandle) {
578     minikin::MeasuredText* mt = reinterpret_cast<minikin::MeasuredText*>(mtHandle);
579     const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
580 
581     ScopedCharArrayRO text(env, charArray);
582     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
583     const Typeface* typeface = paint->getAndroidTypeface();
584     get_canvas(canvasHandle)->drawText(
585             text.get(), text.size(),  // text buffer
586             index, count,  // draw range
587             contextIndex, contextCount,  // context range,
588             x, y,  // draw position
589             bidiFlags, *paint, typeface, mt);
590 }
591 
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)592 static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring strObj,
593                               jint start, jint end, jint contextStart, jint contextEnd,
594                               jfloat x, jfloat y, jboolean isRtl, jlong paintHandle) {
595     const minikin::Bidi bidiFlags = isRtl ? minikin::Bidi::FORCE_RTL : minikin::Bidi::FORCE_LTR;
596 
597     ScopedStringChars text(env, strObj);
598     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
599     const Typeface* typeface = paint->getAndroidTypeface();
600     get_canvas(canvasHandle)->drawText(
601             text.get(), text.size(),  // text buffer
602             start, end - start,  // draw range
603             contextStart, contextEnd - contextStart,  // context range
604             x, y,  // draw position
605             bidiFlags, *paint, typeface, nullptr /* measured text */);
606 }
607 
drawTextOnPathChars(JNIEnv * env,jobject,jlong canvasHandle,jcharArray text,jint index,jint count,jlong pathHandle,jfloat hOffset,jfloat vOffset,jint bidiFlags,jlong paintHandle)608 static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
609                                 jint index, jint count, jlong pathHandle, jfloat hOffset,
610                                 jfloat vOffset, jint bidiFlags, jlong paintHandle) {
611     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
612     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
613     const Typeface* typeface = paint->getAndroidTypeface();
614 
615     jchar* jchars = env->GetCharArrayElements(text, NULL);
616 
617     get_canvas(canvasHandle)->drawTextOnPath(jchars + index, count,
618             static_cast<minikin::Bidi>(bidiFlags), *path, hOffset, vOffset, *paint, typeface);
619 
620     env->ReleaseCharArrayElements(text, jchars, 0);
621 }
622 
drawTextOnPathString(JNIEnv * env,jobject,jlong canvasHandle,jstring text,jlong pathHandle,jfloat hOffset,jfloat vOffset,jint bidiFlags,jlong paintHandle)623 static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
624                                  jlong pathHandle, jfloat hOffset, jfloat vOffset,
625                                  jint bidiFlags, jlong paintHandle) {
626     SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
627     Paint* paint = reinterpret_cast<Paint*>(paintHandle);
628     const Typeface* typeface = paint->getAndroidTypeface();
629 
630     const jchar* jchars = env->GetStringChars(text, NULL);
631     int count = env->GetStringLength(text);
632 
633     get_canvas(canvasHandle)->drawTextOnPath(jchars, count, static_cast<minikin::Bidi>(bidiFlags),
634             *path, hOffset, vOffset, *paint, typeface);
635 
636     env->ReleaseStringChars(text, jchars);
637 }
638 
setPaintFilter(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle,jlong filterHandle)639 static void setPaintFilter(CRITICAL_JNI_PARAMS_COMMA jlong canvasHandle, jlong filterHandle) {
640     PaintFilter* paintFilter = reinterpret_cast<PaintFilter*>(filterHandle);
641     get_canvas(canvasHandle)->setPaintFilter(sk_ref_sp(paintFilter));
642 }
643 
freeCaches(JNIEnv * env,jobject)644 static void freeCaches(JNIEnv* env, jobject) {
645     SkGraphics::PurgeFontCache();
646 }
647 
freeTextLayoutCaches(JNIEnv * env,jobject)648 static void freeTextLayoutCaches(JNIEnv* env, jobject) {
649     minikin::Layout::purgeCaches();
650 }
651 
setCompatibilityVersion(JNIEnv * env,jobject,jint apiLevel)652 static void setCompatibilityVersion(JNIEnv* env, jobject, jint apiLevel) {
653     Canvas::setCompatibilityVersion(apiLevel);
654 }
655 
656 
657 }; // namespace CanvasJNI
658 
659 static const JNINativeMethod gMethods[] = {
660     {"nGetNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer},
661     {"nFreeCaches", "()V", (void*) CanvasJNI::freeCaches},
662     {"nFreeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches},
663     {"nSetCompatibilityVersion", "(I)V", (void*) CanvasJNI::setCompatibilityVersion},
664 
665     // ------------ @FastNative ----------------
666     {"nInitRaster", "(J)J", (void*) CanvasJNI::initRaster},
667     {"nSetBitmap", "(JJ)V", (void*) CanvasJNI::setBitmap},
668     {"nGetClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
669 
670     // ------------ @CriticalNative ----------------
671     {"nIsOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
672     {"nGetWidth","(J)I", (void*) CanvasJNI::getWidth},
673     {"nGetHeight","(J)I", (void*) CanvasJNI::getHeight},
674     {"nSave","(JI)I", (void*) CanvasJNI::save},
675     {"nSaveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
676     {"nSaveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
677     {"nSaveUnclippedLayer","(JIIII)I", (void*) CanvasJNI::saveUnclippedLayer},
678     {"nRestoreUnclippedLayer","(JIJ)V", (void*) CanvasJNI::restoreUnclippedLayer},
679     {"nGetSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
680     {"nRestore","(J)Z", (void*) CanvasJNI::restore},
681     {"nRestoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
682     {"nGetMatrix", "(JJ)V", (void*)CanvasJNI::getMatrix},
683     {"nSetMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
684     {"nConcat","(JJ)V", (void*) CanvasJNI::concat},
685     {"nRotate","(JF)V", (void*) CanvasJNI::rotate},
686     {"nScale","(JFF)V", (void*) CanvasJNI::scale},
687     {"nSkew","(JFF)V", (void*) CanvasJNI::skew},
688     {"nTranslate","(JFF)V", (void*) CanvasJNI::translate},
689     {"nQuickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
690     {"nQuickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
691     {"nClipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
692     {"nClipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
693     {"nSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setPaintFilter},
694 };
695 
696 // If called from Canvas these are regular JNI
697 // If called from DisplayListCanvas they are @FastNative
698 static const JNINativeMethod gDrawMethods[] = {
699     {"nDrawColor","(JII)V", (void*) CanvasJNI::drawColor},
700     {"nDrawColor","(JJJI)V", (void*) CanvasJNI::drawColorLong},
701     {"nDrawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
702     {"nDrawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
703     {"nDrawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
704     {"nDrawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
705     {"nDrawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
706     {"nDrawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
707     {"nDrawRegion", "(JJJ)V", (void*) CanvasJNI::drawRegion },
708     {"nDrawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
709     {"nDrawDoubleRoundRect", "(JFFFFFFFFFFFFJ)V", (void*) CanvasJNI::drawDoubleRoundRectXY},
710     {"nDrawDoubleRoundRect", "(JFFFF[FFFFF[FJ)V", (void*) CanvasJNI::drawDoubleRoundRectRadii},
711     {"nDrawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
712     {"nDrawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
713     {"nDrawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
714     {"nDrawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
715     {"nDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
716     {"nDrawNinePatch", "(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
717     {"nDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
718     {"nDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
719     {"nDrawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
720     {"nDrawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
721     {"nDrawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
722     {"nDrawText","(J[CIIFFIJ)V", (void*) CanvasJNI::drawTextChars},
723     {"nDrawText","(JLjava/lang/String;IIFFIJ)V", (void*) CanvasJNI::drawTextString},
724     {"nDrawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
725     {"nDrawTextRun","(JLjava/lang/String;IIIIFFZJ)V", (void*) CanvasJNI::drawTextRunString},
726     {"nDrawTextOnPath","(J[CIIJFFIJ)V", (void*) CanvasJNI::drawTextOnPathChars},
727     {"nDrawTextOnPath","(JLjava/lang/String;JFFIJ)V", (void*) CanvasJNI::drawTextOnPathString},
728 };
729 
register_android_graphics_Canvas(JNIEnv * env)730 int register_android_graphics_Canvas(JNIEnv* env) {
731     int ret = 0;
732     ret |= RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
733     ret |= RegisterMethodsOrDie(env, "android/graphics/BaseCanvas", gDrawMethods, NELEM(gDrawMethods));
734     ret |= RegisterMethodsOrDie(env, "android/graphics/BaseRecordingCanvas", gDrawMethods, NELEM(gDrawMethods));
735     return ret;
736 
737 }
738 
739 }; // namespace android
740