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