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