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