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