android_graphics_Canvas.cpp revision 0dba1f611410e5075a910fb73ff3d3c703bbc5ce
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
547// Same values used by Skia
548#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
549#define kStdUnderline_Offset    (1.0f / 9.0f)
550#define kStdUnderline_Thickness (1.0f / 18.0f)
551
552void drawTextDecorations(Canvas* canvas, float x, float y, float length, const SkPaint& paint) {
553    uint32_t flags;
554    SkDrawFilter* drawFilter = canvas->getDrawFilter();
555    if (drawFilter) {
556        SkPaint paintCopy(paint);
557        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
558        flags = paintCopy.getFlags();
559    } else {
560        flags = paint.getFlags();
561    }
562    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
563        SkScalar left = x;
564        SkScalar right = x + length;
565        float textSize = paint.getTextSize();
566        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
567        if (flags & SkPaint::kUnderlineText_Flag) {
568            SkScalar top = y + textSize * kStdUnderline_Offset - 0.5f * strokeWidth;
569            SkScalar bottom = y + textSize * kStdUnderline_Offset + 0.5f * strokeWidth;
570            canvas->drawRect(left, top, right, bottom, paint);
571        }
572        if (flags & SkPaint::kStrikeThruText_Flag) {
573            SkScalar top = y + textSize * kStdStrikeThru_Offset - 0.5f * strokeWidth;
574            SkScalar bottom = y + textSize * kStdStrikeThru_Offset + 0.5f * strokeWidth;
575            canvas->drawRect(left, top, right, bottom, paint);
576        }
577    }
578}
579
580void drawText(Canvas* canvas, const uint16_t* text, int start, int count, int contextCount,
581             float x, float y, int bidiFlags, const Paint& origPaint, TypefaceImpl* typeface) {
582    // minikin may modify the original paint
583    Paint paint(origPaint);
584
585    Layout layout;
586    MinikinUtils::doLayout(&layout, &paint, bidiFlags, typeface, text, start, count, contextCount);
587
588    size_t nGlyphs = layout.nGlyphs();
589    uint16_t* glyphs = new uint16_t[nGlyphs];
590    float* pos = new float[nGlyphs * 2];
591
592    x += MinikinUtils::xOffsetForTextAlign(&paint, layout);
593
594    MinikinRect bounds;
595    layout.getBounds(&bounds);
596    if (!canvas->drawTextAbsolutePos()) {
597        bounds.offset(x, y);
598    }
599
600    DrawTextFunctor f(layout, canvas, glyphs, pos, paint, x, y, bounds, layout.getAdvance());
601    MinikinUtils::forFontRun(layout, &paint, f);
602
603    drawTextDecorations(canvas, x, y, layout.getAdvance(), paint);
604
605    delete[] glyphs;
606    delete[] pos;
607}
608
609static void drawTextChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
610                          jint index, jint count, jfloat x, jfloat y, jint bidiFlags,
611                          jlong paintHandle, jlong typefaceHandle) {
612    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
613    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
614    jchar* jchars = env->GetCharArrayElements(text, NULL);
615    drawText(get_canvas(canvasHandle), jchars + index, 0, count, count, x, y,
616                                       bidiFlags, *paint, typeface);
617    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
618}
619
620static void drawTextString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
621                           jint start, jint end, jfloat x, jfloat y, jint bidiFlags,
622                           jlong paintHandle, jlong typefaceHandle) {
623    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
624    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
625    const int count = end - start;
626    const jchar* jchars = env->GetStringChars(text, NULL);
627    drawText(get_canvas(canvasHandle), jchars + start, 0, count, count, x, y,
628                                       bidiFlags, *paint, typeface);
629    env->ReleaseStringChars(text, jchars);
630}
631
632static void drawTextRunChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text, jint index,
633                             jint count, jint contextIndex, jint contextCount, jfloat x, jfloat y,
634                             jboolean isRtl, jlong paintHandle, jlong typefaceHandle) {
635    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
636    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
637
638    const int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
639    jchar* jchars = env->GetCharArrayElements(text, NULL);
640    drawText(get_canvas(canvasHandle), jchars + contextIndex, index - contextIndex, count,
641                                       contextCount, x, y, bidiFlags, *paint, typeface);
642    env->ReleaseCharArrayElements(text, jchars, JNI_ABORT);
643}
644
645static void drawTextRunString(JNIEnv* env, jobject obj, jlong canvasHandle, jstring text,
646                              jint start, jint end, jint contextStart, jint contextEnd,
647                              jfloat x, jfloat y, jboolean isRtl, jlong paintHandle,
648                              jlong typefaceHandle) {
649    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
650    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
651
652    int bidiFlags = isRtl ? kBidi_Force_RTL : kBidi_Force_LTR;
653    jint count = end - start;
654    jint contextCount = contextEnd - contextStart;
655    const jchar* jchars = env->GetStringChars(text, NULL);
656    drawText(get_canvas(canvasHandle), jchars + contextStart, start - contextStart, count,
657                                       contextCount, x, y, bidiFlags, *paint, typeface);
658    env->ReleaseStringChars(text, jchars);
659}
660
661class DrawTextOnPathFunctor {
662public:
663    DrawTextOnPathFunctor(const Layout& layout, Canvas* canvas, float hOffset,
664                float vOffset, const Paint& paint, const SkPath& path)
665            : layout(layout), canvas(canvas), hOffset(hOffset), vOffset(vOffset),
666                paint(paint), path(path) {
667    }
668    void operator()(size_t start, size_t end) {
669        uint16_t glyphs[1];
670        for (size_t i = start; i < end; i++) {
671            glyphs[0] = layout.getGlyphId(i);
672            float x = hOffset + layout.getX(i);
673            float y = vOffset + layout.getY(i);
674            canvas->drawTextOnPath(glyphs, 1, path, x, y, paint);
675        }
676    }
677private:
678    const Layout& layout;
679    Canvas* canvas;
680    float hOffset;
681    float vOffset;
682    const Paint& paint;
683    const SkPath& path;
684};
685
686static void drawTextOnPath(Canvas* canvas, const uint16_t* text, int count, int bidiFlags,
687                           const SkPath& path, float hOffset, float vOffset,
688                           const Paint& paint, TypefaceImpl* typeface) {
689    Paint paintCopy(paint);
690    Layout layout;
691    MinikinUtils::doLayout(&layout, &paintCopy, bidiFlags, typeface, text, 0, count, count);
692    hOffset += MinikinUtils::hOffsetForTextAlign(&paintCopy, layout, path);
693
694    // Set align to left for drawing, as we don't want individual
695    // glyphs centered or right-aligned; the offset above takes
696    // care of all alignment.
697    paintCopy.setTextAlign(Paint::kLeft_Align);
698
699    DrawTextOnPathFunctor f(layout, canvas, hOffset, vOffset, paintCopy, path);
700    MinikinUtils::forFontRun(layout, &paintCopy, f);
701}
702
703static void drawTextOnPathChars(JNIEnv* env, jobject, jlong canvasHandle, jcharArray text,
704                                jint index, jint count, jlong pathHandle, jfloat hOffset,
705                                jfloat vOffset, jint bidiFlags, jlong paintHandle,
706                                jlong typefaceHandle) {
707    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
708    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
709    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
710
711    jchar* jchars = env->GetCharArrayElements(text, NULL);
712
713    drawTextOnPath(get_canvas(canvasHandle), jchars + index, count, bidiFlags, *path,
714                   hOffset, vOffset, *paint, typeface);
715
716    env->ReleaseCharArrayElements(text, jchars, 0);
717}
718
719static void drawTextOnPathString(JNIEnv* env, jobject, jlong canvasHandle, jstring text,
720                                 jlong pathHandle, jfloat hOffset, jfloat vOffset,
721                                 jint bidiFlags, jlong paintHandle, jlong typefaceHandle) {
722    SkPath* path = reinterpret_cast<SkPath*>(pathHandle);
723    Paint* paint = reinterpret_cast<Paint*>(paintHandle);
724    TypefaceImpl* typeface = reinterpret_cast<TypefaceImpl*>(typefaceHandle);
725
726    const jchar* jchars = env->GetStringChars(text, NULL);
727    int count = env->GetStringLength(text);
728
729    drawTextOnPath(get_canvas(canvasHandle), jchars, count, bidiFlags, *path,
730                   hOffset, vOffset, *paint, typeface);
731
732    env->ReleaseStringChars(text, jchars);
733}
734
735static void setDrawFilter(JNIEnv* env, jobject, jlong canvasHandle, jlong filterHandle) {
736    get_canvas(canvasHandle)->setDrawFilter(reinterpret_cast<SkDrawFilter*>(filterHandle));
737}
738
739static void freeCaches(JNIEnv* env, jobject) {
740    SkGraphics::PurgeFontCache();
741}
742
743static void freeTextLayoutCaches(JNIEnv* env, jobject) {
744    Layout::purgeCaches();
745}
746
747}; // namespace CanvasJNI
748
749static const JNINativeMethod gMethods[] = {
750    {"finalizer", "(J)V", (void*) CanvasJNI::finalizer},
751    {"initRaster", "(Landroid/graphics/Bitmap;)J", (void*) CanvasJNI::initRaster},
752    {"native_setBitmap", "!(JLandroid/graphics/Bitmap;)V", (void*) CanvasJNI::setBitmap},
753    {"native_isOpaque","!(J)Z", (void*) CanvasJNI::isOpaque},
754    {"native_getWidth","!(J)I", (void*) CanvasJNI::getWidth},
755    {"native_getHeight","!(J)I", (void*) CanvasJNI::getHeight},
756    {"native_setHighContrastText","!(JZ)V", (void*) CanvasJNI::setHighContrastText},
757    {"native_save","!(JI)I", (void*) CanvasJNI::save},
758    {"native_saveLayer","!(JFFFFJI)I", (void*) CanvasJNI::saveLayer},
759    {"native_saveLayerAlpha","!(JFFFFII)I", (void*) CanvasJNI::saveLayerAlpha},
760    {"native_getSaveCount","!(J)I", (void*) CanvasJNI::getSaveCount},
761    {"native_restore","!(JZ)V", (void*) CanvasJNI::restore},
762    {"native_restoreToCount","!(JIZ)V", (void*) CanvasJNI::restoreToCount},
763    {"native_getCTM", "!(JJ)V", (void*)CanvasJNI::getCTM},
764    {"native_setMatrix","!(JJ)V", (void*) CanvasJNI::setMatrix},
765    {"native_concat","!(JJ)V", (void*) CanvasJNI::concat},
766    {"native_rotate","!(JF)V", (void*) CanvasJNI::rotate},
767    {"native_scale","!(JFF)V", (void*) CanvasJNI::scale},
768    {"native_skew","!(JFF)V", (void*) CanvasJNI::skew},
769    {"native_translate","!(JFF)V", (void*) CanvasJNI::translate},
770    {"native_getClipBounds","!(JLandroid/graphics/Rect;)Z", (void*) CanvasJNI::getClipBounds},
771    {"native_quickReject","!(JJ)Z", (void*) CanvasJNI::quickRejectPath},
772    {"native_quickReject","!(JFFFF)Z", (void*)CanvasJNI::quickRejectRect},
773    {"native_clipRect","!(JFFFFI)Z", (void*) CanvasJNI::clipRect},
774    {"native_clipPath","!(JJI)Z", (void*) CanvasJNI::clipPath},
775    {"native_clipRegion","!(JJI)Z", (void*) CanvasJNI::clipRegion},
776    {"native_drawColor","!(JII)V", (void*) CanvasJNI::drawColor},
777    {"native_drawPaint","!(JJ)V", (void*) CanvasJNI::drawPaint},
778    {"native_drawPoint", "!(JFFJ)V", (void*) CanvasJNI::drawPoint},
779    {"native_drawPoints", "!(J[FIIJ)V", (void*) CanvasJNI::drawPoints},
780    {"native_drawLine", "!(JFFFFJ)V", (void*) CanvasJNI::drawLine},
781    {"native_drawLines", "!(J[FIIJ)V", (void*) CanvasJNI::drawLines},
782    {"native_drawRect","!(JFFFFJ)V", (void*) CanvasJNI::drawRect},
783    {"native_drawRegion", "!(JJJ)V", (void*) CanvasJNI::drawRegion },
784    {"native_drawRoundRect","!(JFFFFFFJ)V", (void*) CanvasJNI::drawRoundRect},
785    {"native_drawCircle","!(JFFFJ)V", (void*) CanvasJNI::drawCircle},
786    {"native_drawOval","!(JFFFFJ)V", (void*) CanvasJNI::drawOval},
787    {"native_drawArc","!(JFFFFFFZJ)V", (void*) CanvasJNI::drawArc},
788    {"native_drawPath","!(JJJ)V", (void*) CanvasJNI::drawPath},
789    {"nativeDrawVertices", "!(JII[FI[FI[II[SIIJ)V", (void*)CanvasJNI::drawVertices},
790    {"native_drawNinePatch", "!(JJJFFFFJII)V", (void*)CanvasJNI::drawNinePatch},
791    {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFJIII)V", (void*) CanvasJNI::drawBitmap},
792    {"nativeDrawBitmapMatrix", "!(JLandroid/graphics/Bitmap;JJ)V", (void*)CanvasJNI::drawBitmapMatrix},
793    {"native_drawBitmap","!(JLandroid/graphics/Bitmap;FFFFFFFFJII)V", (void*) CanvasJNI::drawBitmapRect},
794    {"native_drawBitmap", "!(J[IIIFFIIZJ)V", (void*)CanvasJNI::drawBitmapArray},
795    {"nativeDrawBitmapMesh", "!(JLandroid/graphics/Bitmap;II[FI[IIJ)V", (void*)CanvasJNI::drawBitmapMesh},
796    {"native_drawText","!(J[CIIFFIJJ)V", (void*) CanvasJNI::drawTextChars},
797    {"native_drawText","!(JLjava/lang/String;IIFFIJJ)V", (void*) CanvasJNI::drawTextString},
798    {"native_drawTextRun","!(J[CIIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunChars},
799    {"native_drawTextRun","!(JLjava/lang/String;IIIIFFZJJ)V", (void*) CanvasJNI::drawTextRunString},
800    {"native_drawTextOnPath","!(J[CIIJFFIJJ)V", (void*) CanvasJNI::drawTextOnPathChars},
801    {"native_drawTextOnPath","!(JLjava/lang/String;JFFIJJ)V", (void*) CanvasJNI::drawTextOnPathString},
802    {"nativeSetDrawFilter", "!(JJ)V", (void*) CanvasJNI::setDrawFilter},
803    {"freeCaches", "()V", (void*) CanvasJNI::freeCaches},
804    {"freeTextLayoutCaches", "()V", (void*) CanvasJNI::freeTextLayoutCaches}
805};
806
807int register_android_graphics_Canvas(JNIEnv* env) {
808    return RegisterMethodsOrDie(env, "android/graphics/Canvas", gMethods, NELEM(gMethods));
809}
810
811}; // namespace android
812