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