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