Canvas.cpp revision 4beb8ff7175ebd14b96942724a658f407d0b9951
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 "SkGLCanvas.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 "SkBoundaryPatch.h"
31#include "SkMeshUtils.h"
32
33#include "unicode/ubidi.h"
34#include "unicode/ushape.h"
35
36#include <utils/Log.h>
37
38#define TIME_DRAWx
39
40static uint32_t get_thread_msec() {
41#if defined(HAVE_POSIX_CLOCKS)
42    struct timespec tm;
43
44    clock_gettime(CLOCK_THREAD_CPUTIME_ID, &tm);
45
46    return tm.tv_sec * 1000LL + tm.tv_nsec / 1000000;
47#else
48    struct timeval tv;
49
50    gettimeofday(&tv, NULL);
51    return tv.tv_sec * 1000LL + tv.tv_usec / 1000;
52#endif
53}
54
55namespace android {
56
57class SkCanvasGlue {
58public:
59
60    enum {
61      kDirection_LTR = 0,
62      kDirection_RTL = 1
63    };
64
65    enum {
66      kDirection_Mask = 0x1
67    };
68
69    enum {
70      kBidi_LTR = 0,
71      kBidi_RTL = 1,
72      kBidi_Default_LTR = 2,
73      kBidi_Default_RTL = 3,
74      kBidi_Force_LTR = 4,
75      kBidi_Force_RTL = 5
76    };
77
78    static void finalizer(JNIEnv* env, jobject clazz, SkCanvas* canvas) {
79        canvas->unref();
80    }
81
82    static SkCanvas* initRaster(JNIEnv* env, jobject, SkBitmap* bitmap) {
83        return bitmap ? new SkCanvas(*bitmap) : new SkCanvas;
84    }
85
86    static SkCanvas* initGL(JNIEnv* env, jobject) {
87        return new SkGLCanvas;
88    }
89
90    static void freeCaches(JNIEnv* env, jobject) {
91        // these are called in no particular order
92        SkGLCanvas::DeleteAllTextures();
93        SkImageRef_GlobalPool::SetRAMUsed(0);
94        SkGraphics::SetFontCacheUsed(0);
95    }
96
97    static jboolean isOpaque(JNIEnv* env, jobject jcanvas) {
98        NPE_CHECK_RETURN_ZERO(env, jcanvas);
99        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
100
101        /*
102            Currently we cannot support transparency in GL-based canvas' at
103            the view level. Therefore we cannot base our answer on the device's
104            bitmap, but need to hard-code the answer. If we relax this
105            limitation in views, we can simplify the following code as well.
106
107            Use the getViewport() call to find out if we're gl-based...
108        */
109        if (canvas->getViewport(NULL)) {
110            return true;
111        }
112
113        // normal technique, rely on the device's bitmap for the answer
114        return canvas->getDevice()->accessBitmap(false).isOpaque();
115    }
116
117    static int getWidth(JNIEnv* env, jobject jcanvas) {
118        NPE_CHECK_RETURN_ZERO(env, jcanvas);
119        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
120        return canvas->getDevice()->accessBitmap(false).width();
121    }
122
123    static int getHeight(JNIEnv* env, jobject jcanvas) {
124        NPE_CHECK_RETURN_ZERO(env, jcanvas);
125        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
126        return canvas->getDevice()->accessBitmap(false).height();
127    }
128
129    static void setViewport(JNIEnv* env, jobject, SkCanvas* canvas,
130                            int width, int height) {
131        canvas->setViewport(width, height);
132    }
133
134    static void setBitmap(JNIEnv* env, jobject, SkCanvas* canvas,
135                          SkBitmap* bitmap) {
136        canvas->setBitmapDevice(*bitmap);
137    }
138
139    static int saveAll(JNIEnv* env, jobject jcanvas) {
140        NPE_CHECK_RETURN_ZERO(env, jcanvas);
141        return GraphicsJNI::getNativeCanvas(env, jcanvas)->save();
142    }
143
144    static int save(JNIEnv* env, jobject jcanvas, SkCanvas::SaveFlags flags) {
145        NPE_CHECK_RETURN_ZERO(env, jcanvas);
146        return GraphicsJNI::getNativeCanvas(env, jcanvas)->save(flags);
147    }
148
149    static int saveLayer(JNIEnv* env, jobject, SkCanvas* canvas, jobject bounds,
150                         SkPaint* paint, int flags) {
151        SkRect* bounds_ = NULL;
152        SkRect  storage;
153        if (bounds != NULL) {
154            GraphicsJNI::jrectf_to_rect(env, bounds, &storage);
155            bounds_ = &storage;
156        }
157        return canvas->saveLayer(bounds_, paint, (SkCanvas::SaveFlags)flags);
158    }
159
160    static int saveLayer4F(JNIEnv* env, jobject, SkCanvas* canvas,
161                           jfloat l, jfloat t, jfloat r, jfloat b,
162                           SkPaint* paint, int flags) {
163        SkRect bounds;
164        bounds.set(SkFloatToScalar(l), SkFloatToScalar(t), SkFloatToScalar(r),
165                   SkFloatToScalar(b));
166        return canvas->saveLayer(&bounds, paint, (SkCanvas::SaveFlags)flags);
167    }
168
169    static int saveLayerAlpha(JNIEnv* env, jobject, SkCanvas* canvas,
170                              jobject bounds, int alpha, int flags) {
171        SkRect* bounds_ = NULL;
172        SkRect  storage;
173        if (bounds != NULL) {
174            GraphicsJNI::jrectf_to_rect(env, bounds, &storage);
175            bounds_ = &storage;
176        }
177        return canvas->saveLayerAlpha(bounds_, alpha,
178                                      (SkCanvas::SaveFlags)flags);
179    }
180
181    static int saveLayerAlpha4F(JNIEnv* env, jobject, SkCanvas* canvas,
182                                jfloat l, jfloat t, jfloat r, jfloat b,
183                                int alpha, int flags) {
184        SkRect  bounds;
185        bounds.set(SkFloatToScalar(l), SkFloatToScalar(t), SkFloatToScalar(r),
186                   SkFloatToScalar(b));
187        return canvas->saveLayerAlpha(&bounds, alpha,
188                                      (SkCanvas::SaveFlags)flags);
189    }
190
191    static void restore(JNIEnv* env, jobject jcanvas) {
192        NPE_CHECK_RETURN_VOID(env, jcanvas);
193        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
194        if (canvas->getSaveCount() <= 1) {  // cannot restore anymore
195            doThrowISE(env, "Underflow in restore");
196            return;
197        }
198        canvas->restore();
199    }
200
201    static int getSaveCount(JNIEnv* env, jobject jcanvas) {
202        NPE_CHECK_RETURN_ZERO(env, jcanvas);
203        return GraphicsJNI::getNativeCanvas(env, jcanvas)->getSaveCount();
204    }
205
206    static void restoreToCount(JNIEnv* env, jobject jcanvas, int restoreCount) {
207        NPE_CHECK_RETURN_VOID(env, jcanvas);
208        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
209        if (restoreCount < 1) {
210            doThrowIAE(env, "Underflow in restoreToCount");
211            return;
212        }
213        canvas->restoreToCount(restoreCount);
214    }
215
216    static void translate(JNIEnv* env, jobject jcanvas, jfloat dx, jfloat dy) {
217        NPE_CHECK_RETURN_VOID(env, jcanvas);
218        SkScalar dx_ = SkFloatToScalar(dx);
219        SkScalar dy_ = SkFloatToScalar(dy);
220        (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->translate(dx_, dy_);
221    }
222
223    static void scale__FF(JNIEnv* env, jobject jcanvas, jfloat sx, jfloat sy) {
224        NPE_CHECK_RETURN_VOID(env, jcanvas);
225        SkScalar sx_ = SkFloatToScalar(sx);
226        SkScalar sy_ = SkFloatToScalar(sy);
227        (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->scale(sx_, sy_);
228    }
229
230    static void rotate__F(JNIEnv* env, jobject jcanvas, jfloat degrees) {
231        NPE_CHECK_RETURN_VOID(env, jcanvas);
232        SkScalar degrees_ = SkFloatToScalar(degrees);
233        (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->rotate(degrees_);
234    }
235
236    static void skew__FF(JNIEnv* env, jobject jcanvas, jfloat sx, jfloat sy) {
237        NPE_CHECK_RETURN_VOID(env, jcanvas);
238        SkScalar sx_ = SkFloatToScalar(sx);
239        SkScalar sy_ = SkFloatToScalar(sy);
240        (void)GraphicsJNI::getNativeCanvas(env, jcanvas)->skew(sx_, sy_);
241    }
242
243    static void concat(JNIEnv* env, jobject, SkCanvas* canvas,
244                       const SkMatrix* matrix) {
245        canvas->concat(*matrix);
246    }
247
248    static void setMatrix(JNIEnv* env, jobject, SkCanvas* canvas,
249                          const SkMatrix* matrix) {
250        if (NULL == matrix) {
251            canvas->resetMatrix();
252        } else {
253            canvas->setMatrix(*matrix);
254        }
255    }
256
257    static jboolean clipRect_FFFF(JNIEnv* env, jobject jcanvas, jfloat left,
258                                  jfloat top, jfloat right, jfloat bottom) {
259        NPE_CHECK_RETURN_ZERO(env, jcanvas);
260        SkRect  r;
261        r.set(SkFloatToScalar(left), SkFloatToScalar(top),
262              SkFloatToScalar(right), SkFloatToScalar(bottom));
263        SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas);
264        return c->clipRect(r);
265    }
266
267    static jboolean clipRect_IIII(JNIEnv* env, jobject jcanvas, jint left,
268                                  jint top, jint right, jint bottom) {
269        NPE_CHECK_RETURN_ZERO(env, jcanvas);
270        SkRect  r;
271        r.set(SkIntToScalar(left), SkIntToScalar(top),
272              SkIntToScalar(right), SkIntToScalar(bottom));
273        return GraphicsJNI::getNativeCanvas(env, jcanvas)->clipRect(r);
274    }
275
276    static jboolean clipRect_RectF(JNIEnv* env, jobject jcanvas, jobject rectf) {
277        NPE_CHECK_RETURN_ZERO(env, jcanvas);
278        NPE_CHECK_RETURN_ZERO(env, rectf);
279        SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas);
280        SkRect tmp;
281        return c->clipRect(*GraphicsJNI::jrectf_to_rect(env, rectf, &tmp));
282    }
283
284    static jboolean clipRect_Rect(JNIEnv* env, jobject jcanvas, jobject rect) {
285        NPE_CHECK_RETURN_ZERO(env, jcanvas);
286        NPE_CHECK_RETURN_ZERO(env, rect);
287        SkCanvas* c = GraphicsJNI::getNativeCanvas(env, jcanvas);
288        SkRect tmp;
289        return c->clipRect(*GraphicsJNI::jrect_to_rect(env, rect, &tmp));
290    }
291
292    static jboolean clipRect(JNIEnv* env, jobject, SkCanvas* canvas,
293                             float left, float top, float right, float bottom,
294                             int op) {
295        SkRect rect;
296        rect.set(SkFloatToScalar(left), SkFloatToScalar(top),
297                 SkFloatToScalar(right), SkFloatToScalar(bottom));
298        return canvas->clipRect(rect, (SkRegion::Op)op);
299    }
300
301    static jboolean clipPath(JNIEnv* env, jobject, SkCanvas* canvas,
302                             SkPath* path, int op) {
303        return canvas->clipPath(*path, (SkRegion::Op)op);
304    }
305
306    static jboolean clipRegion(JNIEnv* env, jobject, SkCanvas* canvas,
307                               SkRegion* deviceRgn, int op) {
308        return canvas->clipRegion(*deviceRgn, (SkRegion::Op)op);
309    }
310
311    static void setDrawFilter(JNIEnv* env, jobject, SkCanvas* canvas,
312                              SkDrawFilter* filter) {
313        canvas->setDrawFilter(filter);
314    }
315
316    static jboolean quickReject__RectFI(JNIEnv* env, jobject, SkCanvas* canvas,
317                                        jobject rect, int edgetype) {
318        SkRect rect_;
319        GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
320        return canvas->quickReject(rect_, (SkCanvas::EdgeType)edgetype);
321    }
322
323    static jboolean quickReject__PathI(JNIEnv* env, jobject, SkCanvas* canvas,
324                                       SkPath* path, int edgetype) {
325        return canvas->quickReject(*path, (SkCanvas::EdgeType)edgetype);
326    }
327
328    static jboolean quickReject__FFFFI(JNIEnv* env, jobject, SkCanvas* canvas,
329                                       jfloat left, jfloat top, jfloat right,
330                                       jfloat bottom, int edgetype) {
331        SkRect r;
332        r.set(SkFloatToScalar(left), SkFloatToScalar(top),
333              SkFloatToScalar(right), SkFloatToScalar(bottom));
334        return canvas->quickReject(r, (SkCanvas::EdgeType)edgetype);
335    }
336
337    static void drawRGB(JNIEnv* env, jobject, SkCanvas* canvas,
338                        jint r, jint g, jint b) {
339        canvas->drawARGB(0xFF, r, g, b);
340    }
341
342    static void drawARGB(JNIEnv* env, jobject, SkCanvas* canvas,
343                         jint a, jint r, jint g, jint b) {
344        canvas->drawARGB(a, r, g, b);
345    }
346
347    static void drawColor__I(JNIEnv* env, jobject, SkCanvas* canvas,
348                             jint color) {
349        canvas->drawColor(color);
350    }
351
352    static void drawColor__II(JNIEnv* env, jobject, SkCanvas* canvas,
353                              jint color, SkPorterDuff::Mode mode) {
354        canvas->drawColor(color, SkPorterDuff::ToXfermodeMode(mode));
355    }
356
357    static void drawPaint(JNIEnv* env, jobject, SkCanvas* canvas,
358                          SkPaint* paint) {
359        canvas->drawPaint(*paint);
360    }
361
362    static void doPoints(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray,
363                         jint offset, jint count, jobject jpaint,
364                         SkCanvas::PointMode mode) {
365        NPE_CHECK_RETURN_VOID(env, jcanvas);
366        NPE_CHECK_RETURN_VOID(env, jptsArray);
367        NPE_CHECK_RETURN_VOID(env, jpaint);
368        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
369        const SkPaint& paint = *GraphicsJNI::getNativePaint(env, jpaint);
370
371        AutoJavaFloatArray autoPts(env, jptsArray);
372        float* floats = autoPts.ptr();
373        const int length = autoPts.length();
374
375        if ((offset | count) < 0 || offset + count > length) {
376            doThrowAIOOBE(env);
377            return;
378        }
379
380        // now convert the floats into SkPoints
381        count >>= 1;    // now it is the number of points
382        SkAutoSTMalloc<32, SkPoint> storage(count);
383        SkPoint* pts = storage.get();
384        const float* src = floats + offset;
385        for (int i = 0; i < count; i++) {
386            pts[i].set(SkFloatToScalar(src[0]), SkFloatToScalar(src[1]));
387            src += 2;
388        }
389        canvas->drawPoints(mode, count, pts, paint);
390    }
391
392    static void drawPoints(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray,
393                           jint offset, jint count, jobject jpaint) {
394        doPoints(env, jcanvas, jptsArray, offset, count, jpaint,
395                 SkCanvas::kPoints_PointMode);
396    }
397
398    static void drawLines(JNIEnv* env, jobject jcanvas, jfloatArray jptsArray,
399                           jint offset, jint count, jobject jpaint) {
400        doPoints(env, jcanvas, jptsArray, offset, count, jpaint,
401                 SkCanvas::kLines_PointMode);
402    }
403
404    static void drawPoint(JNIEnv* env, jobject jcanvas, float x, float y,
405                          jobject jpaint) {
406        NPE_CHECK_RETURN_VOID(env, jcanvas);
407        NPE_CHECK_RETURN_VOID(env, jpaint);
408        SkCanvas* canvas = GraphicsJNI::getNativeCanvas(env, jcanvas);
409        const SkPaint& paint = *GraphicsJNI::getNativePaint(env, jpaint);
410
411        canvas->drawPoint(SkFloatToScalar(x), SkFloatToScalar(y), paint);
412    }
413
414    static void drawLine__FFFFPaint(JNIEnv* env, jobject, SkCanvas* canvas,
415                                    jfloat startX, jfloat startY, jfloat stopX,
416                                    jfloat stopY, SkPaint* paint) {
417        canvas->drawLine(SkFloatToScalar(startX), SkFloatToScalar(startY),
418                         SkFloatToScalar(stopX), SkFloatToScalar(stopY),
419                         *paint);
420    }
421
422    static void drawRect__RectFPaint(JNIEnv* env, jobject, SkCanvas* canvas,
423                                     jobject rect, SkPaint* paint) {
424        SkRect rect_;
425        GraphicsJNI::jrectf_to_rect(env, rect, &rect_);
426        canvas->drawRect(rect_, *paint);
427    }
428
429    static void drawRect__FFFFPaint(JNIEnv* env, jobject, SkCanvas* canvas,
430                                    jfloat left, jfloat top, jfloat right,
431                                    jfloat bottom, SkPaint* paint) {
432        SkScalar left_ = SkFloatToScalar(left);
433        SkScalar top_ = SkFloatToScalar(top);
434        SkScalar right_ = SkFloatToScalar(right);
435        SkScalar bottom_ = SkFloatToScalar(bottom);
436        canvas->drawRectCoords(left_, top_, right_, bottom_, *paint);
437    }
438
439    static void drawOval(JNIEnv* env, jobject, SkCanvas* canvas, jobject joval,
440                         SkPaint* paint) {
441        SkRect oval;
442        GraphicsJNI::jrectf_to_rect(env, joval, &oval);
443        canvas->drawOval(oval, *paint);
444    }
445
446    static void drawCircle(JNIEnv* env, jobject, SkCanvas* canvas, jfloat cx,
447                           jfloat cy, jfloat radius, SkPaint* paint) {
448        canvas->drawCircle(SkFloatToScalar(cx), SkFloatToScalar(cy),
449                           SkFloatToScalar(radius), *paint);
450    }
451
452    static void drawArc(JNIEnv* env, jobject, SkCanvas* canvas, jobject joval,
453                        jfloat startAngle, jfloat sweepAngle,
454                        jboolean useCenter, SkPaint* paint) {
455        SkRect oval;
456        GraphicsJNI::jrectf_to_rect(env, joval, &oval);
457        canvas->drawArc(oval, SkFloatToScalar(startAngle),
458                        SkFloatToScalar(sweepAngle), useCenter, *paint);
459    }
460
461    static void drawRoundRect(JNIEnv* env, jobject, SkCanvas* canvas,
462                              jobject jrect, jfloat rx, jfloat ry,
463                              SkPaint* paint) {
464        SkRect rect;
465        GraphicsJNI::jrectf_to_rect(env, jrect, &rect);
466        canvas->drawRoundRect(rect, SkFloatToScalar(rx), SkFloatToScalar(ry),
467                              *paint);
468    }
469
470    static void drawPath(JNIEnv* env, jobject, SkCanvas* canvas, SkPath* path,
471                         SkPaint* paint) {
472        canvas->drawPath(*path, *paint);
473    }
474
475    static void drawPicture(JNIEnv* env, jobject, SkCanvas* canvas,
476                            SkPicture* picture) {
477        SkASSERT(canvas);
478        SkASSERT(picture);
479
480#ifdef TIME_DRAW
481        SkMSec now = get_thread_msec(); //SkTime::GetMSecs();
482#endif
483        canvas->drawPicture(*picture);
484#ifdef TIME_DRAW
485        LOGD("---- picture playback %d ms\n", get_thread_msec() - now);
486#endif
487    }
488
489    static void drawBitmap__BitmapFFPaint(JNIEnv* env, jobject jcanvas,
490                                          SkCanvas* canvas, SkBitmap* bitmap,
491                                          jfloat left, jfloat top,
492                                          SkPaint* paint, jint canvasDensity,
493                                          jint screenDensity, jint bitmapDensity) {
494        SkScalar left_ = SkFloatToScalar(left);
495        SkScalar top_ = SkFloatToScalar(top);
496
497        if (canvasDensity == bitmapDensity || canvasDensity == 0
498                || bitmapDensity == 0) {
499            if (screenDensity != 0 && screenDensity != bitmapDensity) {
500                SkPaint filteredPaint;
501                if (paint) {
502                    filteredPaint = *paint;
503                }
504                filteredPaint.setFilterBitmap(true);
505                canvas->drawBitmap(*bitmap, left_, top_, &filteredPaint);
506            } else {
507                canvas->drawBitmap(*bitmap, left_, top_, paint);
508            }
509        } else {
510            canvas->save();
511            SkScalar scale = SkFloatToScalar(canvasDensity / (float)bitmapDensity);
512            canvas->translate(left_, top_);
513            canvas->scale(scale, scale);
514
515            SkPaint filteredPaint;
516            if (paint) {
517                filteredPaint = *paint;
518            }
519            filteredPaint.setFilterBitmap(true);
520
521            canvas->drawBitmap(*bitmap, 0, 0, &filteredPaint);
522
523            canvas->restore();
524        }
525    }
526
527    static void doDrawBitmap(JNIEnv* env, SkCanvas* canvas, SkBitmap* bitmap,
528                        jobject srcIRect, const SkRect& dst, SkPaint* paint,
529                        jint screenDensity, jint bitmapDensity) {
530        SkIRect    src, *srcPtr = NULL;
531
532        if (NULL != srcIRect) {
533            GraphicsJNI::jrect_to_irect(env, srcIRect, &src);
534            srcPtr = &src;
535        }
536
537        if (screenDensity != 0 && screenDensity != bitmapDensity) {
538            SkPaint filteredPaint;
539            if (paint) {
540                filteredPaint = *paint;
541            }
542            filteredPaint.setFilterBitmap(true);
543            canvas->drawBitmapRect(*bitmap, srcPtr, dst, &filteredPaint);
544        } else {
545            canvas->drawBitmapRect(*bitmap, srcPtr, dst, paint);
546        }
547    }
548
549    static void drawBitmapRF(JNIEnv* env, jobject, SkCanvas* canvas,
550                             SkBitmap* bitmap, jobject srcIRect,
551                             jobject dstRectF, SkPaint* paint,
552                             jint screenDensity, jint bitmapDensity) {
553        SkRect      dst;
554        GraphicsJNI::jrectf_to_rect(env, dstRectF, &dst);
555        doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
556                screenDensity, bitmapDensity);
557    }
558
559    static void drawBitmapRR(JNIEnv* env, jobject, SkCanvas* canvas,
560                             SkBitmap* bitmap, jobject srcIRect,
561                             jobject dstRect, SkPaint* paint,
562                             jint screenDensity, jint bitmapDensity) {
563        SkRect      dst;
564        GraphicsJNI::jrect_to_rect(env, dstRect, &dst);
565        doDrawBitmap(env, canvas, bitmap, srcIRect, dst, paint,
566                screenDensity, bitmapDensity);
567    }
568
569    static void drawBitmapArray(JNIEnv* env, jobject, SkCanvas* canvas,
570                                jintArray jcolors, int offset, int stride,
571                                jfloat x, jfloat y, int width, int height,
572                                jboolean hasAlpha, SkPaint* paint)
573    {
574        SkBitmap    bitmap;
575
576        bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config :
577                         SkBitmap::kRGB_565_Config, width, height);
578        if (!bitmap.allocPixels()) {
579            return;
580        }
581
582        if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride,
583                                    0, 0, width, height, bitmap)) {
584            return;
585        }
586
587        canvas->drawBitmap(bitmap, SkFloatToScalar(x), SkFloatToScalar(y),
588                           paint);
589    }
590
591    static void drawBitmapMatrix(JNIEnv* env, jobject, SkCanvas* canvas,
592                                 const SkBitmap* bitmap, const SkMatrix* matrix,
593                                 const SkPaint* paint) {
594        canvas->drawBitmapMatrix(*bitmap, *matrix, paint);
595    }
596
597    static void drawBitmapMesh(JNIEnv* env, jobject, SkCanvas* canvas,
598                          const SkBitmap* bitmap, int meshWidth, int meshHeight,
599                          jfloatArray jverts, int vertIndex, jintArray jcolors,
600                          int colorIndex, const SkPaint* paint) {
601
602        const int ptCount = (meshWidth + 1) * (meshHeight + 1);
603        const int indexCount = meshWidth * meshHeight * 6;
604
605        AutoJavaFloatArray  vertA(env, jverts, vertIndex + (ptCount << 1));
606        AutoJavaIntArray    colorA(env, jcolors, colorIndex + ptCount);
607
608        /*  Our temp storage holds 2 or 3 arrays.
609            texture points [ptCount * sizeof(SkPoint)]
610            optionally vertex points [ptCount * sizeof(SkPoint)] if we need a
611                copy to convert from float to fixed
612            indices [ptCount * sizeof(uint16_t)]
613        */
614        ssize_t storageSize = ptCount * sizeof(SkPoint); // texs[]
615#ifdef SK_SCALAR_IS_FIXED
616        storageSize += ptCount * sizeof(SkPoint);  // storage for verts
617#endif
618        storageSize += indexCount * sizeof(uint16_t);  // indices[]
619
620        SkAutoMalloc storage(storageSize);
621        SkPoint* texs = (SkPoint*)storage.get();
622        SkPoint* verts;
623        uint16_t* indices;
624#ifdef SK_SCALAR_IS_FLOAT
625        verts = (SkPoint*)(vertA.ptr() + vertIndex);
626        indices = (uint16_t*)(texs + ptCount);
627#else
628        verts = texs + ptCount;
629        indices = (uint16_t*)(verts + ptCount);
630        // convert floats to fixed
631        {
632            const float* src = vertA.ptr() + vertIndex;
633            for (int i = 0; i < ptCount; i++) {
634                verts[i].set(SkFloatToFixed(src[0]), SkFloatToFixed(src[1]));
635                src += 2;
636            }
637        }
638#endif
639
640        // cons up texture coordinates and indices
641        {
642            const SkScalar w = SkIntToScalar(bitmap->width());
643            const SkScalar h = SkIntToScalar(bitmap->height());
644            const SkScalar dx = w / meshWidth;
645            const SkScalar dy = h / meshHeight;
646
647            SkPoint* texsPtr = texs;
648            SkScalar y = 0;
649            for (int i = 0; i <= meshHeight; i++) {
650                if (i == meshHeight) {
651                    y = h;  // to ensure numerically we hit h exactly
652                }
653                SkScalar x = 0;
654                for (int j = 0; j < meshWidth; j++) {
655                    texsPtr->set(x, y);
656                    texsPtr += 1;
657                    x += dx;
658                }
659                texsPtr->set(w, y);
660                texsPtr += 1;
661                y += dy;
662            }
663            SkASSERT(texsPtr - texs == ptCount);
664        }
665
666        // cons up indices
667        {
668            uint16_t* indexPtr = indices;
669            int index = 0;
670            for (int i = 0; i < meshHeight; i++) {
671                for (int j = 0; j < meshWidth; j++) {
672                    // lower-left triangle
673                    *indexPtr++ = index;
674                    *indexPtr++ = index + meshWidth + 1;
675                    *indexPtr++ = index + meshWidth + 2;
676                    // upper-right triangle
677                    *indexPtr++ = index;
678                    *indexPtr++ = index + meshWidth + 2;
679                    *indexPtr++ = index + 1;
680                    // bump to the next cell
681                    index += 1;
682                }
683                // bump to the next row
684                index += 1;
685            }
686            SkASSERT(indexPtr - indices == indexCount);
687            SkASSERT((char*)indexPtr - (char*)storage.get() == storageSize);
688        }
689
690        // double-check that we have legal indices
691#ifdef SK_DEBUG
692        {
693            for (int i = 0; i < indexCount; i++) {
694                SkASSERT((unsigned)indices[i] < (unsigned)ptCount);
695            }
696        }
697#endif
698
699        // cons-up a shader for the bitmap
700        SkPaint tmpPaint;
701        if (paint) {
702            tmpPaint = *paint;
703        }
704        SkShader* shader = SkShader::CreateBitmapShader(*bitmap,
705                        SkShader::kClamp_TileMode, SkShader::kClamp_TileMode);
706        tmpPaint.setShader(shader)->safeUnref();
707
708        canvas->drawVertices(SkCanvas::kTriangles_VertexMode, ptCount, verts,
709                             texs, (const SkColor*)colorA.ptr(), NULL, indices,
710                             indexCount, tmpPaint);
711    }
712
713    static void drawVertices(JNIEnv* env, jobject, SkCanvas* canvas,
714                             SkCanvas::VertexMode mode, int vertexCount,
715                             jfloatArray jverts, int vertIndex,
716                             jfloatArray jtexs, int texIndex,
717                             jintArray jcolors, int colorIndex,
718                             jshortArray jindices, int indexIndex,
719                             int indexCount, const SkPaint* paint) {
720
721        AutoJavaFloatArray  vertA(env, jverts, vertIndex + vertexCount);
722        AutoJavaFloatArray  texA(env, jtexs, texIndex + vertexCount);
723        AutoJavaIntArray    colorA(env, jcolors, colorIndex + vertexCount);
724        AutoJavaShortArray  indexA(env, jindices, indexIndex + indexCount);
725
726        const int ptCount = vertexCount >> 1;
727
728        SkPoint* verts;
729        SkPoint* texs = NULL;
730#ifdef SK_SCALAR_IS_FLOAT
731        verts = (SkPoint*)(vertA.ptr() + vertIndex);
732        if (jtexs != NULL) {
733            texs = (SkPoint*)(texA.ptr() + texIndex);
734        }
735#else
736        int count = ptCount;    // for verts
737        if (jtexs != NULL) {
738            count += ptCount;   // += for texs
739        }
740        SkAutoMalloc storage(count * sizeof(SkPoint));
741        verts = (SkPoint*)storage.get();
742        const float* src = vertA.ptr() + vertIndex;
743        for (int i = 0; i < ptCount; i++) {
744            verts[i].set(SkFloatToFixed(src[0]), SkFloatToFixed(src[1]));
745            src += 2;
746        }
747        if (jtexs != NULL) {
748            texs = verts + ptCount;
749            src = texA.ptr() + texIndex;
750            for (int i = 0; i < ptCount; i++) {
751                texs[i].set(SkFloatToFixed(src[0]), SkFloatToFixed(src[1]));
752                src += 2;
753            }
754        }
755#endif
756
757        const SkColor* colors = NULL;
758        const uint16_t* indices = NULL;
759        if (jcolors != NULL) {
760            colors = (const SkColor*)(colorA.ptr() + colorIndex);
761        }
762        if (jindices != NULL) {
763            indices = (const uint16_t*)(indexA.ptr() + indexIndex);
764        }
765
766        canvas->drawVertices(mode, ptCount, verts, texs, colors, NULL,
767                             indices, indexCount, *paint);
768    }
769
770    /**
771     * Character-based Arabic shaping.
772     *
773     * We'll use harfbuzz and glyph-based shaping instead once we're set up for it.
774     *
775     * @context the text context
776     * @start the start of the text to render
777     * @count the length of the text to render, start + count  must be <= contextCount
778     * @contextCount the length of the context
779     * @shaped where to put the shaped text, must have capacity for count uchars
780     * @return the length of the shaped text, or -1 if error
781     */
782    static int shapeRtlText(const jchar* context, jsize start, jsize count, jsize contextCount,
783                            jchar* shaped, UErrorCode &status) {
784        jchar buffer[contextCount];
785
786        // Use fixed length since we need to keep start and count valid
787        u_shapeArabic(context, contextCount, buffer, contextCount,
788                       U_SHAPE_LENGTH_FIXED_SPACES_NEAR |
789                       U_SHAPE_TEXT_DIRECTION_LOGICAL | U_SHAPE_LETTERS_SHAPE |
790                       U_SHAPE_X_LAMALEF_SUB_ALTERNATE, &status);
791
792        if (U_SUCCESS(status)) {
793            // trim out 0xffff following ligatures, if any
794            int end = 0;
795            for (int i = start, e = start + count; i < e; ++i) {
796                if (buffer[i] != 0xffff) {
797                    buffer[end++] = buffer[i];
798                }
799            }
800            count = end;
801            // LOG(LOG_INFO, "CSRTL", "start %d count %d ccount %d\n", start, count, contextCount);
802            ubidi_writeReverse(buffer, count, shaped, count, UBIDI_DO_MIRRORING | UBIDI_OUTPUT_REVERSE
803                               | UBIDI_KEEP_BASE_COMBINING, &status);
804            if (U_SUCCESS(status)) {
805                return count;
806            }
807        }
808
809        return -1;
810    }
811
812    /**
813     * Basic character-based layout supporting rtl and arabic shaping.
814     * Runs bidi on the text and generates a reordered, shaped line in buffer, returning
815     * the length.
816     * @text the text
817     * @len the length of the text in uchars
818     * @dir receives the resolved paragraph direction
819     * @buffer the buffer to receive the reordered, shaped line.  Must have capacity of
820     * at least len jchars.
821     * @flags line bidi flags
822     * @return the length of the reordered, shaped line, or -1 if error
823     */
824    static jint layoutLine(const jchar* text, jint len, jint flags, int &dir, jchar* buffer,
825            UErrorCode &status) {
826        static int RTL_OPTS = UBIDI_DO_MIRRORING | UBIDI_KEEP_BASE_COMBINING |
827                UBIDI_REMOVE_BIDI_CONTROLS | UBIDI_OUTPUT_REVERSE;
828
829        UBiDiLevel bidiReq = 0;
830        switch (flags) {
831          case kBidi_LTR: bidiReq = 0; break; // no ICU constant, canonical LTR level
832          case kBidi_RTL: bidiReq = 1; break; // no ICU constant, canonical RTL level
833          case kBidi_Default_LTR: bidiReq = UBIDI_DEFAULT_LTR; break;
834          case kBidi_Default_RTL: bidiReq = UBIDI_DEFAULT_RTL; break;
835          case kBidi_Force_LTR: memcpy(buffer, text, len * sizeof(jchar)); return len;
836          case kBidi_Force_RTL: return shapeRtlText(text, 0, len, len, buffer, status);
837        }
838
839        int32_t result = -1;
840
841        UBiDi* bidi = ubidi_open();
842        if (bidi) {
843            ubidi_setPara(bidi, text, len, bidiReq, NULL, &status);
844            if (U_SUCCESS(status)) {
845                dir = ubidi_getParaLevel(bidi) & 0x1; // 0 if ltr, 1 if rtl
846
847                int rc = ubidi_countRuns(bidi, &status);
848                if (U_SUCCESS(status)) {
849                    // LOG(LOG_INFO, "LAYOUT", "para bidiReq=%d dir=%d rc=%d\n", bidiReq, dir, rc);
850
851                    int32_t slen = 0;
852                    for (int i = 0; i < rc; ++i) {
853                        int32_t start;
854                        int32_t length;
855                        UBiDiDirection runDir = ubidi_getVisualRun(bidi, i, &start, &length);
856                        // LOG(LOG_INFO, "LAYOUT", "  [%2d] runDir=%d start=%3d len=%3d\n", i, runDir, start, length);
857                        if (runDir == UBIDI_RTL) {
858                            slen += shapeRtlText(text + start, 0, length, length, buffer + slen, status);
859                        } else {
860                            memcpy(buffer + slen, text + start, length * sizeof(jchar));
861                            slen += length;
862                        }
863                    }
864                    if (U_SUCCESS(status)) {
865                        result = slen;
866                    }
867                }
868            }
869            ubidi_close(bidi);
870        }
871
872        return result;
873    }
874
875    // Returns true if we might need layout.  If bidiFlags force LTR, assume no layout, if
876    // bidiFlags indicate there probably is RTL, assume we do, otherwise scan the text
877    // looking for a character >= the first RTL character in unicode and assume we do if
878    // we find one.
879    static bool needsLayout(const jchar* text, jint len, jint bidiFlags) {
880        if (bidiFlags == kBidi_Force_LTR) {
881            return false;
882        }
883        if ((bidiFlags == kBidi_RTL) || (bidiFlags == kBidi_Default_RTL) ||
884                bidiFlags == kBidi_Force_RTL) {
885            return true;
886        }
887        for (int i = 0; i < len; ++i) {
888            if (text[i] >= 0x0590) {
889                return true;
890            }
891        }
892        return false;
893    }
894
895    // Draws a paragraph of text on a single line, running bidi and shaping
896    static void drawText(JNIEnv* env, SkCanvas* canvas, const jchar* text, jsize len,
897                           jfloat x, jfloat y, int bidiFlags, SkPaint* paint) {
898
899        SkScalar x_ = SkFloatToScalar(x);
900        SkScalar y_ = SkFloatToScalar(y);
901
902        SkPaint::Align horiz = paint->getTextAlign();
903
904        const jchar *workText = text;
905        jchar *buffer = NULL;
906        int dir = kDirection_LTR;
907        if (needsLayout(text, len, bidiFlags)) {
908            buffer =(jchar *) malloc(len * sizeof(jchar));
909            if (!buffer) {
910                return;
911            }
912            UErrorCode status = U_ZERO_ERROR;
913            len = layoutLine(text, len, bidiFlags, dir, buffer, status); // might change len, dir
914            if (!U_SUCCESS(status)) {
915                LOG(LOG_WARN, "LAYOUT", "drawText error %d\n", status);
916                free(buffer);
917                return; // can't render
918            }
919
920            workText = buffer; // use the shaped text
921        }
922
923        bool trimLeft = false;
924        bool trimRight = false;
925
926        switch (horiz) {
927        case SkPaint::kLeft_Align: trimLeft = dir & kDirection_Mask; break;
928        case SkPaint::kCenter_Align: trimLeft = trimRight = true; break;
929        case SkPaint::kRight_Align: trimRight = !(dir & kDirection_Mask);
930        default: break;
931        }
932        const jchar* workLimit = workText + len;
933
934        if (trimLeft) {
935            while (workText < workLimit && *workText == ' ') {
936                ++workText;
937            }
938        }
939        if (trimRight) {
940            while (workLimit > workText && *(workLimit - 1) == ' ') {
941                --workLimit;
942            }
943        }
944        int32_t workBytes = (workLimit - workText) << 1;
945
946        canvas->drawText(workText, workBytes, x_, y_, *paint);
947
948        free(buffer);
949    }
950
951    static void drawText___CIIFFIPaint(JNIEnv* env, jobject, SkCanvas* canvas,
952                                      jcharArray text, int index, int count,
953                                      jfloat x, jfloat y, int flags, SkPaint* paint) {
954        jchar* textArray = env->GetCharArrayElements(text, NULL);
955        drawText(env, canvas, textArray + index, count, x, y, flags, paint);
956        env->ReleaseCharArrayElements(text, textArray, JNI_ABORT);
957    }
958
959    static void drawText__StringIIFFIPaint(JNIEnv* env, jobject,
960                                          SkCanvas* canvas, jstring text,
961                                          int start, int end,
962                                          jfloat x, jfloat y, int flags, SkPaint* paint) {
963        const jchar* textArray = env->GetStringChars(text, NULL);
964        drawText(env, canvas, textArray + start, end - start, x, y, flags, paint);
965        env->ReleaseStringChars(text, textArray);
966    }
967
968    // Draws a unidirectional run of text.
969    static void drawTextRun(JNIEnv* env, SkCanvas* canvas, const jchar* chars,
970                              jint start, jint count, jint contextCount,
971                              jfloat x, jfloat y, int dirFlags, SkPaint* paint) {
972
973        SkScalar x_ = SkFloatToScalar(x);
974        SkScalar y_ = SkFloatToScalar(y);
975
976        uint8_t rtl = dirFlags & 0x1;
977        if (rtl) {
978            SkAutoSTMalloc<80, jchar> buffer(contextCount);
979            UErrorCode status = U_ZERO_ERROR;
980            count = shapeRtlText(chars, start, count, contextCount, buffer.get(), status);
981            if (U_SUCCESS(status)) {
982                canvas->drawText(buffer.get(), count << 1, x_, y_, *paint);
983            } else {
984                LOG(LOG_WARN, "LAYOUT", "drawTextRun error %d\n", status);
985            }
986        } else {
987            canvas->drawText(chars + start, count << 1, x_, y_, *paint);
988        }
989    }
990
991    static void drawTextRun___CIIIIFFIPaint(
992        JNIEnv* env, jobject, SkCanvas* canvas, jcharArray text, int index,
993        int count, int contextIndex, int contextCount,
994        jfloat x, jfloat y, int dirFlags, SkPaint* paint) {
995
996        jchar* chars = env->GetCharArrayElements(text, NULL);
997        drawTextRun(env, canvas, chars + contextIndex, index - contextIndex,
998                      count, contextCount, x, y, dirFlags, paint);
999        env->ReleaseCharArrayElements(text, chars, JNI_ABORT);
1000    }
1001
1002    static void drawTextRun__StringIIIIFFIPaint(
1003        JNIEnv* env, jobject obj, SkCanvas* canvas, jstring text, jint start,
1004        jint end, jint contextStart, jint contextEnd,
1005        jfloat x, jfloat y, jint dirFlags, SkPaint* paint) {
1006
1007        jint count = end - start;
1008        jint contextCount = contextEnd - contextStart;
1009        const jchar* chars = env->GetStringChars(text, NULL);
1010        drawTextRun(env, canvas, chars + contextStart, start - contextStart,
1011                      count, contextCount, x, y, dirFlags, paint);
1012        env->ReleaseStringChars(text, chars);
1013    }
1014
1015    static void drawPosText___CII_FPaint(JNIEnv* env, jobject, SkCanvas* canvas,
1016                                         jcharArray text, int index, int count,
1017                                         jfloatArray pos, SkPaint* paint) {
1018        jchar* textArray = text ? env->GetCharArrayElements(text, NULL) : NULL;
1019        jsize textCount = text ? env->GetArrayLength(text) : NULL;
1020        float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
1021        int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
1022        SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
1023        int indx;
1024        for (indx = 0; indx < posCount; indx++) {
1025            posPtr[indx].fX = SkFloatToScalar(posArray[indx << 1]);
1026            posPtr[indx].fY = SkFloatToScalar(posArray[(indx << 1) + 1]);
1027        }
1028        canvas->drawPosText(textArray + index, count << 1, posPtr, *paint);
1029        if (text) {
1030            env->ReleaseCharArrayElements(text, textArray, 0);
1031        }
1032        if (pos) {
1033            env->ReleaseFloatArrayElements(pos, posArray, 0);
1034        }
1035        delete[] posPtr;
1036    }
1037
1038    static void drawPosText__String_FPaint(JNIEnv* env, jobject,
1039                                           SkCanvas* canvas, jstring text,
1040                                           jfloatArray pos,
1041                                           SkPaint* paint) {
1042        const void* text_ = text ? env->GetStringChars(text, NULL) : NULL;
1043        int byteLength = text ? env->GetStringLength(text) : 0;
1044        float* posArray = pos ? env->GetFloatArrayElements(pos, NULL) : NULL;
1045        int posCount = pos ? env->GetArrayLength(pos) >> 1: 0;
1046        SkPoint* posPtr = posCount > 0 ? new SkPoint[posCount] : NULL;
1047
1048        for (int indx = 0; indx < posCount; indx++) {
1049            posPtr[indx].fX = SkFloatToScalar(posArray[indx << 1]);
1050            posPtr[indx].fY = SkFloatToScalar(posArray[(indx << 1) + 1]);
1051        }
1052        canvas->drawPosText(text_, byteLength << 1, posPtr, *paint);
1053        if (text) {
1054            env->ReleaseStringChars(text, (const jchar*) text_);
1055        }
1056        if (pos) {
1057            env->ReleaseFloatArrayElements(pos, posArray, 0);
1058        }
1059        delete[] posPtr;
1060    }
1061
1062    static void drawTextOnPath(JNIEnv *env, SkCanvas* canvas, const jchar* text, int count,
1063            int bidiFlags, SkPath* path, jfloat hOffset, jfloat vOffset, SkPaint* paint) {
1064
1065        if (!needsLayout(text, count, bidiFlags)) {
1066            canvas->drawTextOnPathHV(text, count << 1, *path,
1067                SkFloatToScalar(hOffset), SkFloatToScalar(vOffset), *paint);
1068            return;
1069        }
1070
1071        SkAutoSTMalloc<80, jchar> buffer(count);
1072        int dir = kDirection_LTR;
1073        UErrorCode status = U_ZERO_ERROR;
1074        count = layoutLine(text, count, bidiFlags, dir, buffer.get(), status);
1075        if (U_SUCCESS(status)) {
1076            canvas->drawTextOnPathHV(buffer.get(), count << 1, *path,
1077                SkFloatToScalar(hOffset), SkFloatToScalar(vOffset), *paint);
1078        }
1079    }
1080
1081    static void drawTextOnPath___CIIPathFFPaint(JNIEnv* env, jobject,
1082            SkCanvas* canvas, jcharArray text, int index, int count,
1083            SkPath* path, jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
1084
1085        jchar* textArray = env->GetCharArrayElements(text, NULL);
1086        drawTextOnPath(env, canvas, textArray, count, bidiFlags, path, hOffset, vOffset, paint);
1087        env->ReleaseCharArrayElements(text, textArray, 0);
1088    }
1089
1090    static void drawTextOnPath__StringPathFFPaint(JNIEnv* env, jobject,
1091            SkCanvas* canvas, jstring text, SkPath* path,
1092            jfloat hOffset, jfloat vOffset, jint bidiFlags, SkPaint* paint) {
1093        const jchar* text_ = env->GetStringChars(text, NULL);
1094        int count = env->GetStringLength(text);
1095        drawTextOnPath(env, canvas, text_, count, bidiFlags, path, hOffset, vOffset, paint);
1096        env->ReleaseStringChars(text, text_);
1097    }
1098
1099    static bool getClipBounds(JNIEnv* env, jobject, SkCanvas* canvas,
1100                              jobject bounds) {
1101        SkRect   r;
1102        SkIRect ir;
1103        bool     result = canvas->getClipBounds(&r, SkCanvas::kBW_EdgeType);
1104
1105        r.round(&ir);
1106        (void)GraphicsJNI::irect_to_jrect(ir, env, bounds);
1107        return result;
1108    }
1109
1110    static void getCTM(JNIEnv* env, jobject, SkCanvas* canvas,
1111                       SkMatrix* matrix) {
1112        *matrix = canvas->getTotalMatrix();
1113    }
1114};
1115
1116static JNINativeMethod gCanvasMethods[] = {
1117    {"finalizer", "(I)V", (void*) SkCanvasGlue::finalizer},
1118    {"initRaster","(I)I", (void*) SkCanvasGlue::initRaster},
1119    {"initGL","()I", (void*) SkCanvasGlue::initGL},
1120    {"isOpaque","()Z", (void*) SkCanvasGlue::isOpaque},
1121    {"getWidth","()I", (void*) SkCanvasGlue::getWidth},
1122    {"getHeight","()I", (void*) SkCanvasGlue::getHeight},
1123    {"native_setBitmap","(II)V", (void*) SkCanvasGlue::setBitmap},
1124    {"nativeSetViewport", "(III)V", (void*) SkCanvasGlue::setViewport},
1125    {"save","()I", (void*) SkCanvasGlue::saveAll},
1126    {"save","(I)I", (void*) SkCanvasGlue::save},
1127    {"native_saveLayer","(ILandroid/graphics/RectF;II)I",
1128        (void*) SkCanvasGlue::saveLayer},
1129    {"native_saveLayer","(IFFFFII)I", (void*) SkCanvasGlue::saveLayer4F},
1130    {"native_saveLayerAlpha","(ILandroid/graphics/RectF;II)I",
1131        (void*) SkCanvasGlue::saveLayerAlpha},
1132    {"native_saveLayerAlpha","(IFFFFII)I",
1133        (void*) SkCanvasGlue::saveLayerAlpha4F},
1134    {"restore","()V", (void*) SkCanvasGlue::restore},
1135    {"getSaveCount","()I", (void*) SkCanvasGlue::getSaveCount},
1136    {"restoreToCount","(I)V", (void*) SkCanvasGlue::restoreToCount},
1137    {"translate","(FF)V", (void*) SkCanvasGlue::translate},
1138    {"scale","(FF)V", (void*) SkCanvasGlue::scale__FF},
1139    {"rotate","(F)V", (void*) SkCanvasGlue::rotate__F},
1140    {"skew","(FF)V", (void*) SkCanvasGlue::skew__FF},
1141    {"native_concat","(II)V", (void*) SkCanvasGlue::concat},
1142    {"native_setMatrix","(II)V", (void*) SkCanvasGlue::setMatrix},
1143    {"clipRect","(FFFF)Z", (void*) SkCanvasGlue::clipRect_FFFF},
1144    {"clipRect","(IIII)Z", (void*) SkCanvasGlue::clipRect_IIII},
1145    {"clipRect","(Landroid/graphics/RectF;)Z",
1146        (void*) SkCanvasGlue::clipRect_RectF},
1147    {"clipRect","(Landroid/graphics/Rect;)Z",
1148        (void*) SkCanvasGlue::clipRect_Rect},
1149    {"native_clipRect","(IFFFFI)Z", (void*) SkCanvasGlue::clipRect},
1150    {"native_clipPath","(III)Z", (void*) SkCanvasGlue::clipPath},
1151    {"native_clipRegion","(III)Z", (void*) SkCanvasGlue::clipRegion},
1152    {"nativeSetDrawFilter", "(II)V", (void*) SkCanvasGlue::setDrawFilter},
1153    {"native_getClipBounds","(ILandroid/graphics/Rect;)Z",
1154        (void*) SkCanvasGlue::getClipBounds},
1155    {"native_getCTM", "(II)V", (void*)SkCanvasGlue::getCTM},
1156    {"native_quickReject","(ILandroid/graphics/RectF;I)Z",
1157        (void*) SkCanvasGlue::quickReject__RectFI},
1158    {"native_quickReject","(III)Z", (void*) SkCanvasGlue::quickReject__PathI},
1159    {"native_quickReject","(IFFFFI)Z", (void*)SkCanvasGlue::quickReject__FFFFI},
1160    {"native_drawRGB","(IIII)V", (void*) SkCanvasGlue::drawRGB},
1161    {"native_drawARGB","(IIIII)V", (void*) SkCanvasGlue::drawARGB},
1162    {"native_drawColor","(II)V", (void*) SkCanvasGlue::drawColor__I},
1163    {"native_drawColor","(III)V", (void*) SkCanvasGlue::drawColor__II},
1164    {"native_drawPaint","(II)V", (void*) SkCanvasGlue::drawPaint},
1165    {"drawPoint", "(FFLandroid/graphics/Paint;)V",
1166    (void*) SkCanvasGlue::drawPoint},
1167    {"drawPoints", "([FIILandroid/graphics/Paint;)V",
1168        (void*) SkCanvasGlue::drawPoints},
1169    {"drawLines", "([FIILandroid/graphics/Paint;)V",
1170        (void*) SkCanvasGlue::drawLines},
1171    {"native_drawLine","(IFFFFI)V", (void*) SkCanvasGlue::drawLine__FFFFPaint},
1172    {"native_drawRect","(ILandroid/graphics/RectF;I)V",
1173        (void*) SkCanvasGlue::drawRect__RectFPaint},
1174    {"native_drawRect","(IFFFFI)V", (void*) SkCanvasGlue::drawRect__FFFFPaint},
1175    {"native_drawOval","(ILandroid/graphics/RectF;I)V",
1176        (void*) SkCanvasGlue::drawOval},
1177    {"native_drawCircle","(IFFFI)V", (void*) SkCanvasGlue::drawCircle},
1178    {"native_drawArc","(ILandroid/graphics/RectF;FFZI)V",
1179        (void*) SkCanvasGlue::drawArc},
1180    {"native_drawRoundRect","(ILandroid/graphics/RectF;FFI)V",
1181        (void*) SkCanvasGlue::drawRoundRect},
1182    {"native_drawPath","(III)V", (void*) SkCanvasGlue::drawPath},
1183    {"native_drawBitmap","(IIFFIIII)V",
1184        (void*) SkCanvasGlue::drawBitmap__BitmapFFPaint},
1185    {"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/RectF;III)V",
1186        (void*) SkCanvasGlue::drawBitmapRF},
1187    {"native_drawBitmap","(IILandroid/graphics/Rect;Landroid/graphics/Rect;III)V",
1188        (void*) SkCanvasGlue::drawBitmapRR},
1189    {"native_drawBitmap", "(I[IIIFFIIZI)V",
1190    (void*)SkCanvasGlue::drawBitmapArray},
1191    {"nativeDrawBitmapMatrix", "(IIII)V",
1192        (void*)SkCanvasGlue::drawBitmapMatrix},
1193    {"nativeDrawBitmapMesh", "(IIII[FI[III)V",
1194        (void*)SkCanvasGlue::drawBitmapMesh},
1195    {"nativeDrawVertices", "(III[FI[FI[II[SIII)V",
1196        (void*)SkCanvasGlue::drawVertices},
1197    {"native_drawText","(I[CIIFFII)V",
1198        (void*) SkCanvasGlue::drawText___CIIFFIPaint},
1199    {"native_drawText","(ILjava/lang/String;IIFFII)V",
1200        (void*) SkCanvasGlue::drawText__StringIIFFIPaint},
1201    {"native_drawTextRun","(I[CIIIIFFII)V",
1202        (void*) SkCanvasGlue::drawTextRun___CIIIIFFIPaint},
1203    {"native_drawTextRun","(ILjava/lang/String;IIIIFFII)V",
1204        (void*) SkCanvasGlue::drawTextRun__StringIIIIFFIPaint},
1205    {"native_drawPosText","(I[CII[FI)V",
1206        (void*) SkCanvasGlue::drawPosText___CII_FPaint},
1207    {"native_drawPosText","(ILjava/lang/String;[FI)V",
1208        (void*) SkCanvasGlue::drawPosText__String_FPaint},
1209    {"native_drawTextOnPath","(I[CIIIFFII)V",
1210        (void*) SkCanvasGlue::drawTextOnPath___CIIPathFFPaint},
1211    {"native_drawTextOnPath","(ILjava/lang/String;IFFII)V",
1212        (void*) SkCanvasGlue::drawTextOnPath__StringPathFFPaint},
1213    {"native_drawPicture", "(II)V", (void*) SkCanvasGlue::drawPicture},
1214
1215    {"freeCaches", "()V", (void*) SkCanvasGlue::freeCaches}
1216};
1217
1218///////////////////////////////////////////////////////////////////////////////
1219
1220static void BoundaryPatch_computeCubic(JNIEnv* env, jobject, jfloatArray jpts,
1221                                   int texW, int texH, int rows, int cols,
1222                                   jfloatArray jverts, jshortArray jidx) {
1223    AutoJavaFloatArray ptsArray(env, jpts, 24, kRO_JNIAccess);
1224
1225    int vertCount = rows * cols;
1226    AutoJavaFloatArray vertsArray(env, jverts, vertCount * 4, kRW_JNIAccess);
1227    SkPoint* verts = (SkPoint*)vertsArray.ptr();
1228    SkPoint* texs = verts + vertCount;
1229
1230    int idxCount = (rows - 1) * (cols - 1) * 6;
1231    AutoJavaShortArray idxArray(env, jidx, idxCount, kRW_JNIAccess);
1232    uint16_t* idx = (uint16_t*)idxArray.ptr();  // cast from int16_t*
1233
1234    SkCubicBoundary cubic;
1235    memcpy(cubic.fPts, ptsArray.ptr(), 12 * sizeof(SkPoint));
1236
1237    SkBoundaryPatch patch;
1238    patch.setBoundary(&cubic);
1239    // generate our verts
1240    patch.evalPatch(verts, rows, cols);
1241
1242    SkMeshIndices mesh;
1243    // generate our texs and idx
1244    mesh.init(texs, idx, texW, texH, rows, cols);
1245}
1246
1247static JNINativeMethod gBoundaryPatchMethods[] = {
1248    {"nativeComputeCubicPatch", "([FIIII[F[S)V",
1249    (void*)BoundaryPatch_computeCubic },
1250};
1251
1252///////////////////////////////////////////////////////////////////////////////
1253
1254#include <android_runtime/AndroidRuntime.h>
1255
1256#define REG(env, name, array) \
1257    result = android::AndroidRuntime::registerNativeMethods(env, name, array, \
1258                                                    SK_ARRAY_COUNT(array));  \
1259    if (result < 0) return result
1260
1261int register_android_graphics_Canvas(JNIEnv* env) {
1262    int result;
1263
1264    REG(env, "android/graphics/Canvas", gCanvasMethods);
1265    REG(env, "android/graphics/utils/BoundaryPatch", gBoundaryPatchMethods);
1266
1267    return result;
1268}
1269
1270}
1271