1/*
2 * Copyright (C) 2006-2007 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 <android_runtime/AndroidRuntime.h>
20
21#include "SkCanvas.h"
22#include "SkDevice.h"
23#include "SkDrawFilter.h"
24#include "SkGraphics.h"
25#include "SkImageRef_GlobalPool.h"
26#include "SkPorterDuff.h"
27#include "SkShader.h"
28#include "SkTemplates.h"
29
30#include "TextLayout.h"
31#include "TextLayoutCache.h"
32
33#include "unicode/ubidi.h"
34#include "unicode/ushape.h"
35
36#include <utils/Log.h>
37
38static uint32_t get_thread_msec() {
39#if defined(HAVE_POSIX_CLOCKS)
40    struct timespec tm;
41
42    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
43
44    return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000;
45#else
46    struct timeval tv;
47
48    gettimeofday(&tv, NULL);
49    return tv.tv_sec * 1000LL + tv.tv_usec / 1000;
50#endif
51}
52
53namespace android {
54
55class ClipCopier : public SkCanvas::ClipVisitor {
56public:
57    ClipCopier(SkCanvas* dstCanvas) : m_dstCanvas(dstCanvas) {}
58
59    virtual void clipRect(const SkRect& rect, SkRegion::Op op, bool antialias) {
60        m_dstCanvas->clipRect(rect, op, antialias);
61    }
62    virtual void clipPath(const SkPath& path, SkRegion::Op op, bool antialias) {
63        m_dstCanvas->clipPath(path, op, antialias);
64    }
65
66private:
67    SkCanvas* m_dstCanvas;
68};
69
70class SkCanvasGlue {
71public:
72
73    static void finalizer(JNIEnv* env, jobject clazz, SkCanvas* canvas) {
74        canvas->unref();
75    }
76
77    static SkCanvas* initRaster(JNIEnv* env, jobject, SkBitmap* bitmap) {
78        if (bitmap) {
79            return new SkCanvas(*bitmap);
80        } else {
81            // Create an empty bitmap device to prevent callers from crashing
82            // if they attempt to draw into this canvas.
83            SkBitmap emptyBitmap;
84            return new SkCanvas(emptyBitmap);
85        }
86    }
87
88    static void copyCanvasState(JNIEnv* env, jobject clazz,
89                                SkCanvas* srcCanvas, SkCanvas* dstCanvas) {
90        if (srcCanvas && dstCanvas) {
91            dstCanvas->setMatrix(srcCanvas->getTotalMatrix());
92            if (NULL != srcCanvas->getDevice() && NULL != dstCanvas->getDevice()) {
93                ClipCopier copier(dstCanvas);
94                srcCanvas->replayClips(&copier);
95            }
96        }
97    }
98
99
100    static void freeCaches(JNIEnv* env, jobject) {
101        // these are called in no particular order
102        SkImageRef_GlobalPool::SetRAMUsed(0);
103        SkGraphics::PurgeFontCache();
104    }
105
106    static void freeTextLayoutCaches(JNIEnv* env, jobject) {
107        TextLayoutEngine::getInstance().purgeCaches();
108    }
109
110    static jboolean isOpaque(JNIEnv* env, jobject jcanvas) {
111        NPE_CHECK_RETURN_ZERO(env, jcanvas);
112        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
113        return canvas->getDevice()->accessBitmap(false).isOpaque();
114    }
115
116    static int getWidth(JNIEnv* env, jobject jcanvas) {
117        NPE_CHECK_RETURN_ZERO(env, jcanvas);
118        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
119        return canvas->getDevice()->accessBitmap(false).width();
120    }
121
122    static int getHeight(JNIEnv* env, jobject jcanvas) {
123        NPE_CHECK_RETURN_ZERO(env, jcanvas);
124        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
125        return canvas->getDevice()->accessBitmap(false).height();
126    }
127
128    static int saveAll(JNIEnv* env, jobject jcanvas) {
129        NPE_CHECK_RETURN_ZERO(env, jcanvas);
130        return GraphicsJNI::getNativeCanvas(env, jcanvas)->save();
131    }
132
133    static int save(JNIEnv* env, jobject jcanvas, SkCanvas::SaveFlags flags) {
134        NPE_CHECK_RETURN_ZERO(env, jcanvas);
135        return GraphicsJNI::getNativeCanvas(env, jcanvas)->save(flags);
136    }
137
138    static int saveLayer(JNIEnv* env, jobject, SkCanvas* canvas, jobject bounds,
139                         SkPaint* paint, int flags) {
140        SkRect* bounds_ = NULL;
141        SkRect  storage;
142        if (bounds != NULL) {
143            GraphicsJNI::jrectf_to_rect(env, bounds, &storage);
144            bounds_ = &storage;
145        }
146        return canvas->saveLayer(bounds_, paint, (SkCanvas::SaveFlags)flags);
147    }
148
149    static int saveLayer4F(JNIEnv* env, jobject, SkCanvas* canvas,
150                           jfloat l, jfloat t, jfloat r, jfloat b,
151                           SkPaint* paint, int flags) {
152        SkRect bounds;
153        bounds.set(SkFloatToScalar(l), SkFloatToScalar(t), SkFloatToScalar(r),
154                   SkFloatToScalar(b));
155        return canvas->saveLayer(&bounds, paint, (SkCanvas::SaveFlags)flags);
156    }
157
158    static int saveLayerAlpha(JNIEnv* env, jobject, SkCanvas* canvas,
159                              jobject bounds, int alpha, int flags) {
160        SkRect* bounds_ = NULL;
161        SkRect  storage;
162        if (bounds != NULL) {
163            GraphicsJNI::jrectf_to_rect(env, bounds, &storage);
164            bounds_ = &storage;
165        }
166        return canvas->saveLayerAlpha(bounds_, alpha,
167                                      (SkCanvas::SaveFlags)flags);
168    }
169
170    static int saveLayerAlpha4F(JNIEnv* env, jobject, SkCanvas* canvas,
171                                jfloat l, jfloat t, jfloat r, jfloat b,
172                                int alpha, int flags) {
173        SkRect  bounds;
174        bounds.set(SkFloatToScalar(l), SkFloatToScalar(t), SkFloatToScalar(r),
175                   SkFloatToScalar(b));
176        return canvas->saveLayerAlpha(&bounds, alpha,
177                                      (SkCanvas::SaveFlags)flags);
178    }
179
180    static void restore(JNIEnv* env, jobject jcanvas) {
181        NPE_CHECK_RETURN_VOID(env, jcanvas);
182        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
183        if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
184            doThrowISE(env, "Underflow in restore");
185            return;
186        }
187        canvas->restore();
188    }
189
190    static int getSaveCount(JNIEnv* env, jobject jcanvas) {
191        NPE_CHECK_RETURN_ZERO(env, jcanvas);
192        return GraphicsJNI::getNativeCanvas(env, jcanvas)->getSaveCount();
193    }
194
195    static void restoreToCount(JNIEnv* env, jobject jcanvas, int restoreCount) {
196        NPE_CHECK_RETURN_VOID(env, jcanvas);
197        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
198        if (restoreCount < 1) {
199            doThrowIAE(env, "Underflow in restoreToCount");
200            return;
201        }
202        canvas->restoreToCount(restoreCount);
203    }
204
205    static void translate(JNIEnv* env, jobject jcanvas, jfloat dx, jfloat dy) {
206        NPE_CHECK_RETURN_VOID(env, jcanvas);
207        SkScalar dx_ = SkFloatToScalar(dx);
208        SkScalar dy_ = SkFloatToScalar(dy);
209        (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->translate(dx_, dy_);
210    }
211
212    static void scale__FF(JNIEnv* env, jobject jcanvas, jfloat sx, jfloat sy) {
213        NPE_CHECK_RETURN_VOID(env, jcanvas);
214        SkScalar sx_ = SkFloatToScalar(sx);
215        SkScalar sy_ = SkFloatToScalar(sy);
216        (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->scale(sx_, sy_);
217    }
218
219    static void rotate__F(JNIEnv* env, jobject jcanvas, jfloat degrees) {
220        NPE_CHECK_RETURN_VOID(env, jcanvas);
221        SkScalar degrees_ = SkFloatToScalar(degrees);
222        (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->rotate(degrees_);
223    }
224
225    static void skew__FF(JNIEnv* env, jobject jcanvas, jfloat sx, jfloat sy) {
226        NPE_CHECK_RETURN_VOID(env, jcanvas);
227        SkScalar sx_ = SkFloatToScalar(sx);
228        SkScalar sy_ = SkFloatToScalar(sy);
229        (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->skew(sx_, sy_);
230    }
231
232    static void concat(JNIEnv* env, jobject, SkCanvas* canvas,
233                       const SkMatrix* matrix) {
234        canvas->concat(*matrix);
235    }
236
237    static void setMatrix(JNIEnv* env, jobject, SkCanvas* canvas,
238                          const SkMatrix* matrix) {
239        if (NULL == matrix) {
240            canvas->resetMatrix();
241        } else {
242            canvas->setMatrix(*matrix);
243        }
244    }
245
246    static jboolean clipRect_FFFF(JNIEnv* env, jobject jcanvas, jfloat left,
247                                  jfloat top, jfloat right, jfloat bottom) {
248        NPE_CHECK_RETURN_ZERO(env, jcanvas);
249        SkRect  r;
250        r.set(SkFloatToScalar(left), SkFloatToScalar(top),
251              SkFloatToScalar(right), SkFloatToScalar(bottom));
252        SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas);
253        return c->clipRect(r);
254    }
255
256    static jboolean clipRect_IIII(JNIEnv* env, jobject jcanvas, jint left,
257                                  jint top, jint right, jint bottom) {
258        NPE_CHECK_RETURN_ZERO(env, jcanvas);
259        SkRect  r;
260        r.set(SkIntToScalar(left), SkIntToScalar(top),
261              SkIntToScalar(right), SkIntToScalar(bottom));
262        return GraphicsJNI::getNativeCanvas(env, jcanvas)->clipRect(r);
263    }
264
265    static jboolean clipRect_RectF(JNIEnv* env, jobject jcanvas, jobject rectf) {
266        NPE_CHECK_RETURN_ZERO(env, jcanvas);
267        NPE_CHECK_RETURN_ZERO(env, rectf);
268        SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas);
269        SkRect tmp;
270        return c->clipRect(*GraphicsJNI::jrectf_to_rect(env, rectf, &tmp));
271    }
272
273    static jboolean clipRect_Rect(JNIEnv* env, jobject jcanvas, jobject rect) {
274        NPE_CHECK_RETURN_ZERO(env, jcanvas);
275        NPE_CHECK_RETURN_ZERO(env, rect);
276        SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas);
277        SkRect tmp;
278        return c->clipRect(*GraphicsJNI::jrect_to_rect(env, rect, &tmp));
279    }
280
281    static jboolean clipRect(JNIEnv* env, jobject, SkCanvas* canvas,
282                             float left, float top, float right, float bottom,
283                             int op) {
284        SkRect rect;
285        rect.set(SkFloatToScalar(left), SkFloatToScalar(top),
286                 SkFloatToScalar(right), SkFloatToScalar(bottom));
287        return canvas->clipRect(rect, (SkRegion::Op)op);
288    }
289
290    static jboolean clipPath(JNIEnv* env, jobject, SkCanvas* canvas,
291                             SkPath* path, int op) {
292        return canvas->clipPath(*path, (SkRegion::Op)op);
293    }
294
295    static jboolean clipRegion(JNIEnv* env, jobject, SkCanvas* canvas,
296                               SkRegion* deviceRgn, int op) {
297        return canvas->clipRegion(*deviceRgn, (SkRegion::Op)op);
298    }
299
300    static void setDrawFilter(JNIEnv* env, jobject, SkCanvas* canvas,
301                              SkDrawFilter* filter) {
302        canvas->setDrawFilter(filter);
303    }
304
305    static jboolean quickReject__RectF(JNIEnv* env, jobject, SkCanvas* canvas,
306                                        jobject rect) {
307        SkRect rect_;
308        GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
309        return canvas->quickReject(rect_);
310    }
311
312    static jboolean quickReject__Path(JNIEnv* env, jobject, SkCanvas* canvas,
313                                       SkPath* path) {
314        return canvas->quickReject(*path);
315    }
316
317    static jboolean quickReject__FFFF(JNIEnv* env, jobject, SkCanvas* canvas,
318                                       jfloat left, jfloat top, jfloat right,
319                                       jfloat bottom) {
320        SkRect r;
321        r.set(SkFloatToScalar(left), SkFloatToScalar(top),
322              SkFloatToScalar(right), SkFloatToScalar(bottom));
323        return canvas->quickReject(r);
324    }
325
326    static void drawRGB(JNIEnv* env, jobject, SkCanvas* canvas,
327                        jint r, jint g, jint b) {
328        canvas->drawARGB(0xFF, r, g, b);
329    }
330
331    static void drawARGB(JNIEnv* env, jobject, SkCanvas* canvas,
332                         jint a, jint r, jint g, jint b) {
333        canvas->drawARGB(a, r, g, b);
334    }
335
336    static void drawColor__I(JNIEnv* env, jobject, SkCanvas* canvas,
337                             jint color) {
338        canvas->drawColor(color);
339    }
340
341    static void drawColor__II(JNIEnv* env, jobject, SkCanvas* canvas,
342                              jint color, SkPorterDuff::Mode mode) {
343        canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
344    }
345
346    static void drawPaint(JNIEnv* env, jobject, SkCanvas* canvas,
347                          SkPaint* paint) {
348        canvas->drawPaint(*paint);
349    }
350
351    static void doPoints(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray,
352                         jint offset, jint count, jobject jpaint,
353                         SkCanvas::PointMode mode) {
354        NPE_CHECK_RETURN_VOID(env, jcanvas);
355        NPE_CHECK_RETURN_VOID(env, jptsArray);
356        NPE_CHECK_RETURN_VOID(env, jpaint);
357        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
358        const SkPaint& paint = *GraphicsJNI::getNativePaint(env, jpaint);
359
360        AutoJavaFloatArray autoPts(env, jptsArray);
361        float* floats = autoPts.ptr();
362        const int length = autoPts.length();
363
364        if ((offset | count) < 0 || offset + count > length) {
365            doThrowAIOOBE(env);
366            return;
367        }
368
369        // now convert the floats into SkPoints
370        count >>= 1;    // now it is the number of points
371        SkAutoSTMalloc<32, SkPoint> storage(count);
372        SkPoint* pts = storage.get();
373        const float* src = floats + offset;
374        for (int i = 0; i < count; i++) {
375            pts[i].set(SkFloatToScalar(src[0]), SkFloatToScalar(src[1]));
376            src += 2;
377        }
378        canvas->drawPoints(mode, count, pts, paint);
379    }
380
381    static void drawPoints(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray,
382                           jint offset, jint count, jobject jpaint) {
383        doPoints(env, jcanvas, jptsArray, offset, count, jpaint,
384                 SkCanvas::kPoints_PointMode);
385    }
386
387    static void drawLines(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray,
388                           jint offset, jint count, jobject jpaint) {
389        doPoints(env, jcanvas, jptsArray, offset, count, jpaint,
390                 SkCanvas::kLines_PointMode);
391    }
392
393    static void drawPoint(JNIEnv* env, jobject jcanvas, float x, float y,
394                          jobject jpaint) {
395        NPE_CHECK_RETURN_VOID(env, jcanvas);
396        NPE_CHECK_RETURN_VOID(env, jpaint);
397        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
398        const SkPaint& paint = *GraphicsJNI::getNativePaint(env, jpaint);
399
400        canvas->drawPoint(SkFloatToScalar(x), SkFloatToScalar(y), paint);
401    }
402
403    static void drawLine__FFFFPaint(JNIEnv* env, jobject, SkCanvas* canvas,
404                                    jfloat startX, jfloat startY, jfloat stopX,
405                                    jfloat stopY, SkPaint* paint) {
406        canvas->drawLine(SkFloatToScalar(startX), SkFloatToScalar(startY),
407                         SkFloatToScalar(stopX), SkFloatToScalar(stopY),
408                         *paint);
409    }
410
411    static void drawRect__RectFPaint(JNIEnv* env, jobject, SkCanvas* canvas,
412                                     jobject rect, SkPaint* paint) {
413        SkRect rect_;
414        GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
415        canvas->drawRect(rect_, *paint);
416    }
417
418    static void drawRect__FFFFPaint(JNIEnv* env, jobject, SkCanvas* canvas,
419                                    jfloat left, jfloat top, jfloat right,
420                                    jfloat bottom, SkPaint* paint) {
421        SkScalar left_ = SkFloatToScalar(left);
422        SkScalar top_ = SkFloatToScalar(top);
423        SkScalar right_ = SkFloatToScalar(right);
424        SkScalar bottom_ = SkFloatToScalar(bottom);
425        canvas->drawRectCoords(left_, top_, right_, bottom_, *paint);
426    }
427
428    static void drawOval(JNIEnv* env, jobject, SkCanvas* canvas, jobject joval,
429                         SkPaint* paint) {
430        SkRect oval;
431        GraphicsJNI::jrectf_to_rect(env, joval, &oval);
432        canvas->drawOval(oval, *paint);
433    }
434
435    static void drawCircle(JNIEnv* env, jobject, SkCanvas* canvas, jfloat cx,
436                           jfloat cy, jfloat radius, SkPaint* paint) {
437        canvas->drawCircle(SkFloatToScalar(cx), SkFloatToScalar(cy),
438                           SkFloatToScalar(radius), *paint);
439    }
440
441    static void drawArc(JNIEnv* env, jobject, SkCanvas* canvas, jobject joval,
442                        jfloat startAngle, jfloat sweepAngle,
443                        jboolean useCenter, SkPaint* paint) {
444        SkRect oval;
445        GraphicsJNI::jrectf_to_rect(env, joval, &oval);
446        canvas->drawArc(oval, SkFloatToScalar(startAngle),
447                        SkFloatToScalar(sweepAngle), useCenter, *paint);
448    }
449
450    static void drawRoundRect(JNIEnv* env, jobject, SkCanvas* canvas,
451                              jobject jrect, jfloat rx, jfloat ry,
452                              SkPaint* paint) {
453        SkRect rect;
454        GraphicsJNI::jrectf_to_rect(env, jrect, &rect);
455        canvas->drawRoundRect(rect, SkFloatToScalar(rx), SkFloatToScalar(ry),
456                              *paint);
457    }
458
459    static void drawPath(JNIEnv* env, jobject, SkCanvas* canvas, SkPath* path,
460                         SkPaint* paint) {
461        canvas->drawPath(*path, *paint);
462    }
463
464    static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas,
465                                          SkCanvas* canvas, SkBitmap* bitmap,
466                                          jfloat left, jfloat top,
467                                          SkPaint* paint, jint canvasDensity,
468                                          jint screenDensity, jint bitmapDensity) {
469        SkScalar left_ = SkFloatToScalar(left);
470        SkScalar top_ = SkFloatToScalar(top);
471
472        if (canvasDensity == bitmapDensity || canvasDensity == 0
473                || bitmapDensity == 0) {
474            if (screenDensity != 0 && screenDensity != bitmapDensity) {
475                SkPaint filteredPaint;
476                if (paint) {
477                    filteredPaint = *paint;
478                }
479                filteredPaint.setFilterBitmap(true);
480                canvas->drawBitmap(*bitmap, left_, top_, &filteredPaint);
481            } else {
482                canvas->drawBitmap(*bitmap, left_, top_, paint);
483            }
484        } else {
485            canvas->save();
486            SkScalar scale = SkFloatToScalar(canvasDensity / (float)bitmapDensity);
487            canvas->translate(left_, top_);
488            canvas->scale(scale, scale);
489
490            SkPaint filteredPaint;
491            if (paint) {
492                filteredPaint = *paint;
493            }
494            filteredPaint.setFilterBitmap(true);
495
496            canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
497
498            canvas->restore();
499        }
500    }
501
502    static void doDrawBitmap(JNIEnv* env, SkCanvas* canvas, SkBitmap* bitmap,
503                        jobject srcIRect, const SkRect& dst, SkPaint* paint,
504                        jint screenDensity, jint bitmapDensity) {
505        SkIRect    src, *srcPtr = NULL;
506
507        if (NULL != srcIRect) {
508            GraphicsJNI::jrect_to_irect(env, srcIRect, &src);
509            srcPtr = &src;
510        }
511
512        if (screenDensity != 0 && screenDensity != bitmapDensity) {
513            SkPaint filteredPaint;
514            if (paint) {
515                filteredPaint = *paint;
516            }
517            filteredPaint.setFilterBitmap(true);
518            canvas->drawBitmapRect(*bitmap, srcPtr, dst, &filteredPaint);
519        } else {
520            canvas->drawBitmapRect(*bitmap, srcPtr, dst, paint);
521        }
522    }
523
524    static void drawBitmapRF(JNIEnv* env, jobject, SkCanvas* canvas,
525                             SkBitmap* bitmap, jobject srcIRect,
526                             jobject dstRectF, SkPaint* paint,
527                             jint screenDensity, jint bitmapDensity) {
528        SkRect      dst;
529        GraphicsJNI::jrectf_to_rect(env, dstRectF, &dst);
530        doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
531                screenDensity, bitmapDensity);
532    }
533
534    static void drawBitmapRR(JNIEnv* env, jobject, SkCanvas* canvas,
535                             SkBitmap* bitmap, jobject srcIRect,
536                             jobject dstRect, SkPaint* paint,
537                             jint screenDensity, jint bitmapDensity) {
538        SkRect      dst;
539        GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
540        doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
541                screenDensity, bitmapDensity);
542    }
543
544    static void drawBitmapArray(JNIEnv* env, jobject, SkCanvas* canvas,
545                                jintArray jcolors, int offset, int stride,
546                                jfloat x, jfloat y, int width, int height,
547                                jboolean hasAlpha, SkPaint* paint)
548    {
549        SkBitmap    bitmap;
550        bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config :
551                         SkBitmap::kRGB_565_Config, width, height);
552        if (!bitmap.allocPixels()) {
553            return;
554        }
555
556        if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride,
557                0, 0, width, height, bitmap, true)) {
558            return;
559        }
560
561        canvas->drawBitmap(bitmap, SkFloatToScalar(x), SkFloatToScalar(y),
562                           paint);
563    }
564
565    static void drawBitmapMatrix(JNIEnv* env, jobject, SkCanvas* canvas,
566                                 const SkBitmap* bitmap, const SkMatrix* matrix,
567                                 const SkPaint* paint) {
568        canvas->drawBitmapMatrix(*bitmap, *matrix, paint);
569    }
570
571    static void drawBitmapMesh(JNIEnv* env, jobject, SkCanvas* canvas,
572                          const SkBitmap* bitmap, int meshWidth, int meshHeight,
573                          jfloatArray jverts, int vertIndex, jintArray jcolors,
574                          int colorIndex, const SkPaint* paint) {
575
576        const int ptCount = (meshWidth + 1) * (meshHeight + 1);
577        const int indexCount = meshWidth * meshHeight * 6;
578
579        AutoJavaFloatArray  vertA(env, jverts, vertIndex + (ptCount << 1));
580        AutoJavaIntArray    colorA(env, jcolors, colorIndex + ptCount);
581
582        /*  Our temp storage holds 2 or 3 arrays.
583            texture points [ptCount * sizeof(SkPoint)]
584            optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
585                copy to convert from float to fixed
586            indices [ptCount * sizeof(uint16_t)]
587        */
588        ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
589#ifdef SK_SCALAR_IS_FIXED
590        storageSize += ptCount * sizeof(SkPoint);  // storage for verts
591#endif
592        storageSize += indexCount * sizeof(uint16_t);  // indices[]
593
594        SkAutoMalloc storage(storageSize);
595        SkPoint* texs = (SkPoint*)storage.get();
596        SkPoint* verts;
597        uint16_t* indices;
598#ifdef SK_SCALAR_IS_FLOAT
599        verts = (SkPoint*)(vertA.ptr() + vertIndex);
600        indices = (uint16_t*)(texs + ptCount);
601#else
602        verts = texs + ptCount;
603        indices = (uint16_t*)(verts + ptCount);
604        // convert floats to fixed
605        {
606            const float* src = vertA.ptr() + vertIndex;
607            for (int i = 0; i < ptCount; i++) {
608                verts[i].set(SkFloatToFixed(src[0]), SkFloatToFixed(src[1]));
609                src += 2;
610            }
611        }
612#endif
613
614        // cons up texture coordinates and indices
615        {
616            const SkScalar w = SkIntToScalar(bitmap->width());
617            const SkScalar h = SkIntToScalar(bitmap->height());
618            const SkScalar dx = w / meshWidth;
619            const SkScalar dy = h / meshHeight;
620
621            SkPoint* texsPtr = texs;
622            SkScalar y = 0;
623            for (int i = 0; i <= meshHeight; i++) {
624                if (i == meshHeight) {
625                    y = h;  // to ensure numerically we hit h exactly
626                }
627                SkScalar x = 0;
628                for (int j = 0; j < meshWidth; j++) {
629                    texsPtr->set(x, y);
630                    texsPtr += 1;
631                    x += dx;
632                }
633                texsPtr->set(w, y);
634                texsPtr += 1;
635                y += dy;
636            }
637            SkASSERT(texsPtr - texs == ptCount);
638        }
639
640        // cons up indices
641        {
642            uint16_t* indexPtr = indices;
643            int index = 0;
644            for (int i = 0; i < meshHeight; i++) {
645                for (int j = 0; j < meshWidth; j++) {
646                    // lower-left triangle
647                    *indexPtr++ = index;
648                    *indexPtr++ = index + meshWidth + 1;
649                    *indexPtr++ = index + meshWidth + 2;
650                    // upper-right triangle
651                    *indexPtr++ = index;
652                    *indexPtr++ = index + meshWidth + 2;
653                    *indexPtr++ = index + 1;
654                    // bump to the next cell
655                    index += 1;
656                }
657                // bump to the next row
658                index += 1;
659            }
660            SkASSERT(indexPtr - indices == indexCount);
661            SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
662        }
663
664        // double-check that we have legal indices
665#ifdef SK_DEBUG
666        {
667            for (int i = 0; i < indexCount; i++) {
668                SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
669            }
670        }
671#endif
672
673        // cons-up a shader for the bitmap
674        SkPaint tmpPaint;
675        if (paint) {
676            tmpPaint = *paint;
677        }
678        SkShader* shader = SkShader::CreateBitmapShader(*bitmap,
679                        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
680        SkSafeUnref(tmpPaint.setShader(shader));
681
682        canvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, verts,
683                             texs, (const SkColor*)colorA.ptr(), NULL, indices,
684                             indexCount, tmpPaint);
685    }
686
687    static void drawVertices(JNIEnv* env, jobject, SkCanvas* canvas,
688                             SkCanvas::VertexMode mode, int vertexCount,
689                             jfloatArray jverts, int vertIndex,
690                             jfloatArray jtexs, int texIndex,
691                             jintArray jcolors, int colorIndex,
692                             jshortArray jindices, int indexIndex,
693                             int indexCount, const SkPaint* paint) {
694
695        AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
696        AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
697        AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
698        AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
699
700        const int ptCount = vertexCount >> 1;
701
702        SkPoint* verts;
703        SkPoint* texs = NULL;
704#ifdef SK_SCALAR_IS_FLOAT
705        verts = (SkPoint*)(vertA.ptr() + vertIndex);
706        if (jtexs != NULL) {
707            texs = (SkPoint*)(texA.ptr() + texIndex);
708        }
709#else
710        int count = ptCount;    // for verts
711        if (jtexs != NULL) {
712            count += ptCount;   // += for texs
713        }
714        SkAutoMalloc storage(count * sizeof(SkPoint));
715        verts = (SkPoint*)storage.get();
716        const float* src = vertA.ptr() + vertIndex;
717        for (int i = 0; i < ptCount; i++) {
718            verts[i].set(SkFloatToFixed(src[0]), SkFloatToFixed(src[1]));
719            src += 2;
720        }
721        if (jtexs != NULL) {
722            texs = verts + ptCount;
723            src = texA.ptr() + texIndex;
724            for (int i = 0; i < ptCount; i++) {
725                texs[i].set(SkFloatToFixed(src[0]), SkFloatToFixed(src[1]));
726                src += 2;
727            }
728        }
729#endif
730
731        const SkColor* colors = NULL;
732        const uint16_t* indices = NULL;
733        if (jcolors != NULL) {
734            colors = (const SkColor*)(colorA.ptr() + colorIndex);
735        }
736        if (jindices != NULL) {
737            indices = (const uint16_t*)(indexA.ptr() + indexIndex);
738        }
739
740        canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL,
741                             indices, indexCount, *paint);
742    }
743
744
745    static void drawText___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
746                                      jcharArray text, int index, int count,
747                                      jfloat x, jfloat y, int flags, SkPaint* paint) {
748        jchar* textArray = env->GetCharArrayElements(text, NULL);
749        drawTextWithGlyphs(canvas, textArray + index, 0, count, x, y, flags, paint);
750        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
751    }
752
753    static void drawText__StringIIFFIPaint(JNIEnv* env, jobject,
754                                          SkCanvas* canvas, jstring text,
755                                          int start, int end,
756                                          jfloat x, jfloat y, int flags, SkPaint* paint) {
757        const jchar* textArray = env->GetStringChars(text, NULL);
758        drawTextWithGlyphs(canvas, textArray, start, end, x, y, flags, paint);
759        env->ReleaseStringChars(text, textArray);
760    }
761
762    static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
763            int start, int end,
764            jfloat x, jfloat y, int flags, SkPaint* paint) {
765
766        jint count = end - start;
767        drawTextWithGlyphs(canvas, textArray + start, 0, count, count, x, y, flags, paint);
768    }
769
770    static void drawTextWithGlyphs(SkCanvas* canvas, const jchar* textArray,
771            int start, int count, int contextCount,
772            jfloat x, jfloat y, int flags, SkPaint* paint) {
773
774        sp<TextLayoutValue> value = TextLayoutEngine::getInstance().getValue(paint,
775                textArray, start, count, contextCount, flags);
776        if (value == NULL) {
777            return;
778        }
779        SkPaint::Align align = paint->getTextAlign();
780        if (align == SkPaint::kCenter_Align) {
781            x -= 0.5 * value->getTotalAdvance();
782        } else if (align == SkPaint::kRight_Align) {
783            x -= value->getTotalAdvance();
784        }
785        paint->setTextAlign(SkPaint::kLeft_Align);
786        doDrawGlyphsPos(canvas, value->getGlyphs(), value->getPos(), 0, value->getGlyphsCount(), x, y, flags, paint);
787        doDrawTextDecorations(canvas, x, y, value->getTotalAdvance(), paint);
788        paint->setTextAlign(align);
789    }
790
791// Same values used by Skia
792#define kStdStrikeThru_Offset   (-6.0f / 21.0f)
793#define kStdUnderline_Offset    (1.0f / 9.0f)
794#define kStdUnderline_Thickness (1.0f / 18.0f)
795
796static void doDrawTextDecorations(SkCanvas* canvas, jfloat x, jfloat y, jfloat length, SkPaint* paint) {
797    uint32_t flags;
798    SkDrawFilter* drawFilter = canvas->getDrawFilter();
799    if (drawFilter) {
800        SkPaint paintCopy(*paint);
801        drawFilter->filter(&paintCopy, SkDrawFilter::kText_Type);
802        flags = paintCopy.getFlags();
803    } else {
804        flags = paint->getFlags();
805    }
806    if (flags & (SkPaint::kUnderlineText_Flag | SkPaint::kStrikeThruText_Flag)) {
807        SkScalar left = SkFloatToScalar(x);
808        SkScalar right = SkFloatToScalar(x + length);
809        float textSize = paint->getTextSize();
810        float strokeWidth = fmax(textSize * kStdUnderline_Thickness, 1.0f);
811        if (flags & SkPaint::kUnderlineText_Flag) {
812            SkScalar top = SkFloatToScalar(y + textSize * kStdUnderline_Offset
813                    - 0.5f * strokeWidth);
814            SkScalar bottom = SkFloatToScalar(y + textSize * kStdUnderline_Offset
815                    + 0.5f * strokeWidth);
816            canvas->drawRectCoords(left, top, right, bottom, *paint);
817        }
818        if (flags & SkPaint::kStrikeThruText_Flag) {
819            SkScalar top = SkFloatToScalar(y + textSize * kStdStrikeThru_Offset
820                    - 0.5f * strokeWidth);
821            SkScalar bottom = SkFloatToScalar(y + textSize * kStdStrikeThru_Offset
822                    + 0.5f * strokeWidth);
823            canvas->drawRectCoords(left, top, right, bottom, *paint);
824        }
825    }
826}
827
828    static void doDrawGlyphs(SkCanvas* canvas, const jchar* glyphArray, int index, int count,
829            jfloat x, jfloat y, int flags, SkPaint* paint) {
830        // Beware: this needs Glyph encoding (already done on the Paint constructor)
831        canvas->drawText(glyphArray + index * 2, count * 2, x, y, *paint);
832    }
833
834    static void doDrawGlyphsPos(SkCanvas* canvas, const jchar* glyphArray, const jfloat* posArray,
835            int index, int count, jfloat x, jfloat y, int flags, SkPaint* paint) {
836        SkPoint* posPtr = new SkPoint[count];
837        for (int indx = 0; indx < count; indx++) {
838            posPtr[indx].fX = SkFloatToScalar(x + posArray[indx * 2]);
839            posPtr[indx].fY = SkFloatToScalar(y + posArray[indx * 2 + 1]);
840        }
841        canvas->drawPosText(glyphArray, count << 1, posPtr, *paint);
842        delete[] posPtr;
843    }
844
845    static void drawTextRun___CIIIIFFIPaint(
846        JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index,
847        int count, int contextIndex, int contextCount,
848        jfloat x, jfloat y, int dirFlags, SkPaint* paint) {
849
850        jchar* chars = env->GetCharArrayElements(text, NULL);
851        drawTextWithGlyphs(canvas, chars + contextIndex, index - contextIndex,
852                count, contextCount, x, y, dirFlags, paint);
853        env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
854    }
855
856    static void drawTextRun__StringIIIIFFIPaint(
857        JNIEnv* env, jobject obj, SkCanvas* canvas, jstring text, jint start,
858        jint end, jint contextStart, jint contextEnd,
859        jfloat x, jfloat y, jint dirFlags, SkPaint* paint) {
860
861        jint count = end - start;
862        jint contextCount = contextEnd - contextStart;
863        const jchar* chars = env->GetStringChars(text, NULL);
864        drawTextWithGlyphs(canvas, chars + contextStart, start - contextStart,
865                count, contextCount, x, y, dirFlags, paint);
866        env->ReleaseStringChars(text, chars);
867    }
868
869    static void drawPosText___CII_FPaint(JNIEnv* env, jobject, SkCanvas* canvas,
870                                         jcharArray text, int index, int count,
871                                         jfloatArray pos, SkPaint* paint) {
872        jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL;
873        jsize textCount = text ? env->GetArrayLength(text) : NULL;
874        float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
875        int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
876        SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
877        int indx;
878        for (indx = 0; indx < posCount; indx++) {
879            posPtr[indx].fX = SkFloatToScalar(posArray[indx << 1]);
880            posPtr[indx].fY = SkFloatToScalar(posArray[(indx << 1) + 1]);
881        }
882
883        SkPaint::TextEncoding encoding = paint->getTextEncoding();
884        paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
885        canvas->drawPosText(textArray + index, count << 1, posPtr, *paint);
886        paint->setTextEncoding(encoding);
887
888        if (text) {
889            env->ReleaseCharArrayElements(text, textArray, 0);
890        }
891        if (pos) {
892            env->ReleaseFloatArrayElements(pos, posArray, 0);
893        }
894        delete[] posPtr;
895    }
896
897    static void drawPosText__String_FPaint(JNIEnv* env, jobject,
898                                           SkCanvas* canvas, jstring text,
899                                           jfloatArray pos,
900                                           SkPaint* paint) {
901        const void* text_ = text ? env->GetStringChars(text, NULL) : NULL;
902        int byteLength = text ? env->GetStringLength(text) : 0;
903        float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
904        int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
905        SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
906
907        for (int indx = 0; indx < posCount; indx++) {
908            posPtr[indx].fX = SkFloatToScalar(posArray[indx << 1]);
909            posPtr[indx].fY = SkFloatToScalar(posArray[(indx << 1) + 1]);
910        }
911
912        SkPaint::TextEncoding encoding = paint->getTextEncoding();
913        paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
914        canvas->drawPosText(text_, byteLength << 1, posPtr, *paint);
915        paint->setTextEncoding(encoding);
916
917        if (text) {
918            env->ReleaseStringChars(text, (const jchar*) text_);
919        }
920        if (pos) {
921            env->ReleaseFloatArrayElements(pos, posArray, 0);
922        }
923        delete[] posPtr;
924    }
925
926    static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
927            SkCanvas* canvas, jcharArray text, int index, int count,
928            SkPath* path, jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
929
930        jchar* textArray = env->GetCharArrayElements(text, NULL);
931        TextLayout::drawTextOnPath(paint, textArray + index, count, bidiFlags, hOffset, vOffset,
932                                   path, canvas);
933        env->ReleaseCharArrayElements(text, textArray, 0);
934    }
935
936    static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
937            SkCanvas* canvas, jstring text, SkPath* path,
938            jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
939        const jchar* text_ = env->GetStringChars(text, NULL);
940        int count = env->GetStringLength(text);
941        TextLayout::drawTextOnPath(paint, text_, count, bidiFlags, hOffset, vOffset,
942                                   path, canvas);
943        env->ReleaseStringChars(text, text_);
944    }
945
946
947    // This function is a mirror of SkCanvas::getClipBounds except that it does
948    // not outset the edge of the clip to account for anti-aliasing. There is
949    // a skia bug to investigate pushing this logic into back into skia.
950    // (see https://code.google.com/p/skia/issues/detail?id=1303)
951    static bool getHardClipBounds(SkCanvas* canvas, SkRect* bounds) {
952        SkIRect ibounds;
953        if (!canvas->getClipDeviceBounds(&ibounds)) {
954            return false;
955        }
956
957        SkMatrix inverse;
958        // if we can't invert the CTM, we can't return local clip bounds
959        if (!canvas->getTotalMatrix().invert(&inverse)) {
960            if (bounds) {
961                bounds->setEmpty();
962            }
963            return false;
964        }
965
966        if (NULL != bounds) {
967            SkRect r = SkRect::Make(ibounds);
968            inverse.mapRect(bounds, r);
969        }
970        return true;
971    }
972
973    static bool getClipBounds(JNIEnv* env, jobject, SkCanvas* canvas,
974                              jobject bounds) {
975        SkRect   r;
976        SkIRect ir;
977        bool result = getHardClipBounds(canvas, &r);
978
979        if (!result) {
980            r.setEmpty();
981        }
982        r.round(&ir);
983
984        (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
985        return result;
986    }
987
988    static void getCTM(JNIEnv* env, jobject, SkCanvas* canvas,
989                       SkMatrix* matrix) {
990        *matrix = canvas->getTotalMatrix();
991    }
992};
993
994static JNINativeMethod gCanvasMethods[] = {
995    {"finalizer", "(I)V", (void*) SkCanvasGlue::finalizer},
996    {"initRaster","(I)I", (void*) SkCanvasGlue::initRaster},
997    {"copyNativeCanvasState","(II)V", (void*) SkCanvasGlue::copyCanvasState},
998    {"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque},
999    {"getWidth","()I", (void*) SkCanvasGlue::getWidth},
1000    {"getHeight","()I", (void*) SkCanvasGlue::getHeight},
1001    {"save","()I", (void*) SkCanvasGlue::saveAll},
1002    {"save","(I)I", (void*) SkCanvasGlue::save},
1003    {"native_saveLayer","(ILandroid/graphics/RectF;II)I",
1004        (void*) SkCanvasGlue::saveLayer},
1005    {"native_saveLayer","(IFFFFII)I", (void*) SkCanvasGlue::saveLayer4F},
1006    {"native_saveLayerAlpha","(ILandroid/graphics/RectF;II)I",
1007        (void*) SkCanvasGlue::saveLayerAlpha},
1008    {"native_saveLayerAlpha","(IFFFFII)I",
1009        (void*) SkCanvasGlue::saveLayerAlpha4F},
1010    {"restore","()V", (void*) SkCanvasGlue::restore},
1011    {"getSaveCount","()I", (void*) SkCanvasGlue::getSaveCount},
1012    {"restoreToCount","(I)V", (void*) SkCanvasGlue::restoreToCount},
1013    {"translate","(FF)V", (void*) SkCanvasGlue::translate},
1014    {"scale","(FF)V", (void*) SkCanvasGlue::scale__FF},
1015    {"rotate","(F)V", (void*) SkCanvasGlue::rotate__F},
1016    {"skew","(FF)V", (void*) SkCanvasGlue::skew__FF},
1017    {"native_concat","(II)V", (void*) SkCanvasGlue::concat},
1018    {"native_setMatrix","(II)V", (void*) SkCanvasGlue::setMatrix},
1019    {"clipRect","(FFFF)Z", (void*) SkCanvasGlue::clipRect_FFFF},
1020    {"clipRect","(IIII)Z", (void*) SkCanvasGlue::clipRect_IIII},
1021    {"clipRect","(Landroid/graphics/RectF;)Z",
1022        (void*) SkCanvasGlue::clipRect_RectF},
1023    {"clipRect","(Landroid/graphics/Rect;)Z",
1024        (void*) SkCanvasGlue::clipRect_Rect},
1025    {"native_clipRect","(IFFFFI)Z", (void*) SkCanvasGlue::clipRect},
1026    {"native_clipPath","(III)Z", (void*) SkCanvasGlue::clipPath},
1027    {"native_clipRegion","(III)Z", (void*) SkCanvasGlue::clipRegion},
1028    {"nativeSetDrawFilter", "(II)V", (void*) SkCanvasGlue::setDrawFilter},
1029    {"native_getClipBounds","(ILandroid/graphics/Rect;)Z",
1030        (void*) SkCanvasGlue::getClipBounds},
1031    {"native_getCTM", "(II)V", (void*)SkCanvasGlue::getCTM},
1032    {"native_quickReject","(ILandroid/graphics/RectF;)Z",
1033        (void*) SkCanvasGlue::quickReject__RectF},
1034    {"native_quickReject","(II)Z", (void*) SkCanvasGlue::quickReject__Path},
1035    {"native_quickReject","(IFFFF)Z", (void*)SkCanvasGlue::quickReject__FFFF},
1036    {"native_drawRGB","(IIII)V", (void*) SkCanvasGlue::drawRGB},
1037    {"native_drawARGB","(IIIII)V", (void*) SkCanvasGlue::drawARGB},
1038    {"native_drawColor","(II)V", (void*) SkCanvasGlue::drawColor__I},
1039    {"native_drawColor","(III)V", (void*) SkCanvasGlue::drawColor__II},
1040    {"native_drawPaint","(II)V", (void*) SkCanvasGlue::drawPaint},
1041    {"drawPoint", "(FFLandroid/graphics/Paint;)V",
1042    (void*) SkCanvasGlue::drawPoint},
1043    {"drawPoints", "([FIILandroid/graphics/Paint;)V",
1044        (void*) SkCanvasGlue::drawPoints},
1045    {"drawLines", "([FIILandroid/graphics/Paint;)V",
1046        (void*) SkCanvasGlue::drawLines},
1047    {"native_drawLine","(IFFFFI)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
1048    {"native_drawRect","(ILandroid/graphics/RectF;I)V",
1049        (void*) SkCanvasGlue::drawRect__RectFPaint},
1050    {"native_drawRect","(IFFFFI)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
1051    {"native_drawOval","(ILandroid/graphics/RectF;I)V",
1052        (void*) SkCanvasGlue::drawOval},
1053    {"native_drawCircle","(IFFFI)V", (void*) SkCanvasGlue::drawCircle},
1054    {"native_drawArc","(ILandroid/graphics/RectF;FFZI)V",
1055        (void*) SkCanvasGlue::drawArc},
1056    {"native_drawRoundRect","(ILandroid/graphics/RectF;FFI)V",
1057        (void*) SkCanvasGlue::drawRoundRect},
1058    {"native_drawPath","(III)V", (void*) SkCanvasGlue::drawPath},
1059    {"native_drawBitmap","(IIFFIIII)V",
1060        (void*) SkCanvasGlue::drawBitmap__BitmapFFPaint},
1061    {"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/RectF;III)V",
1062        (void*) SkCanvasGlue::drawBitmapRF},
1063    {"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/Rect;III)V",
1064        (void*) SkCanvasGlue::drawBitmapRR},
1065    {"native_drawBitmap", "(I[IIIFFIIZI)V",
1066    (void*)SkCanvasGlue::drawBitmapArray},
1067    {"nativeDrawBitmapMatrix", "(IIII)V",
1068        (void*)SkCanvasGlue::drawBitmapMatrix},
1069    {"nativeDrawBitmapMesh", "(IIII[FI[III)V",
1070        (void*)SkCanvasGlue::drawBitmapMesh},
1071    {"nativeDrawVertices", "(III[FI[FI[II[SIII)V",
1072        (void*)SkCanvasGlue::drawVertices},
1073    {"native_drawText","(I[CIIFFII)V",
1074        (void*) SkCanvasGlue::drawText___CIIFFIPaint},
1075    {"native_drawText","(ILjava/lang/String;IIFFII)V",
1076        (void*) SkCanvasGlue::drawText__StringIIFFIPaint},
1077    {"native_drawTextRun","(I[CIIIIFFII)V",
1078        (void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaint},
1079    {"native_drawTextRun","(ILjava/lang/String;IIIIFFII)V",
1080        (void*) SkCanvasGlue::drawTextRun__StringIIIIFFIPaint},
1081    {"native_drawPosText","(I[CII[FI)V",
1082        (void*) SkCanvasGlue::drawPosText___CII_FPaint},
1083    {"native_drawPosText","(ILjava/lang/String;[FI)V",
1084        (void*) SkCanvasGlue::drawPosText__String_FPaint},
1085    {"native_drawTextOnPath","(I[CIIIFFII)V",
1086        (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
1087    {"native_drawTextOnPath","(ILjava/lang/String;IFFII)V",
1088        (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
1089
1090    {"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches},
1091
1092    {"freeTextLayoutCaches", "()V", (void*) SkCanvasGlue::freeTextLayoutCaches}
1093};
1094
1095///////////////////////////////////////////////////////////////////////////////
1096
1097#include <android_runtime/AndroidRuntime.h>
1098
1099#define REG(env, name, array) \
1100    result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
1101                                                    SK_ARRAY_COUNT(array));  \
1102    if (result < 0) return result
1103
1104int register_android_graphics_Canvas(JNIEnv* env) {
1105    int result;
1106
1107    REG(env, "android/graphics/Canvas", gCanvasMethods);
1108
1109    return result;
1110}
1111
1112}
1113