android_graphics_Canvas.cpp revision 76f6a86de25e1bf74717e047e55fd44b089673f3
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 "Canvas.h"
22#include "SkDrawFilter.h"
23#include "SkGraphics.h"
24#include "SkPorterDuff.h"
25#include "Paint.h"
26#include "TypefaceImpl.h"
27
28#include "MinikinUtils.h"
29
30namespace android {
31
32namespace CanvasJNI {
33
34static Canvas* get_canvas(jlong canvasHandle) {
35    return reinterpret_cast<Canvas*>(canvasHandle);
36}
37
38static void finalizer(JNIEnv* env, jobject clazz, jlong canvasHandle) {
39    delete get_canvas(canvasHandle);
40}
41
42// Native wrapper constructor used by Canvas(Bitmap)
43static jlong initRaster(JNIEnv* env, jobject, jlong bitmapHandle) {
44    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
45    return reinterpret_cast<jlong>(Canvas::create_canvas(bitmap));
46}
47
48// Set the given bitmap as the new draw target (wrapped in a new SkCanvas),
49// optionally copying canvas matrix & clip state.
50static void setBitmap(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
51                      jboolean copyState) {
52    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
53    get_canvas(canvasHandle)->setBitmap(bitmap, copyState);
54}
55
56static jboolean isOpaque(JNIEnv*, jobject, jlong canvasHandle) {
57    return get_canvas(canvasHandle)->isOpaque() ? JNI_TRUE : JNI_FALSE;
58}
59
60static jint getWidth(JNIEnv*, jobject, jlong canvasHandle) {
61    return static_cast<jint>(get_canvas(canvasHandle)->width());
62}
63
64static jint getHeight(JNIEnv*, jobject, jlong canvasHandle) {
65    return static_cast<jint>(get_canvas(canvasHandle)->height());
66}
67
68static jint getSaveCount(JNIEnv*, jobject, jlong canvasHandle) {
69    return static_cast<jint>(get_canvas(canvasHandle)->getSaveCount());
70}
71
72static jint save(JNIEnv*, jobject, jlong canvasHandle, jint flagsHandle) {
73    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
74    return static_cast<jint>(get_canvas(canvasHandle)->save(flags));
75}
76
77static jint saveLayer(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
78                      jfloat r, jfloat b, jlong paintHandle, jint flagsHandle) {
79    Paint* paint  = reinterpret_cast<Paint*>(paintHandle);
80    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
81    return static_cast<jint>(get_canvas(canvasHandle)->saveLayer(l, t, r, b, paint, flags));
82}
83
84static jint saveLayerAlpha(JNIEnv* env, jobject, jlong canvasHandle, jfloat l, jfloat t,
85                           jfloat r, jfloat b, jint alpha, jint flagsHandle) {
86    SkCanvas::SaveFlags flags = static_cast<SkCanvas::SaveFlags>(flagsHandle);
87    return static_cast<jint>(get_canvas(canvasHandle)->saveLayerAlpha(l, t, r, b, alpha, flags));
88}
89
90static void restore(JNIEnv* env, jobject, jlong canvasHandle) {
91    Canvas* canvas = get_canvas(canvasHandle);
92    if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
93        doThrowISE(env, "Underflow in restore");
94        return;
95    }
96    canvas->restore();
97}
98
99static void restoreToCount(JNIEnv* env, jobject, jlong canvasHandle, jint restoreCount) {
100    Canvas* canvas = get_canvas(canvasHandle);
101    if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
102        doThrowIAE(env, "Underflow in restoreToCount");
103        return;
104    }
105    canvas->restoreToCount(restoreCount);
106}
107
108static void getCTM(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
109    SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
110    get_canvas(canvasHandle)->getMatrix(matrix);
111}
112
113static void setMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
114    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
115    get_canvas(canvasHandle)->setMatrix(matrix ? *matrix : SkMatrix::I());
116}
117
118static void concat(JNIEnv* env, jobject, jlong canvasHandle, jlong matrixHandle) {
119    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
120    get_canvas(canvasHandle)->concat(*matrix);
121}
122
123static void rotate(JNIEnv*, jobject, jlong canvasHandle, jfloat degrees) {
124    get_canvas(canvasHandle)->rotate(degrees);
125}
126
127static void scale(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
128    get_canvas(canvasHandle)->scale(sx, sy);
129}
130
131static void skew(JNIEnv*, jobject, jlong canvasHandle, jfloat sx, jfloat sy) {
132    get_canvas(canvasHandle)->skew(sx, sy);
133}
134
135static void translate(JNIEnv*, jobject, jlong canvasHandle, jfloat dx, jfloat dy) {
136    get_canvas(canvasHandle)->translate(dx, dy);
137}
138
139static jboolean getClipBounds(JNIEnv* env, jobject, jlong canvasHandle, jobject bounds) {
140    SkRect   r;
141    SkIRect ir;
142    bool result = get_canvas(canvasHandle)->getClipBounds(&r);
143
144    if (!result) {
145        r.setEmpty();
146    }
147    r.round(&ir);
148
149    (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
150    return result ? JNI_TRUE : JNI_FALSE;
151}
152
153static jboolean quickRejectRect(JNIEnv* env, jobject, jlong canvasHandle,
154                                jfloat left, jfloat top, jfloat right, jfloat bottom) {
155    bool result = get_canvas(canvasHandle)->quickRejectRect(left, top, right, bottom);
156    return result ? JNI_TRUE : JNI_FALSE;
157}
158
159static jboolean quickRejectPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle) {
160    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
161    bool result = get_canvas(canvasHandle)->quickRejectPath(*path);
162    return result ? JNI_TRUE : JNI_FALSE;
163}
164
165static jboolean clipRect(JNIEnv*, jobject, jlong canvasHandle, jfloat l, jfloat t,
166                         jfloat r, jfloat b, jint opHandle) {
167    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
168    bool emptyClip = get_canvas(canvasHandle)->clipRect(l, t, r, b, op);
169    return emptyClip ? JNI_FALSE : JNI_TRUE;
170}
171
172static jboolean clipPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
173                         jint opHandle) {
174    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
175    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
176    bool emptyClip = get_canvas(canvasHandle)->clipPath(path, op);
177    return emptyClip ? JNI_FALSE : JNI_TRUE;
178}
179
180static jboolean clipRegion(JNIEnv* env, jobject, jlong canvasHandle, jlong deviceRgnHandle,
181                           jint opHandle) {
182    SkRegion* deviceRgn = reinterpret_cast<SkRegion*>(deviceRgnHandle);
183    SkRegion::Op op = static_cast<SkRegion::Op>(opHandle);
184    bool emptyClip = get_canvas(canvasHandle)->clipRegion(deviceRgn, op);
185    return emptyClip ? JNI_FALSE : JNI_TRUE;
186}
187
188static void drawColor(JNIEnv* env, jobject, jlong canvasHandle, jint color, jint modeHandle) {
189     SkPorterDuff::Mode mode = static_cast<SkPorterDuff::Mode>(modeHandle);
190     get_canvas(canvasHandle)->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
191}
192
193static void drawPaint(JNIEnv* env, jobject, jlong canvasHandle, jlong paintHandle) {
194    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
195    get_canvas(canvasHandle)->drawPaint(*paint);
196}
197
198static void drawPoint(JNIEnv*, jobject, jlong canvasHandle, jfloat x, jfloat y,
199                      jlong paintHandle) {
200    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
201    get_canvas(canvasHandle)->drawPoint(x, y, *paint);
202}
203
204static void drawPoints(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
205                       jint offset, jint count, jlong paintHandle) {
206    NPE_CHECK_RETURN_VOID(env, jptsArray);
207    AutoJavaFloatArray autoPts(env, jptsArray);
208    float* floats = autoPts.ptr();
209    const int length = autoPts.length();
210
211    if ((offset | count) < 0 || offset + count > length) {
212        doThrowAIOOBE(env);
213        return;
214    }
215
216    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
217    get_canvas(canvasHandle)->drawPoints(floats + offset, count, *paint);
218}
219
220static void drawLine(JNIEnv* env, jobject, jlong canvasHandle, jfloat startX, jfloat startY,
221                     jfloat stopX, jfloat stopY, jlong paintHandle) {
222    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
223    get_canvas(canvasHandle)->drawLine(startX, startY, stopX, stopY, *paint);
224}
225
226static void drawLines(JNIEnv* env, jobject, jlong canvasHandle, jfloatArray jptsArray,
227                      jint offset, jint count, jlong paintHandle) {
228    NPE_CHECK_RETURN_VOID(env, jptsArray);
229    AutoJavaFloatArray autoPts(env, jptsArray);
230    float* floats = autoPts.ptr();
231    const int length = autoPts.length();
232
233    if ((offset | count) < 0 || offset + count > length) {
234        doThrowAIOOBE(env);
235        return;
236    }
237
238    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
239    get_canvas(canvasHandle)->drawLines(floats + offset, count, *paint);
240}
241
242static void drawRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
243                     jfloat right, jfloat bottom, jlong paintHandle) {
244    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
245    get_canvas(canvasHandle)->drawRect(left, top, right, bottom, *paint);
246}
247
248static void drawRoundRect(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
249                          jfloat right, jfloat bottom, jfloat rx, jfloat ry, jlong paintHandle) {
250    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
251    get_canvas(canvasHandle)->drawRoundRect(left, top, right, bottom, rx, ry, *paint);
252}
253
254static void drawCircle(JNIEnv* env, jobject, jlong canvasHandle, jfloat cx, jfloat cy,
255                       jfloat radius, jlong paintHandle) {
256    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
257    get_canvas(canvasHandle)->drawCircle(cx, cy, radius, *paint);
258}
259
260static void drawOval(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
261                     jfloat right, jfloat bottom, jlong paintHandle) {
262    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
263    get_canvas(canvasHandle)->drawOval(left, top, right, bottom, *paint);
264}
265
266static void drawArc(JNIEnv* env, jobject, jlong canvasHandle, jfloat left, jfloat top,
267                    jfloat right, jfloat bottom, jfloat startAngle, jfloat sweepAngle,
268                    jboolean useCenter, jlong paintHandle) {
269    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
270    get_canvas(canvasHandle)->drawArc(left, top, right, bottom, startAngle, sweepAngle,
271                                       useCenter, *paint);
272}
273
274static void drawPath(JNIEnv* env, jobject, jlong canvasHandle, jlong pathHandle,
275                     jlong paintHandle) {
276    const SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
277    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
278    get_canvas(canvasHandle)->drawPath(*path, *paint);
279}
280
281static void drawVertices(JNIEnv* env, jobject, jlong canvasHandle,
282                         jint modeHandle, jint vertexCount,
283                         jfloatArray jverts, jint vertIndex,
284                         jfloatArray jtexs, jint texIndex,
285                         jintArray jcolors, jint colorIndex,
286                         jshortArray jindices, jint indexIndex,
287                         jint indexCount, jlong paintHandle) {
288    AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
289    AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
290    AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
291    AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
292
293    const float* verts = vertA.ptr() + vertIndex;
294    const float* texs = texA.ptr() + vertIndex;
295    const int* colors = NULL;
296    const uint16_t* indices = NULL;
297
298    if (jcolors != NULL) {
299        colors = colorA.ptr() + colorIndex;
300    }
301    if (jindices != NULL) {
302        indices = (const uint16_t*)(indexA.ptr() + indexIndex);
303    }
304
305    SkCanvas::VertexMode mode = static_cast<SkCanvas::VertexMode>(modeHandle);
306    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
307    get_canvas(canvasHandle)->drawVertices(mode, vertexCount, verts, texs, colors,
308                                           indices, indexCount, *paint);
309}
310
311static void drawBitmap(JNIEnv* env, jobject jcanvas, jlong canvasHandle, jlong bitmapHandle,
312                       jfloat left, jfloat top, jlong paintHandle, jint canvasDensity,
313                       jint screenDensity, jint bitmapDensity) {
314    Canvas* canvas = get_canvas(canvasHandle);
315    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
316    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
317
318    if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
319        if (screenDensity != 0 && screenDensity != bitmapDensity) {
320            Paint filteredPaint;
321            if (paint) {
322                filteredPaint = *paint;
323            }
324            filteredPaint.setFilterLevel(Paint::kLow_FilterLevel);
325            canvas->drawBitmap(*bitmap, left, top, &filteredPaint);
326        } else {
327            canvas->drawBitmap(*bitmap, left, top, paint);
328        }
329    } else {
330        canvas->save(SkCanvas::kMatrixClip_SaveFlag);
331        SkScalar scale = canvasDensity / (float)bitmapDensity;
332        canvas->translate(left, top);
333        canvas->scale(scale, scale);
334
335        Paint filteredPaint;
336        if (paint) {
337            filteredPaint = *paint;
338        }
339        filteredPaint.setFilterLevel(Paint::kLow_FilterLevel);
340
341        canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
342        canvas->restore();
343    }
344}
345
346static void drawBitmapMatrix(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
347                             jlong matrixHandle, jlong paintHandle) {
348    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
349    const SkMatrix* matrix = reinterpret_cast<SkMatrix*>(matrixHandle);
350    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
351    get_canvas(canvasHandle)->drawBitmap(*bitmap, *matrix, paint);
352}
353
354static void drawBitmapRect(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
355                           float srcLeft, float srcTop, float srcRight, float srcBottom,
356                           float dstLeft, float dstTop, float dstRight, float dstBottom,
357                           jlong paintHandle, jint screenDensity, jint bitmapDensity) {
358    Canvas* canvas = get_canvas(canvasHandle);
359    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
360    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
361
362    if (screenDensity != 0 && screenDensity != bitmapDensity) {
363        Paint filteredPaint;
364        if (paint) {
365            filteredPaint = *paint;
366        }
367        filteredPaint.setFilterLevel(Paint::kLow_FilterLevel);
368        canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
369                           dstLeft, dstTop, dstRight, dstBottom, &filteredPaint);
370    } else {
371        canvas->drawBitmap(*bitmap, srcLeft, srcTop, srcRight, srcBottom,
372                           dstLeft, dstTop, dstRight, dstBottom, paint);
373    }
374}
375
376static void drawBitmapArray(JNIEnv* env, jobject, jlong canvasHandle,
377                            jintArray jcolors, jint offset, jint stride,
378                            jfloat x, jfloat y, jint width, jint height,
379                            jboolean hasAlpha, jlong paintHandle) {
380    // Note: If hasAlpha is false, kRGB_565_SkColorType will be used, which will
381    // correct the alphaType to kOpaque_SkAlphaType.
382    SkImageInfo info = SkImageInfo::Make(width, height,
383                           hasAlpha ? kN32_SkColorType : kRGB_565_SkColorType,
384                           kPremul_SkAlphaType);
385    SkBitmap bitmap;
386    if (!bitmap.allocPixels(info)) {
387        return;
388    }
389
390    if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride, 0, 0, width, height, bitmap)) {
391        return;
392    }
393
394    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
395    get_canvas(canvasHandle)->drawBitmap(bitmap, x, y, paint);
396}
397
398static void drawBitmapMesh(JNIEnv* env, jobject, jlong canvasHandle, jlong bitmapHandle,
399                           jint meshWidth, jint meshHeight, jfloatArray jverts,
400                           jint vertIndex, jintArray jcolors, jint colorIndex, jlong paintHandle) {
401    const int ptCount = (meshWidth + 1) * (meshHeight + 1);
402    AutoJavaFloatArray vertA(env, jverts, vertIndex + (ptCount << 1));
403    AutoJavaIntArray colorA(env, jcolors, colorIndex + ptCount);
404
405    const SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(bitmapHandle);
406    const Paint* paint = reinterpret_cast<Paint*>(paintHandle);
407    get_canvas(canvasHandle)->drawBitmapMesh(*bitmap, meshWidth, meshHeight,
408                                             vertA.ptr(), colorA.ptr(), paint);
409}
410
411class DrawTextFunctor {
412public:
413    DrawTextFunctor(const Layout& layout, Canvas* canvas, uint16_t* glyphs, float* pos,
414                    const SkPaint& paint, float x, float y, MinikinRect& bounds)
415            : layout(layout), canvas(canvas), glyphs(glyphs), pos(pos), paint(paint),
416              x(x), y(y), bounds(bounds) { }
417
418    void operator()(size_t start, size_t end) {
419        if (canvas->drawTextAbsolutePos()) {
420            for (size_t i = start; i < end; i++) {
421                glyphs[i] = layout.getGlyphId(i);
422                pos[2 * i] = x + layout.getX(i);
423                pos[2 * i + 1] = y + layout.getY(i);
424            }
425        } else {
426            for (size_t i = start; i < end; i++) {
427                glyphs[i] = layout.getGlyphId(i);
428                pos[2 * i] = layout.getX(i);
429                pos[2 * i + 1] = layout.getY(i);
430            }
431        }
432
433        size_t glyphCount = end - start;
434        canvas->drawText(glyphs + start, pos + (2 * start), glyphCount, paint, x, y,
435                         bounds.mLeft , bounds.mTop , bounds.mRight , bounds.mBottom);
436    }
437private:
438    const Layout& layout;
439    Canvas* canvas;
440    uint16_t* glyphs;
441    float* pos;
442    const SkPaint& paint;
443    float x;
444    float y;
445    MinikinRect& bounds;
446};
447
448// Same values used by Skia
449#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
450#define kStdUnderline_Offset    (1.0f / 9.0f)
451#define kStdUnderline_Thickness (1.0f / 18.0f)
452
453void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) {
454    uint32_t flags;
455    SkDrawFilter* drawFilter = canvas->getDrawFilter();
456    if (drawFilter) {
457        SkPaint paintCopy(paint);
458        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
459        flags = paintCopy.getFlags();
460    } else {
461        flags = paint.getFlags();
462    }
463    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
464        SkScalar left = x;
465        SkScalar right = x + length;
466        float textSize = paint.getTextSize();
467        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
468        if (flags & SkPaint::kUnderlineText_Flag) {
469            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
470            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
471            canvas->drawRect(left, top, right, bottom, paint);
472        }
473        if (flags & SkPaint::kStrikeThruText_Flag) {
474            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
475            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
476            canvas->drawRect(left, top, right, bottom, paint);
477        }
478    }
479}
480
481void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount,
482             float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) {
483    // minikin may modify the original paint
484    Paint paint(origPaint);
485
486    Layout layout;
487    MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
488
489    size_t nGlyphs = layout.nGlyphs();
490    uint16_t* glyphs = new uint16_t[nGlyphs];
491    float* pos = new float[nGlyphs * 2];
492
493    x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
494
495    MinikinRect bounds;
496    layout.getBounds(&bounds);
497
498    DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds);
499    MinikinUtils::forFontRun(layout, &paint, f);
500
501    drawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
502
503    delete[] glyphs;
504    delete[] pos;
505}
506
507static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
508                          jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
509                          jlong paintHandle, jlong typefaceHandle) {
510    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
511    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
512    jchar* jchars = env->GetCharArrayElements(text, NULL);
513    drawText(get_canvas(canvasHandle), jchars + index, 0, count, count, x, y,
514                                       bidiFlags, *paint, typeface);
515    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
516}
517
518static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
519                           jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
520                           jlong paintHandle, jlong typefaceHandle) {
521    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
522    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
523    const int count = end - start;
524    const jchar* jchars = env->GetStringChars(text, NULL);
525    drawText(get_canvas(canvasHandle), jchars + start, 0, count, count, x, y,
526                                       bidiFlags, *paint, typeface);
527    env->ReleaseStringChars(text, jchars);
528}
529
530static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
531                             jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
532                             jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
533    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
534    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
535
536    const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
537    jchar* jchars = env->GetCharArrayElements(text, NULL);
538    drawText(get_canvas(canvasHandle), jchars + contextIndex, index - contextIndex, count,
539                                       contextCount, x, y, bidiFlags, *paint, typeface);
540    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
541}
542
543static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring text,
544                              jint start, jint end, jint contextStart, jint contextEnd,
545                              jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
546                              jlong typefaceHandle) {
547    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
548    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
549
550    int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
551    jint count = end - start;
552    jint contextCount = contextEnd - contextStart;
553    const jchar* jchars = env->GetStringChars(text, NULL);
554    drawText(get_canvas(canvasHandle), jchars + contextStart, start - contextStart, count,
555                                       contextCount, x, y, bidiFlags, *paint, typeface);
556    env->ReleaseStringChars(text, jchars);
557}
558
559class DrawTextOnPathFunctor {
560public:
561    DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
562                float vOffset, const Paint& paint, const SkPath& path)
563            : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
564                paint(paint), path(path) {
565    }
566    void operator()(size_t start, size_t end) {
567        uint16_t glyphs[1];
568        for (size_t i = start; i < end; i++) {
569            glyphs[0] = layout.getGlyphId(i);
570            float x = hOffset + layout.getX(i);
571            float y = vOffset + layout.getY(i);
572            canvas->drawTextOnPath(glyphs, 1, path, x, y, paint);
573        }
574    }
575private:
576    const Layout& layout;
577    Canvas* canvas;
578    float hOffset;
579    float vOffset;
580    const Paint& paint;
581    const SkPath& path;
582};
583
584static void drawTextOnPath(Canvas* canvas, const uint16_t* text, int count, int bidiFlags,
585                           const SkPath& path, float hOffset, float vOffset,
586                           const Paint& paint, TypefaceImpl* typeface) {
587    Paint paintCopy(paint);
588    Layout layout;
589    MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
590    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
591
592    // Set align to left for drawing, as we don't want individual
593    // glyphs centered or right-aligned; the offset above takes
594    // care of all alignment.
595    paintCopy.setTextAlign(Paint::kLeft_Align);
596
597    DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
598    MinikinUtils::forFontRun(layout, &paintCopy, f);
599}
600
601static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
602                                jint index, jint count, jlong pathHandle, jfloat hOffset,
603                                jfloat vOffset, jint bidiFlags, jlong paintHandle,
604                                jlong typefaceHandle) {
605    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
606    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
607    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
608
609    jchar* jchars = env->GetCharArrayElements(text, NULL);
610
611    drawTextOnPath(get_canvas(canvasHandle), jchars + index, count, bidiFlags, *path,
612                   hOffset, vOffset, *paint, typeface);
613
614    env->ReleaseCharArrayElements(text, jchars, 0);
615}
616
617static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
618                                 jlong pathHandle, jfloat hOffset, jfloat vOffset,
619                                 jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
620    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
621    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
622    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
623
624    const jchar* jchars = env->GetStringChars(text, NULL);
625    int count = env->GetStringLength(text);
626
627    drawTextOnPath(get_canvas(canvasHandle), jchars, count, bidiFlags, *path,
628                   hOffset, vOffset, *paint, typeface);
629
630    env->ReleaseStringChars(text, jchars);
631}
632
633static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
634    get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
635}
636
637static void freeCaches(JNIEnv* env, jobject) {
638    SkGraphics::PurgeFontCache();
639}
640
641static void freeTextLayoutCaches(JNIEnv* env, jobject) {
642    Layout::purgeCaches();
643}
644
645}; // namespace CanvasJNI
646
647static const JNINativeMethod gMethods[] = {
648    {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
649    {"initRaster", "(J)J", (void*) CanvasJNI::initRaster},
650    {"native_setBitmap", "(JJZ)V", (void*) CanvasJNI::setBitmap},
651    {"native_isOpaque","(J)Z", (void*) CanvasJNI::isOpaque},
652    {"native_getWidth","(J)I", (void*) CanvasJNI::getWidth},
653    {"native_getHeight","(J)I", (void*) CanvasJNI::getHeight},
654    {"native_save","(JI)I", (void*) CanvasJNI::save},
655    {"native_saveLayer","(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
656    {"native_saveLayerAlpha","(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
657    {"native_getSaveCount","(J)I", (void*) CanvasJNI::getSaveCount},
658    {"native_restore","(J)V", (void*) CanvasJNI::restore},
659    {"native_restoreToCount","(JI)V", (void*) CanvasJNI::restoreToCount},
660    {"native_getCTM", "(JJ)V", (void*)CanvasJNI::getCTM},
661    {"native_setMatrix","(JJ)V", (void*) CanvasJNI::setMatrix},
662    {"native_concat","(JJ)V", (void*) CanvasJNI::concat},
663    {"native_rotate","(JF)V", (void*) CanvasJNI::rotate},
664    {"native_scale","(JFF)V", (void*) CanvasJNI::scale},
665    {"native_skew","(JFF)V", (void*) CanvasJNI::skew},
666    {"native_translate","(JFF)V", (void*) CanvasJNI::translate},
667    {"native_getClipBounds","(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
668    {"native_quickReject","(JJ)Z", (void*) CanvasJNI::quickRejectPath},
669    {"native_quickReject","(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
670    {"native_clipRect","(JFFFFI)Z", (void*) CanvasJNI::clipRect},
671    {"native_clipPath","(JJI)Z", (void*) CanvasJNI::clipPath},
672    {"native_clipRegion","(JJI)Z", (void*) CanvasJNI::clipRegion},
673    {"native_drawColor","(JII)V", (void*) CanvasJNI::drawColor},
674    {"native_drawPaint","(JJ)V", (void*) CanvasJNI::drawPaint},
675    {"native_drawPoint", "(JFFJ)V", (void*) CanvasJNI::drawPoint},
676    {"native_drawPoints", "(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
677    {"native_drawLine", "(JFFFFJ)V", (void*) CanvasJNI::drawLine},
678    {"native_drawLines", "(J[FIIJ)V", (void*) CanvasJNI::drawLines},
679    {"native_drawRect","(JFFFFJ)V", (void*) CanvasJNI::drawRect},
680    {"native_drawRoundRect","(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
681    {"native_drawCircle","(JFFFJ)V", (void*) CanvasJNI::drawCircle},
682    {"native_drawOval","(JFFFFJ)V", (void*) CanvasJNI::drawOval},
683    {"native_drawArc","(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
684    {"native_drawPath","(JJJ)V", (void*) CanvasJNI::drawPath},
685    {"nativeDrawVertices", "(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
686    {"native_drawBitmap","(JJFFJIII)V", (void*) CanvasJNI::drawBitmap},
687    {"nativeDrawBitmapMatrix", "(JJJJ)V", (void*)CanvasJNI::drawBitmapMatrix},
688    {"native_drawBitmap","(JJFFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
689    {"native_drawBitmap", "(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
690    {"nativeDrawBitmapMesh", "(JJII[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
691    {"native_drawText","(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
692    {"native_drawText","(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
693    {"native_drawTextRun","(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
694    {"native_drawTextRun","(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
695    {"native_drawTextOnPath","(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
696    {"native_drawTextOnPath","(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
697    {"nativeSetDrawFilter", "(JJ)V", (void*) CanvasJNI::setDrawFilter},
698    {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
699    {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
700};
701
702int register_android_graphics_Canvas(JNIEnv* env) {
703    return RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
704}
705
706}; // namespace android
707