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