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