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