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