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