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