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