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
186static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
187                         jfloat r, jfloat b, jint opHandle) {
188    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
189    bool nonEmptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
190    return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
191}
192
193static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
194                         jint opHandle) {
195    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
196    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
197    bool nonEmptyClip = get_canvas(canvasHandle)->clipPath(path, op);
198    return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
199}
200
201static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle,
202                           jint opHandle) {
203    SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
204    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
205    bool nonEmptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
206    return nonEmptyClip ? JNI_TRUE : JNI_FALSE;
207}
208
209static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
210    SkXfermode::Mode mode = static_cast<SkXfermode::Mode>(modeHandle);
211    get_canvas(canvasHandle)->drawColor(color, mode);
212}
213
214static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
215    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
216    get_canvas(canvasHandle)->drawPaint(*paint);
217}
218
219static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
220                      jlong paintHandle) {
221    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
222    get_canvas(canvasHandle)->drawPoint(x, y, *paint);
223}
224
225static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
226                       jint offset, jint count, jlong paintHandle) {
227    NPE_CHECK_RETURN_VOID(env, jptsArray);
228    AutoJavaFloatArray autoPts(env, jptsArray);
229    float* floats = autoPts.ptr();
230    const int length = autoPts.length();
231
232    if ((offset | count) < 0 || offset + count > length) {
233        doThrowAIOOBE(env);
234        return;
235    }
236
237    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
238    get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
239}
240
241static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
242                     jfloat stopX, jfloat stopY, jlong paintHandle) {
243    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
244    get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
245}
246
247static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
248                      jint offset, jint count, jlong paintHandle) {
249    NPE_CHECK_RETURN_VOID(env, jptsArray);
250    AutoJavaFloatArray autoPts(env, jptsArray);
251    float* floats = autoPts.ptr();
252    const int length = autoPts.length();
253
254    if ((offset | count) < 0 || offset + count > length) {
255        doThrowAIOOBE(env);
256        return;
257    }
258
259    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
260    get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
261}
262
263static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
264                     jfloat right, jfloat bottom, jlong paintHandle) {
265    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
266    get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
267}
268
269static void drawRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong regionHandle,
270                       jlong paintHandle) {
271    const SkRegion* region = reinterpret_cast<SkRegion*>(regionHandle);
272    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
273    get_canvas(canvasHandle)->drawRegion(*region, *paint);
274}
275
276static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
277                          jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
278    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
279    get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
280}
281
282static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
283                       jfloat radius, jlong paintHandle) {
284    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
285    get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
286}
287
288static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
289                     jfloat right, jfloat bottom, jlong paintHandle) {
290    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
291    get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
292}
293
294static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
295                    jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
296                    jboolean useCenter, jlong paintHandle) {
297    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
298    get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
299                                       useCenter, *paint);
300}
301
302static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
303                     jlong paintHandle) {
304    const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
305    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
306    get_canvas(canvasHandle)->drawPath(*path, *paint);
307}
308
309static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
310                         jint modeHandle, jint vertexCount,
311                         jfloatArray jverts, jint vertIndex,
312                         jfloatArray jtexs, jint texIndex,
313                         jintArray jcolors, jint colorIndex,
314                         jshortArray jindices, jint indexIndex,
315                         jint indexCount, jlong paintHandle) {
316    AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
317    AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
318    AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
319    AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
320
321    const float* verts = vertA.ptr() + vertIndex;
322    const float* texs = texA.ptr() + vertIndex;
323    const int* colors = NULL;
324    const uint16_t* indices = NULL;
325
326    if (jcolors != NULL) {
327        colors = colorA.ptr() + colorIndex;
328    }
329    if (jindices != NULL) {
330        indices = (const uint16_t*)(indexA.ptr() + indexIndex);
331    }
332
333    SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
334    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
335    get_canvas(canvasHandle)->drawVertices(mode, vertexCount, verts, texs, colors,
336                                           indices, indexCount, *paint);
337}
338
339static void drawNinePatch(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
340        jlong chunkHandle, jfloat left, jfloat top, jfloat right, jfloat bottom,
341        jlong paintHandle, jint dstDensity, jint srcDensity) {
342
343    Canvas* canvas = get_canvas(canvasHandle);
344    Bitmap* bitmap = reinterpret_cast<Bitmap*>(bitmapHandle);
345    SkBitmap skiaBitmap;
346    bitmap->getSkBitmap(&skiaBitmap);
347    const android::Res_png_9patch* chunk = reinterpret_cast<android::Res_png_9patch*>(chunkHandle);
348    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
349
350    if (CC_LIKELY(dstDensity == srcDensity || dstDensity == 0 || srcDensity == 0)) {
351        canvas->drawNinePatch(skiaBitmap, *chunk, left, top, right, bottom, paint);
352    } else {
353        canvas->save(SaveFlags::MatrixClip);
354
355        SkScalar scale = dstDensity / (float)srcDensity;
356        canvas->translate(left, top);
357        canvas->scale(scale, scale);
358
359        Paint filteredPaint;
360        if (paint) {
361            filteredPaint = *paint;
362        }
363        filteredPaint.setFilterQuality(kLow_SkFilterQuality);
364
365        canvas->drawNinePatch(skiaBitmap, *chunk, 0, 0, (right-left)/scale, (bottom-top)/scale,
366                &filteredPaint);
367
368        canvas->restore();
369    }
370}
371
372static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jobject jbitmap,
373                       jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
374                       jint screenDensity, jint bitmapDensity) {
375    Canvas* canvas = get_canvas(canvasHandle);
376    SkBitmap bitmap;
377    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
378    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
379
380    if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
381        if (screenDensity != 0 && screenDensity != bitmapDensity) {
382            Paint filteredPaint;
383            if (paint) {
384                filteredPaint = *paint;
385            }
386            filteredPaint.setFilterQuality(kLow_SkFilterQuality);
387            canvas->drawBitmap(bitmap, left, top, &filteredPaint);
388        } else {
389            canvas->drawBitmap(bitmap, left, top, paint);
390        }
391    } else {
392        canvas->save(SaveFlags::MatrixClip);
393        SkScalar scale = canvasDensity / (float)bitmapDensity;
394        canvas->translate(left, top);
395        canvas->scale(scale, scale);
396
397        Paint filteredPaint;
398        if (paint) {
399            filteredPaint = *paint;
400        }
401        filteredPaint.setFilterQuality(kLow_SkFilterQuality);
402
403        canvas->drawBitmap(bitmap, 0, 0, &filteredPaint);
404        canvas->restore();
405    }
406}
407
408static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
409                             jlong matrixHandle, jlong paintHandle) {
410    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
411    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
412    SkBitmap bitmap;
413    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
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    SkBitmap bitmap;
425    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
426    if (screenDensity != 0 && screenDensity != bitmapDensity) {
427        Paint filteredPaint;
428        if (paint) {
429            filteredPaint = *paint;
430        }
431        filteredPaint.setFilterQuality(kLow_SkFilterQuality);
432        canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
433                           dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
434    } else {
435        canvas->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
436                           dstLeft, dstTop, dstRight, dstBottom, paint);
437    }
438}
439
440static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
441                            jintArray jcolors, jint offset, jint stride,
442                            jfloat x, jfloat y, jint width, jint height,
443                            jboolean hasAlpha, jlong paintHandle) {
444    // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
445    // correct the alphaType to kOpaque_SkAlphaType.
446    SkImageInfo info = SkImageInfo::Make(width, height,
447                           hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
448                           kPremul_SkAlphaType);
449    SkBitmap bitmap;
450    bitmap.setInfo(info);
451    if (!GraphicsJNI::allocatePixels(env, &bitmap, NULL)) {
452        return;
453    }
454
455    if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, bitmap)) {
456        return;
457    }
458
459    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
460    get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
461}
462
463static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jobject jbitmap,
464                           jint meshWidth, jint meshHeight, jfloatArray jverts,
465                           jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
466    const int ptCount = (meshWidth + 1) * (meshHeight + 1);
467    AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
468    AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
469
470    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
471    SkBitmap bitmap;
472    GraphicsJNI::getSkBitmap(env, jbitmap, &bitmap);
473    get_canvas(canvasHandle)->drawBitmapMesh(bitmap, meshWidth, meshHeight,
474                                             vertA.ptr(), colorA.ptr(), paint);
475}
476
477static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
478                          jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
479                          jlong paintHandle, jlong typefaceHandle) {
480    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
481    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
482    jchar* jchars = env->GetCharArrayElements(text, NULL);
483    get_canvas(canvasHandle)->drawText(jchars + index, 0, count, count, x, y,
484                                       bidiFlags, *paint, typeface);
485    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
486}
487
488static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
489                           jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
490                           jlong paintHandle, jlong typefaceHandle) {
491    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
492    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
493    const int count = end - start;
494    const jchar* jchars = env->GetStringChars(text, NULL);
495    get_canvas(canvasHandle)->drawText(jchars + start, 0, count, count, x, y,
496                                       bidiFlags, *paint, typeface);
497    env->ReleaseStringChars(text, jchars);
498}
499
500static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
501                             jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
502                             jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
503    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
504    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
505
506    const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
507    jchar* jchars = env->GetCharArrayElements(text, NULL);
508    get_canvas(canvasHandle)->drawText(jchars + contextIndex, index - contextIndex, count,
509                                       contextCount, x, y, bidiFlags, *paint, typeface);
510    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
511}
512
513static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring text,
514                              jint start, jint end, jint contextStart, jint contextEnd,
515                              jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
516                              jlong typefaceHandle) {
517    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
518    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
519
520    int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
521    jint count = end - start;
522    jint contextCount = contextEnd - contextStart;
523    const jchar* jchars = env->GetStringChars(text, NULL);
524    get_canvas(canvasHandle)->drawText(jchars + contextStart, start - contextStart, count,
525                                       contextCount, x, y, bidiFlags, *paint, typeface);
526    env->ReleaseStringChars(text, jchars);
527}
528
529static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
530                                jint index, jint count, jlong pathHandle, jfloat hOffset,
531                                jfloat vOffset, jint bidiFlags, jlong paintHandle,
532                                jlong typefaceHandle) {
533    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
534    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
535    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
536
537    jchar* jchars = env->GetCharArrayElements(text, NULL);
538
539    get_canvas(canvasHandle)->drawTextOnPath(jchars + index, count, bidiFlags, *path,
540                   hOffset, vOffset, *paint, typeface);
541
542    env->ReleaseCharArrayElements(text, jchars, 0);
543}
544
545static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
546                                 jlong pathHandle, jfloat hOffset, jfloat vOffset,
547                                 jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
548    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
549    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
550    Typeface* typeface = reinterpret_cast<Typeface*>(typefaceHandle);
551
552    const jchar* jchars = env->GetStringChars(text, NULL);
553    int count = env->GetStringLength(text);
554
555    get_canvas(canvasHandle)->drawTextOnPath(jchars, count, bidiFlags, *path,
556                   hOffset, vOffset, *paint, typeface);
557
558    env->ReleaseStringChars(text, jchars);
559}
560
561static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
562    get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
563}
564
565static void freeCaches(JNIEnv* env, jobject) {
566    SkGraphics::PurgeFontCache();
567}
568
569static void freeTextLayoutCaches(JNIEnv* env, jobject) {
570    Layout::purgeCaches();
571}
572
573}; // namespace CanvasJNI
574
575static const JNINativeMethod gMethods[] = {
576    {"getNativeFinalizer", "()J", (void*) CanvasJNI::getNativeFinalizer},
577    {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
578    {"native_setBitmap", "!(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
579    {"native_isOpaque","!(J)Z", (void*) CanvasJNI::isOpaque},
580    {"native_getWidth","!(J)I", (void*) CanvasJNI::getWidth},
581    {"native_getHeight","!(J)I", (void*) CanvasJNI::getHeight},
582    {"native_setHighContrastText","!(JZ)V", (void*) CanvasJNI::setHighContrastText},
583    {"native_save","!(JI)I", (void*) CanvasJNI::save},
584    {"native_saveLayer","!(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
585    {"native_saveLayerAlpha","!(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
586    {"native_getSaveCount","!(J)I", (void*) CanvasJNI::getSaveCount},
587    {"native_restore","!(JZ)V", (void*) CanvasJNI::restore},
588    {"native_restoreToCount","!(JIZ)V", (void*) CanvasJNI::restoreToCount},
589    {"native_getCTM", "!(JJ)V", (void*)CanvasJNI::getCTM},
590    {"native_setMatrix","!(JJ)V", (void*) CanvasJNI::setMatrix},
591    {"native_concat","!(JJ)V", (void*) CanvasJNI::concat},
592    {"native_rotate","!(JF)V", (void*) CanvasJNI::rotate},
593    {"native_scale","!(JFF)V", (void*) CanvasJNI::scale},
594    {"native_skew","!(JFF)V", (void*) CanvasJNI::skew},
595    {"native_translate","!(JFF)V", (void*) CanvasJNI::translate},
596    {"native_getClipBounds","!(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
597    {"native_quickReject","!(JJ)Z", (void*) CanvasJNI::quickRejectPath},
598    {"native_quickReject","!(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
599    {"native_clipRect","!(JFFFFI)Z", (void*) CanvasJNI::clipRect},
600    {"native_clipPath","!(JJI)Z", (void*) CanvasJNI::clipPath},
601    {"native_clipRegion","!(JJI)Z", (void*) CanvasJNI::clipRegion},
602    {"native_drawColor","!(JII)V", (void*) CanvasJNI::drawColor},
603    {"native_drawPaint","!(JJ)V", (void*) CanvasJNI::drawPaint},
604    {"native_drawPoint", "!(JFFJ)V", (void*) CanvasJNI::drawPoint},
605    {"native_drawPoints", "!(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
606    {"native_drawLine", "!(JFFFFJ)V", (void*) CanvasJNI::drawLine},
607    {"native_drawLines", "!(J[FIIJ)V", (void*) CanvasJNI::drawLines},
608    {"native_drawRect","!(JFFFFJ)V", (void*) CanvasJNI::drawRect},
609    {"native_drawRegion", "!(JJJ)V", (void*) CanvasJNI::drawRegion },
610    {"native_drawRoundRect","!(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
611    {"native_drawCircle","!(JFFFJ)V", (void*) CanvasJNI::drawCircle},
612    {"native_drawOval","!(JFFFFJ)V", (void*) CanvasJNI::drawOval},
613    {"native_drawArc","!(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
614    {"native_drawPath","!(JJJ)V", (void*) CanvasJNI::drawPath},
615    {"nativeDrawVertices", "!(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
616    {"native_drawNinePatch", "!(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
617    {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap},
618    {"nativeDrawBitmapMatrix", "!(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix},
619    {"native_drawBitmap","(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
620    {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
621    {"nativeDrawBitmapMesh", "!(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
622    {"native_drawText","!(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
623    {"native_drawText","!(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
624    {"native_drawTextRun","!(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
625    {"native_drawTextRun","!(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
626    {"native_drawTextOnPath","!(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
627    {"native_drawTextOnPath","!(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
628    {"nativeSetDrawFilter", "!(JJ)V", (void*) CanvasJNI::setDrawFilter},
629    {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
630    {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
631};
632
633int register_android_graphics_Canvas(JNIEnv* env) {
634    return RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
635}
636
637}; // namespace android
638