Graphics.cpp revision c04851fd0af87f44a7d7351e0c17442fa1d3fc28
1#include "jni.h"
2#include "GraphicsJNI.h"
3#include "NIOBuffer.h"
4#include "SkPicture.h"
5#include "SkRegion.h"
6#include <android_runtime/AndroidRuntime.h>
7
8//#define REPORT_SIZE_TO_JVM
9//#define TRACK_LOCK_COUNT
10
11void doThrow(JNIEnv* env, const char* exc, const char* msg) {
12    // don't throw a new exception if we already have one pending
13    if (env->ExceptionCheck() == JNI_FALSE) {
14        jclass npeClazz;
15
16        npeClazz = env->FindClass(exc);
17        LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
18
19        env->ThrowNew(npeClazz, msg);
20    }
21}
22
23void doThrowNPE(JNIEnv* env) {
24    doThrow(env, "java/lang/NullPointerException");
25}
26
27void doThrowAIOOBE(JNIEnv* env) {
28    doThrow(env, "java/lang/ArrayIndexOutOfBoundsException");
29}
30
31void doThrowRE(JNIEnv* env, const char* msg) {
32    doThrow(env, "java/lang/RuntimeException", msg);
33}
34
35void doThrowIAE(JNIEnv* env, const char* msg) {
36    doThrow(env, "java/lang/IllegalArgumentException", msg);
37}
38
39void doThrowISE(JNIEnv* env, const char* msg) {
40    doThrow(env, "java/lang/IllegalStateException", msg);
41}
42
43void doThrowOOME(JNIEnv* env, const char* msg) {
44    doThrow(env, "java/lang/OutOfMemoryError", msg);
45}
46
47bool GraphicsJNI::hasException(JNIEnv *env) {
48    if (env->ExceptionCheck() != 0) {
49        LOGE("*** Uncaught exception returned from Java call!\n");
50        env->ExceptionDescribe();
51        return true;
52    }
53    return false;
54}
55
56///////////////////////////////////////////////////////////////////////////////
57
58AutoJavaFloatArray::AutoJavaFloatArray(JNIEnv* env, jfloatArray array,
59                                       int minLength, JNIAccess access)
60: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
61    SkASSERT(env);
62    if (array) {
63        fLen = env->GetArrayLength(array);
64        if (fLen < minLength) {
65            sk_throw();
66        }
67        fPtr = env->GetFloatArrayElements(array, NULL);
68    }
69    fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0;
70}
71
72AutoJavaFloatArray::~AutoJavaFloatArray() {
73    if (fPtr) {
74        fEnv->ReleaseFloatArrayElements(fArray, fPtr, fReleaseMode);
75    }
76}
77
78AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array,
79                                       int minLength)
80: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
81    SkASSERT(env);
82    if (array) {
83        fLen = env->GetArrayLength(array);
84        if (fLen < minLength) {
85            sk_throw();
86        }
87        fPtr = env->GetIntArrayElements(array, NULL);
88    }
89}
90
91AutoJavaIntArray::~AutoJavaIntArray() {
92    if (fPtr) {
93        fEnv->ReleaseIntArrayElements(fArray, fPtr, 0);
94    }
95}
96
97AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array,
98                                       int minLength, JNIAccess access)
99: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
100    SkASSERT(env);
101    if (array) {
102        fLen = env->GetArrayLength(array);
103        if (fLen < minLength) {
104            sk_throw();
105        }
106        fPtr = env->GetShortArrayElements(array, NULL);
107    }
108    fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0;
109}
110
111AutoJavaShortArray::~AutoJavaShortArray() {
112    if (fPtr) {
113        fEnv->ReleaseShortArrayElements(fArray, fPtr, fReleaseMode);
114    }
115}
116
117AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array,
118                                       int minLength)
119: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
120    SkASSERT(env);
121    if (array) {
122        fLen = env->GetArrayLength(array);
123        if (fLen < minLength) {
124            sk_throw();
125        }
126        fPtr = env->GetByteArrayElements(array, NULL);
127    }
128}
129
130AutoJavaByteArray::~AutoJavaByteArray() {
131    if (fPtr) {
132        fEnv->ReleaseByteArrayElements(fArray, fPtr, 0);
133    }
134}
135
136///////////////////////////////////////////////////////////////////////////////
137
138static jclass   gRect_class;
139static jfieldID gRect_leftFieldID;
140static jfieldID gRect_topFieldID;
141static jfieldID gRect_rightFieldID;
142static jfieldID gRect_bottomFieldID;
143
144static jclass   gRectF_class;
145static jfieldID gRectF_leftFieldID;
146static jfieldID gRectF_topFieldID;
147static jfieldID gRectF_rightFieldID;
148static jfieldID gRectF_bottomFieldID;
149
150static jclass   gPoint_class;
151static jfieldID gPoint_xFieldID;
152static jfieldID gPoint_yFieldID;
153
154static jclass   gPointF_class;
155static jfieldID gPointF_xFieldID;
156static jfieldID gPointF_yFieldID;
157
158static jclass   gBitmap_class;
159static jfieldID gBitmap_nativeInstanceID;
160static jmethodID gBitmap_constructorMethodID;
161static jmethodID gBitmap_allocBufferMethodID;
162
163static jclass   gBitmapConfig_class;
164static jfieldID gBitmapConfig_nativeInstanceID;
165
166static jclass   gCanvas_class;
167static jfieldID gCanvas_nativeInstanceID;
168
169static jclass   gPaint_class;
170static jfieldID gPaint_nativeInstanceID;
171
172static jclass   gPicture_class;
173static jfieldID gPicture_nativeInstanceID;
174
175static jclass   gRegion_class;
176static jfieldID gRegion_nativeInstanceID;
177static jmethodID gRegion_constructorMethodID;
178
179static jobject   gVMRuntime_singleton;
180static jmethodID gVMRuntime_trackExternalAllocationMethodID;
181static jmethodID gVMRuntime_trackExternalFreeMethodID;
182
183///////////////////////////////////////////////////////////////////////////////
184
185void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
186{
187    SkASSERT(env->IsInstanceOf(obj, gRect_class));
188
189    *L = env->GetIntField(obj, gRect_leftFieldID);
190    *T = env->GetIntField(obj, gRect_topFieldID);
191    *R = env->GetIntField(obj, gRect_rightFieldID);
192    *B = env->GetIntField(obj, gRect_bottomFieldID);
193}
194
195void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B)
196{
197    SkASSERT(env->IsInstanceOf(obj, gRect_class));
198
199    env->SetIntField(obj, gRect_leftFieldID, L);
200    env->SetIntField(obj, gRect_topFieldID, T);
201    env->SetIntField(obj, gRect_rightFieldID, R);
202    env->SetIntField(obj, gRect_bottomFieldID, B);
203}
204
205SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir)
206{
207    SkASSERT(env->IsInstanceOf(obj, gRect_class));
208
209    ir->set(env->GetIntField(obj, gRect_leftFieldID),
210            env->GetIntField(obj, gRect_topFieldID),
211            env->GetIntField(obj, gRect_rightFieldID),
212            env->GetIntField(obj, gRect_bottomFieldID));
213    return ir;
214}
215
216void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj)
217{
218    SkASSERT(env->IsInstanceOf(obj, gRect_class));
219
220    env->SetIntField(obj, gRect_leftFieldID, ir.fLeft);
221    env->SetIntField(obj, gRect_topFieldID, ir.fTop);
222    env->SetIntField(obj, gRect_rightFieldID, ir.fRight);
223    env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom);
224}
225
226SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r)
227{
228    SkASSERT(env->IsInstanceOf(obj, gRectF_class));
229
230    r->set(SkFloatToScalar(env->GetFloatField(obj, gRectF_leftFieldID)),
231           SkFloatToScalar(env->GetFloatField(obj, gRectF_topFieldID)),
232           SkFloatToScalar(env->GetFloatField(obj, gRectF_rightFieldID)),
233           SkFloatToScalar(env->GetFloatField(obj, gRectF_bottomFieldID)));
234    return r;
235}
236
237SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r)
238{
239    SkASSERT(env->IsInstanceOf(obj, gRect_class));
240
241    r->set(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)),
242           SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)),
243           SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)),
244           SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID)));
245    return r;
246}
247
248void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj)
249{
250    SkASSERT(env->IsInstanceOf(obj, gRectF_class));
251
252    env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft));
253    env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop));
254    env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight));
255    env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom));
256}
257
258SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point)
259{
260    SkASSERT(env->IsInstanceOf(obj, gPoint_class));
261
262    point->set(env->GetIntField(obj, gPoint_xFieldID),
263               env->GetIntField(obj, gPoint_yFieldID));
264    return point;
265}
266
267void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj)
268{
269    SkASSERT(env->IsInstanceOf(obj, gPoint_class));
270
271    env->SetIntField(obj, gPoint_xFieldID, ir.fX);
272    env->SetIntField(obj, gPoint_yFieldID, ir.fY);
273}
274
275SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point)
276{
277    SkASSERT(env->IsInstanceOf(obj, gPointF_class));
278
279    point->set(SkFloatToScalar(env->GetIntField(obj, gPointF_xFieldID)),
280               SkFloatToScalar(env->GetIntField(obj, gPointF_yFieldID)));
281    return point;
282}
283
284void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj)
285{
286    SkASSERT(env->IsInstanceOf(obj, gPointF_class));
287
288    env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX));
289    env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY));
290}
291
292SkBitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) {
293    SkASSERT(env);
294    SkASSERT(bitmap);
295    SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
296    SkBitmap* b = (SkBitmap*)env->GetIntField(bitmap, gBitmap_nativeInstanceID);
297    SkASSERT(b);
298    return b;
299}
300
301SkBitmap::Config GraphicsJNI::getNativeBitmapConfig(JNIEnv* env,
302                                                    jobject jconfig) {
303    SkASSERT(env);
304    if (NULL == jconfig) {
305        return SkBitmap::kNo_Config;
306    }
307    SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class));
308    int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
309    if (c < 0 || c >= SkBitmap::kConfigCount) {
310        c = SkBitmap::kNo_Config;
311    }
312    return static_cast<SkBitmap::Config>(c);
313}
314
315SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
316    SkASSERT(env);
317    SkASSERT(canvas);
318    SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
319    SkCanvas* c = (SkCanvas*)env->GetIntField(canvas, gCanvas_nativeInstanceID);
320    SkASSERT(c);
321    return c;
322}
323
324SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) {
325    SkASSERT(env);
326    SkASSERT(paint);
327    SkASSERT(env->IsInstanceOf(paint, gPaint_class));
328    SkPaint* p = (SkPaint*)env->GetIntField(paint, gPaint_nativeInstanceID);
329    SkASSERT(p);
330    return p;
331}
332
333SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture)
334{
335    SkASSERT(env);
336    SkASSERT(picture);
337    SkASSERT(env->IsInstanceOf(picture, gPicture_class));
338    SkPicture* p = (SkPicture*)env->GetIntField(picture, gPicture_nativeInstanceID);
339    SkASSERT(p);
340    return p;
341}
342
343SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
344{
345    SkASSERT(env);
346    SkASSERT(region);
347    SkASSERT(env->IsInstanceOf(region, gRegion_class));
348    SkRegion* r = (SkRegion*)env->GetIntField(region, gRegion_nativeInstanceID);
349    SkASSERT(r);
350    return r;
351}
352
353///////////////////////////////////////////////////////////////////////////////////////////
354
355jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
356                                  jbyteArray ninepatch, int density)
357{
358    SkASSERT(bitmap != NULL);
359    SkASSERT(NULL != bitmap->pixelRef());
360
361    jobject obj = env->AllocObject(gBitmap_class);
362    if (obj) {
363        env->CallVoidMethod(obj, gBitmap_constructorMethodID,
364                            (jint)bitmap, isMutable, ninepatch, density);
365        if (hasException(env)) {
366            obj = NULL;
367        }
368    }
369    return obj;
370}
371
372jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
373{
374    SkASSERT(region != NULL);
375    jobject obj = env->AllocObject(gRegion_class);
376    if (obj) {
377        env->CallVoidMethod(obj, gRegion_constructorMethodID, (jint)region, 0);
378        if (hasException(env)) {
379            obj = NULL;
380        }
381    }
382    return obj;
383}
384
385#include "SkPixelRef.h"
386
387static JNIEnv* vm2env(JavaVM* vm)
388{
389    JNIEnv* env = NULL;
390    if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || NULL == env)
391    {
392        SkDebugf("------- [%p] vm->GetEnv() failed\n", vm);
393        sk_throw();
394    }
395    return env;
396}
397
398#ifdef TRACK_LOCK_COUNT
399    static int gLockCount;
400#endif
401
402///////////////////////////////////////////////////////////////////////////////
403
404#include "SkMallocPixelRef.h"
405
406/*  Extend SkMallocPixelRef to inform the VM when we free up the storage
407*/
408class AndroidPixelRef : public SkMallocPixelRef {
409public:
410    /** Allocate the specified buffer for pixels. The memory is freed when the
411        last owner of this pixelref is gone. Our caller has already informed
412        the VM of our allocation.
413    */
414    AndroidPixelRef(JNIEnv* env, void* storage, size_t size,
415            SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) {
416        SkASSERT(storage);
417        SkASSERT(env);
418
419        if (env->GetJavaVM(&fVM) != JNI_OK) {
420            SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
421            sk_throw();
422        }
423    }
424
425    virtual ~AndroidPixelRef() {
426        JNIEnv* env = vm2env(fVM);
427//        SkDebugf("-------------- inform VM we're releasing %d bytes\n", this->getSize());
428        jlong jsize = this->getSize();  // the VM wants longs for the size
429        env->CallVoidMethod(gVMRuntime_singleton,
430                            gVMRuntime_trackExternalFreeMethodID,
431                            jsize);
432        if (GraphicsJNI::hasException(env)) {
433            env->ExceptionClear();
434        }
435    }
436
437private:
438    JavaVM* fVM;
439};
440
441bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
442                                  SkColorTable* ctable, bool reportSizeToVM) {
443    Sk64 size64 = bitmap->getSize64();
444    if (size64.isNeg() || !size64.is32()) {
445        doThrow(env, "java/lang/IllegalArgumentException",
446                     "bitmap size exceeds 32bits");
447        return false;
448    }
449
450    size_t size = size64.get32();
451    jlong jsize = size;  // the VM wants longs for the size
452    if (reportSizeToVM) {
453        //    SkDebugf("-------------- inform VM we've allocated %d bytes\n", size);
454        bool r = env->CallBooleanMethod(gVMRuntime_singleton,
455                                    gVMRuntime_trackExternalAllocationMethodID,
456                                    jsize);
457        if (GraphicsJNI::hasException(env)) {
458            return false;
459        }
460        if (!r) {
461            LOGE("VM won't let us allocate %zd bytes\n", size);
462            doThrowOOME(env, "bitmap size exceeds VM budget");
463            return false;
464        }
465    }
466    // call the version of malloc that returns null on failure
467    void* addr = sk_malloc_flags(size, 0);
468    if (NULL == addr) {
469        if (reportSizeToVM) {
470            //        SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size);
471            // we didn't actually allocate it, so inform the VM
472            env->CallVoidMethod(gVMRuntime_singleton,
473                                 gVMRuntime_trackExternalFreeMethodID,
474                                 jsize);
475            if (!GraphicsJNI::hasException(env)) {
476                doThrowOOME(env, "bitmap size too large for malloc");
477            }
478        }
479        return false;
480    }
481
482    SkPixelRef* pr = reportSizeToVM ?
483                        new AndroidPixelRef(env, addr, size, ctable) :
484                        new SkMallocPixelRef(addr, size, ctable);
485    bitmap->setPixelRef(pr)->unref();
486    // since we're already allocated, we lockPixels right away
487    // HeapAllocator behaves this way too
488    bitmap->lockPixels();
489    return true;
490}
491
492///////////////////////////////////////////////////////////////////////////////
493
494JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM)
495    : fEnv(env), fReportSizeToVM(reportSizeToVM) {}
496
497bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
498    return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM);
499}
500
501////////////////////////////////////////////////////////////////////////////////
502
503static jclass make_globalref(JNIEnv* env, const char classname[])
504{
505    jclass c = env->FindClass(classname);
506    SkASSERT(c);
507    return (jclass)env->NewGlobalRef(c);
508}
509
510static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
511                                const char fieldname[], const char type[])
512{
513    jfieldID id = env->GetFieldID(clazz, fieldname, type);
514    SkASSERT(id);
515    return id;
516}
517
518int register_android_graphics_Graphics(JNIEnv* env)
519{
520    jmethodID m;
521    jclass c;
522
523    gRect_class = make_globalref(env, "android/graphics/Rect");
524    gRect_leftFieldID = getFieldIDCheck(env, gRect_class, "left", "I");
525    gRect_topFieldID = getFieldIDCheck(env, gRect_class, "top", "I");
526    gRect_rightFieldID = getFieldIDCheck(env, gRect_class, "right", "I");
527    gRect_bottomFieldID = getFieldIDCheck(env, gRect_class, "bottom", "I");
528
529    gRectF_class = make_globalref(env, "android/graphics/RectF");
530    gRectF_leftFieldID = getFieldIDCheck(env, gRectF_class, "left", "F");
531    gRectF_topFieldID = getFieldIDCheck(env, gRectF_class, "top", "F");
532    gRectF_rightFieldID = getFieldIDCheck(env, gRectF_class, "right", "F");
533    gRectF_bottomFieldID = getFieldIDCheck(env, gRectF_class, "bottom", "F");
534
535    gPoint_class = make_globalref(env, "android/graphics/Point");
536    gPoint_xFieldID = getFieldIDCheck(env, gPoint_class, "x", "I");
537    gPoint_yFieldID = getFieldIDCheck(env, gPoint_class, "y", "I");
538
539    gPointF_class = make_globalref(env, "android/graphics/PointF");
540    gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F");
541    gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F");
542
543    gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
544    gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
545    gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
546                                            "(IZ[BI)V");
547
548    gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
549    gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
550                                                     "nativeInt", "I");
551
552    gCanvas_class = make_globalref(env, "android/graphics/Canvas");
553    gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I");
554
555    gPaint_class = make_globalref(env, "android/graphics/Paint");
556    gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I");
557
558    gPicture_class = make_globalref(env, "android/graphics/Picture");
559    gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "I");
560
561    gRegion_class = make_globalref(env, "android/graphics/Region");
562    gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "I");
563    gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>",
564        "(II)V");
565
566    // Get the VMRuntime class.
567    c = env->FindClass("dalvik/system/VMRuntime");
568    SkASSERT(c);
569    // Look up VMRuntime.getRuntime().
570    m = env->GetStaticMethodID(c, "getRuntime", "()Ldalvik/system/VMRuntime;");
571    SkASSERT(m);
572    // Call VMRuntime.getRuntime() and hold onto its result.
573    gVMRuntime_singleton = env->CallStaticObjectMethod(c, m);
574    SkASSERT(gVMRuntime_singleton);
575    gVMRuntime_singleton = (jobject)env->NewGlobalRef(gVMRuntime_singleton);
576    // Look up the VMRuntime methods we'll be using.
577    gVMRuntime_trackExternalAllocationMethodID =
578                        env->GetMethodID(c, "trackExternalAllocation", "(J)Z");
579    gVMRuntime_trackExternalFreeMethodID =
580                            env->GetMethodID(c, "trackExternalFree", "(J)V");
581
582    NIOBuffer::RegisterJNI(env);
583
584    return 0;
585}
586
587