Graphics.cpp revision afde46ed008f150e45e1b0d7e1dc588fc047b74f
1#define LOG_TAG "GraphicsJNI"
2
3#include "jni.h"
4#include "GraphicsJNI.h"
5
6#include "SkCanvas.h"
7#include "SkDevice.h"
8#include "SkPicture.h"
9#include "SkRegion.h"
10#include <android_runtime/AndroidRuntime.h>
11
12//#define REPORT_SIZE_TO_JVM
13//#define TRACK_LOCK_COUNT
14
15void doThrow(JNIEnv* env, const char* exc, const char* msg) {
16    // don't throw a new exception if we already have one pending
17    if (env->ExceptionCheck() == JNI_FALSE) {
18        jclass npeClazz;
19
20        npeClazz = env->FindClass(exc);
21        LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
22
23        env->ThrowNew(npeClazz, msg);
24    }
25}
26
27void doThrowNPE(JNIEnv* env) {
28    doThrow(env, "java/lang/NullPointerException");
29}
30
31void doThrowAIOOBE(JNIEnv* env) {
32    doThrow(env, "java/lang/ArrayIndexOutOfBoundsException");
33}
34
35void doThrowRE(JNIEnv* env, const char* msg) {
36    doThrow(env, "java/lang/RuntimeException", msg);
37}
38
39void doThrowIAE(JNIEnv* env, const char* msg) {
40    doThrow(env, "java/lang/IllegalArgumentException", msg);
41}
42
43void doThrowISE(JNIEnv* env, const char* msg) {
44    doThrow(env, "java/lang/IllegalStateException", msg);
45}
46
47void doThrowOOME(JNIEnv* env, const char* msg) {
48    doThrow(env, "java/lang/OutOfMemoryError", msg);
49}
50
51void doThrowIOE(JNIEnv* env, const char* msg) {
52    doThrow(env, "java/io/IOException", msg);
53}
54
55bool GraphicsJNI::hasException(JNIEnv *env) {
56    if (env->ExceptionCheck() != 0) {
57        LOGE("*** Uncaught exception returned from Java call!\n");
58        env->ExceptionDescribe();
59        return true;
60    }
61    return false;
62}
63
64///////////////////////////////////////////////////////////////////////////////
65
66AutoJavaFloatArray::AutoJavaFloatArray(JNIEnv* env, jfloatArray array,
67                                       int minLength, JNIAccess access)
68: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
69    SkASSERT(env);
70    if (array) {
71        fLen = env->GetArrayLength(array);
72        if (fLen < minLength) {
73            sk_throw();
74        }
75        fPtr = env->GetFloatArrayElements(array, NULL);
76    }
77    fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0;
78}
79
80AutoJavaFloatArray::~AutoJavaFloatArray() {
81    if (fPtr) {
82        fEnv->ReleaseFloatArrayElements(fArray, fPtr, fReleaseMode);
83    }
84}
85
86AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array,
87                                       int minLength)
88: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
89    SkASSERT(env);
90    if (array) {
91        fLen = env->GetArrayLength(array);
92        if (fLen < minLength) {
93            sk_throw();
94        }
95        fPtr = env->GetIntArrayElements(array, NULL);
96    }
97}
98
99AutoJavaIntArray::~AutoJavaIntArray() {
100    if (fPtr) {
101        fEnv->ReleaseIntArrayElements(fArray, fPtr, 0);
102    }
103}
104
105AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array,
106                                       int minLength, JNIAccess access)
107: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
108    SkASSERT(env);
109    if (array) {
110        fLen = env->GetArrayLength(array);
111        if (fLen < minLength) {
112            sk_throw();
113        }
114        fPtr = env->GetShortArrayElements(array, NULL);
115    }
116    fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0;
117}
118
119AutoJavaShortArray::~AutoJavaShortArray() {
120    if (fPtr) {
121        fEnv->ReleaseShortArrayElements(fArray, fPtr, fReleaseMode);
122    }
123}
124
125AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array,
126                                       int minLength)
127: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
128    SkASSERT(env);
129    if (array) {
130        fLen = env->GetArrayLength(array);
131        if (fLen < minLength) {
132            sk_throw();
133        }
134        fPtr = env->GetByteArrayElements(array, NULL);
135    }
136}
137
138AutoJavaByteArray::~AutoJavaByteArray() {
139    if (fPtr) {
140        fEnv->ReleaseByteArrayElements(fArray, fPtr, 0);
141    }
142}
143
144///////////////////////////////////////////////////////////////////////////////
145
146static jclass   gRect_class;
147static jfieldID gRect_leftFieldID;
148static jfieldID gRect_topFieldID;
149static jfieldID gRect_rightFieldID;
150static jfieldID gRect_bottomFieldID;
151
152static jclass   gRectF_class;
153static jfieldID gRectF_leftFieldID;
154static jfieldID gRectF_topFieldID;
155static jfieldID gRectF_rightFieldID;
156static jfieldID gRectF_bottomFieldID;
157
158static jclass   gPoint_class;
159static jfieldID gPoint_xFieldID;
160static jfieldID gPoint_yFieldID;
161
162static jclass   gPointF_class;
163static jfieldID gPointF_xFieldID;
164static jfieldID gPointF_yFieldID;
165
166static jclass   gBitmap_class;
167static jfieldID gBitmap_nativeInstanceID;
168static jmethodID gBitmap_constructorMethodID;
169
170static jclass   gBitmapConfig_class;
171static jfieldID gBitmapConfig_nativeInstanceID;
172
173static jclass   gBitmapRegionDecoder_class;
174static jmethodID gBitmapRegionDecoder_constructorMethodID;
175
176static jclass   gCanvas_class;
177static jfieldID gCanvas_nativeInstanceID;
178
179static jclass   gPaint_class;
180static jfieldID gPaint_nativeInstanceID;
181
182static jclass   gPicture_class;
183static jfieldID gPicture_nativeInstanceID;
184
185static jclass   gRegion_class;
186static jfieldID gRegion_nativeInstanceID;
187static jmethodID gRegion_constructorMethodID;
188
189static jobject   gVMRuntime_singleton;
190static jmethodID gVMRuntime_trackExternalAllocationMethodID;
191static jmethodID gVMRuntime_trackExternalFreeMethodID;
192
193///////////////////////////////////////////////////////////////////////////////
194
195void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
196{
197    SkASSERT(env->IsInstanceOf(obj, gRect_class));
198
199    *L = env->GetIntField(obj, gRect_leftFieldID);
200    *T = env->GetIntField(obj, gRect_topFieldID);
201    *R = env->GetIntField(obj, gRect_rightFieldID);
202    *B = env->GetIntField(obj, gRect_bottomFieldID);
203}
204
205void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B)
206{
207    SkASSERT(env->IsInstanceOf(obj, gRect_class));
208
209    env->SetIntField(obj, gRect_leftFieldID, L);
210    env->SetIntField(obj, gRect_topFieldID, T);
211    env->SetIntField(obj, gRect_rightFieldID, R);
212    env->SetIntField(obj, gRect_bottomFieldID, B);
213}
214
215SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir)
216{
217    SkASSERT(env->IsInstanceOf(obj, gRect_class));
218
219    ir->set(env->GetIntField(obj, gRect_leftFieldID),
220            env->GetIntField(obj, gRect_topFieldID),
221            env->GetIntField(obj, gRect_rightFieldID),
222            env->GetIntField(obj, gRect_bottomFieldID));
223    return ir;
224}
225
226void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj)
227{
228    SkASSERT(env->IsInstanceOf(obj, gRect_class));
229
230    env->SetIntField(obj, gRect_leftFieldID, ir.fLeft);
231    env->SetIntField(obj, gRect_topFieldID, ir.fTop);
232    env->SetIntField(obj, gRect_rightFieldID, ir.fRight);
233    env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom);
234}
235
236SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r)
237{
238    SkASSERT(env->IsInstanceOf(obj, gRectF_class));
239
240    r->set(SkFloatToScalar(env->GetFloatField(obj, gRectF_leftFieldID)),
241           SkFloatToScalar(env->GetFloatField(obj, gRectF_topFieldID)),
242           SkFloatToScalar(env->GetFloatField(obj, gRectF_rightFieldID)),
243           SkFloatToScalar(env->GetFloatField(obj, gRectF_bottomFieldID)));
244    return r;
245}
246
247SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r)
248{
249    SkASSERT(env->IsInstanceOf(obj, gRect_class));
250
251    r->set(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)),
252           SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)),
253           SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)),
254           SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID)));
255    return r;
256}
257
258void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj)
259{
260    SkASSERT(env->IsInstanceOf(obj, gRectF_class));
261
262    env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft));
263    env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop));
264    env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight));
265    env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom));
266}
267
268SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point)
269{
270    SkASSERT(env->IsInstanceOf(obj, gPoint_class));
271
272    point->set(env->GetIntField(obj, gPoint_xFieldID),
273               env->GetIntField(obj, gPoint_yFieldID));
274    return point;
275}
276
277void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj)
278{
279    SkASSERT(env->IsInstanceOf(obj, gPoint_class));
280
281    env->SetIntField(obj, gPoint_xFieldID, ir.fX);
282    env->SetIntField(obj, gPoint_yFieldID, ir.fY);
283}
284
285SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point)
286{
287    SkASSERT(env->IsInstanceOf(obj, gPointF_class));
288
289    point->set(SkFloatToScalar(env->GetIntField(obj, gPointF_xFieldID)),
290               SkFloatToScalar(env->GetIntField(obj, gPointF_yFieldID)));
291    return point;
292}
293
294void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj)
295{
296    SkASSERT(env->IsInstanceOf(obj, gPointF_class));
297
298    env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX));
299    env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY));
300}
301
302SkBitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) {
303    SkASSERT(env);
304    SkASSERT(bitmap);
305    SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
306    SkBitmap* b = (SkBitmap*)env->GetIntField(bitmap, gBitmap_nativeInstanceID);
307    SkASSERT(b);
308    return b;
309}
310
311SkBitmap::Config GraphicsJNI::getNativeBitmapConfig(JNIEnv* env,
312                                                    jobject jconfig) {
313    SkASSERT(env);
314    if (NULL == jconfig) {
315        return SkBitmap::kNo_Config;
316    }
317    SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class));
318    int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
319    if (c < 0 || c >= SkBitmap::kConfigCount) {
320        c = SkBitmap::kNo_Config;
321    }
322    return static_cast<SkBitmap::Config>(c);
323}
324
325SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
326    SkASSERT(env);
327    SkASSERT(canvas);
328    SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
329    SkCanvas* c = (SkCanvas*)env->GetIntField(canvas, gCanvas_nativeInstanceID);
330    SkASSERT(c);
331    return c;
332}
333
334SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) {
335    SkASSERT(env);
336    SkASSERT(paint);
337    SkASSERT(env->IsInstanceOf(paint, gPaint_class));
338    SkPaint* p = (SkPaint*)env->GetIntField(paint, gPaint_nativeInstanceID);
339    SkASSERT(p);
340    return p;
341}
342
343SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture)
344{
345    SkASSERT(env);
346    SkASSERT(picture);
347    SkASSERT(env->IsInstanceOf(picture, gPicture_class));
348    SkPicture* p = (SkPicture*)env->GetIntField(picture, gPicture_nativeInstanceID);
349    SkASSERT(p);
350    return p;
351}
352
353SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
354{
355    SkASSERT(env);
356    SkASSERT(region);
357    SkASSERT(env->IsInstanceOf(region, gRegion_class));
358    SkRegion* r = (SkRegion*)env->GetIntField(region, gRegion_nativeInstanceID);
359    SkASSERT(r);
360    return r;
361}
362
363///////////////////////////////////////////////////////////////////////////////////////////
364
365jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
366                                  bool isMutable, jbyteArray ninepatch, int density)
367{
368    SkASSERT(bitmap);
369    SkASSERT(bitmap->pixelRef());
370
371    jobject obj = env->AllocObject(gBitmap_class);
372    if (obj) {
373        env->CallVoidMethod(obj, gBitmap_constructorMethodID,
374                            (jint)bitmap, buffer, isMutable, ninepatch, density);
375        if (hasException(env)) {
376            obj = NULL;
377        }
378    }
379    return obj;
380}
381
382jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
383                            jbyteArray ninepatch, int density)
384{
385    return createBitmap(env, bitmap, NULL, isMutable, ninepatch, density);
386}
387
388
389jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
390{
391    SkASSERT(bitmap != NULL);
392
393    jobject obj = env->AllocObject(gBitmapRegionDecoder_class);
394    if (hasException(env)) {
395        obj = NULL;
396        return obj;
397    }
398    if (obj) {
399        env->CallVoidMethod(obj, gBitmapRegionDecoder_constructorMethodID, (jint)bitmap);
400        if (hasException(env)) {
401            obj = NULL;
402        }
403    }
404    return obj;
405}
406
407jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
408{
409    SkASSERT(region != NULL);
410    jobject obj = env->AllocObject(gRegion_class);
411    if (obj) {
412        env->CallVoidMethod(obj, gRegion_constructorMethodID, (jint)region, 0);
413        if (hasException(env)) {
414            obj = NULL;
415        }
416    }
417    return obj;
418}
419
420static JNIEnv* vm2env(JavaVM* vm)
421{
422    JNIEnv* env = NULL;
423    if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || NULL == env)
424    {
425        SkDebugf("------- [%p] vm->GetEnv() failed\n", vm);
426        sk_throw();
427    }
428    return env;
429}
430
431#ifdef TRACK_LOCK_COUNT
432    static int gLockCount;
433#endif
434
435///////////////////////////////////////////////////////////////////////////////
436
437AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj,
438        SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) {
439    SkASSERT(storage);
440    SkASSERT(env);
441
442    if (env->GetJavaVM(&fVM) != JNI_OK) {
443        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
444        sk_throw();
445    }
446    fStorageObj = storageObj;
447    fHasGlobalRef = false;
448    fGlobalRefCnt = 0;
449
450    // If storageObj is NULL, the memory was NOT allocated on the Java heap
451    fOnJavaHeap = (storageObj != NULL);
452
453}
454
455AndroidPixelRef::~AndroidPixelRef() {
456    if (fOnJavaHeap) {
457        JNIEnv* env = vm2env(fVM);
458
459        if (fStorageObj && fHasGlobalRef) {
460            env->DeleteGlobalRef(fStorageObj);
461        }
462        fStorageObj = NULL;
463
464        // Set this to NULL to prevent the SkMallocPixelRef destructor
465        // from freeing the memory.
466        fStorage = NULL;
467    }
468}
469
470void AndroidPixelRef::setLocalJNIRef(jbyteArray arr) {
471    if (!fHasGlobalRef) {
472        fStorageObj = arr;
473    }
474}
475
476void AndroidPixelRef::globalRef() {
477    if (fOnJavaHeap && sk_atomic_inc(&fGlobalRefCnt) == 0) {
478        JNIEnv *env = vm2env(fVM);
479        if (fStorageObj == NULL) {
480            SkDebugf("Cannot create a global ref, fStorage obj is NULL");
481            sk_throw();
482        }
483        if (fHasGlobalRef) {
484            // This should never happen
485            SkDebugf("Already holding a global ref");
486            sk_throw();
487        }
488
489        fStorageObj = (jbyteArray) env->NewGlobalRef(fStorageObj);
490        // TODO: Check for failure here
491        fHasGlobalRef = true;
492    }
493    ref();
494}
495
496void AndroidPixelRef::globalUnref() {
497    if (fOnJavaHeap && sk_atomic_dec(&fGlobalRefCnt) == 1) {
498        JNIEnv *env = vm2env(fVM);
499        if (!fHasGlobalRef) {
500            SkDebugf("We don't have a global ref!");
501            sk_throw();
502        }
503        env->DeleteGlobalRef(fStorageObj);
504        fStorageObj = NULL;
505        fHasGlobalRef = false;
506    }
507    unref();
508}
509
510///////////////////////////////////////////////////////////////////////////////
511
512jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
513                                             SkColorTable* ctable) {
514    Sk64 size64 = bitmap->getSize64();
515    if (size64.isNeg() || !size64.is32()) {
516        doThrow(env, "java/lang/IllegalArgumentException",
517                     "bitmap size exceeds 32bits");
518        return NULL;
519    }
520
521    size_t size = size64.get32();
522    jbyteArray arrayObj = env->NewByteArray(size);
523    if (arrayObj) {
524        jbyte *addr = env->GetByteArrayElements(arrayObj, NULL);
525        env->ReleaseByteArrayElements(arrayObj, addr, 0);
526        if (addr) {
527            SkPixelRef* pr = new AndroidPixelRef(env, (void*) addr, size, arrayObj, ctable);
528            bitmap->setPixelRef(pr)->unref();
529            // since we're already allocated, we lockPixels right away
530            // HeapAllocator behaves this way too
531            bitmap->lockPixels();
532        }
533    }
534
535    return arrayObj;
536}
537
538bool GraphicsJNI::mallocPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) {
539    Sk64 size64 = bitmap->getSize64();
540    if (size64.isNeg() || !size64.is32()) {
541        doThrow(env, "java/lang/IllegalArgumentException",
542                     "bitmap size exceeds 32bits");
543        return false;
544    }
545
546    size_t size = size64.get32();
547
548    // call the version of malloc that returns null on failure
549    void* addr = sk_malloc_flags(size, 0);
550
551    if (NULL == addr) {
552        return false;
553    }
554
555    SkPixelRef* pr = new AndroidPixelRef(env, addr, size, NULL, ctable);
556    bitmap->setPixelRef(pr)->unref();
557    // since we're already allocated, we lockPixels right away
558    // HeapAllocator behaves this way too
559    bitmap->lockPixels();
560    return true;
561}
562
563///////////////////////////////////////////////////////////////////////////////
564
565JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool allocateInJavaHeap)
566    : fAllocateInJavaHeap(allocateInJavaHeap),
567      fStorageObj(NULL),
568      fAllocCount(0) {
569    if (env->GetJavaVM(&fVM) != JNI_OK) {
570        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
571        sk_throw();
572    }
573}
574
575bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
576    JNIEnv* env = vm2env(fVM);
577
578    // If allocating in the Java heap, only allow a single object to be
579    // allocated for the lifetime of this object.
580    if (fStorageObj != NULL) {
581        SkDebugf("WARNING: One-shot allocator has already allocated (alloc count = %d)\n", fAllocCount);
582//        sk_throw();
583    }
584
585    if (fAllocateInJavaHeap) {
586        fStorageObj = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
587        fAllocCount += 1;
588        return fStorageObj != NULL;
589    }
590    return GraphicsJNI::mallocPixelRef(env, bitmap, ctable);
591}
592
593////////////////////////////////////////////////////////////////////////////////
594
595JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
596    : fTotalSize(0) {
597    if (env->GetJavaVM(&fVM) != JNI_OK) {
598        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
599        sk_throw();
600    }
601}
602
603JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
604    JNIEnv* env = vm2env(fVM);
605    jlong jtotalSize = fTotalSize;
606    env->CallVoidMethod(gVMRuntime_singleton,
607            gVMRuntime_trackExternalFreeMethodID,
608            jtotalSize);
609}
610
611bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
612    jlong jsize = memorySize;  // the VM wants longs for the size
613    JNIEnv* env = vm2env(fVM);
614    bool r = env->CallBooleanMethod(gVMRuntime_singleton,
615            gVMRuntime_trackExternalAllocationMethodID,
616            jsize);
617    if (GraphicsJNI::hasException(env)) {
618        return false;
619    }
620    if (!r) {
621        LOGE("VM won't let us allocate %zd bytes\n", memorySize);
622        doThrowOOME(env, "bitmap size exceeds VM budget");
623        return false;
624    }
625    fTotalSize += memorySize;
626    return true;
627}
628
629////////////////////////////////////////////////////////////////////////////////
630
631JavaHeapBitmapRef::JavaHeapBitmapRef(JNIEnv* env, SkBitmap* nativeBitmap, jbyteArray buffer) {
632    fEnv = env;
633    fNativeBitmap = nativeBitmap;
634    fBuffer = buffer;
635
636    // If the buffer is NULL, the backing memory wasn't allocated on the Java heap
637    if (fBuffer) {
638        ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(fBuffer);
639    }
640}
641
642JavaHeapBitmapRef::~JavaHeapBitmapRef() {
643    if (fBuffer) {
644        ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(NULL);
645    }
646}
647
648////////////////////////////////////////////////////////////////////////////////
649
650static jclass make_globalref(JNIEnv* env, const char classname[])
651{
652    jclass c = env->FindClass(classname);
653    SkASSERT(c);
654    return (jclass)env->NewGlobalRef(c);
655}
656
657static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
658                                const char fieldname[], const char type[])
659{
660    jfieldID id = env->GetFieldID(clazz, fieldname, type);
661    SkASSERT(id);
662    return id;
663}
664
665int register_android_graphics_Graphics(JNIEnv* env)
666{
667    jmethodID m;
668    jclass c;
669
670    gRect_class = make_globalref(env, "android/graphics/Rect");
671    gRect_leftFieldID = getFieldIDCheck(env, gRect_class, "left", "I");
672    gRect_topFieldID = getFieldIDCheck(env, gRect_class, "top", "I");
673    gRect_rightFieldID = getFieldIDCheck(env, gRect_class, "right", "I");
674    gRect_bottomFieldID = getFieldIDCheck(env, gRect_class, "bottom", "I");
675
676    gRectF_class = make_globalref(env, "android/graphics/RectF");
677    gRectF_leftFieldID = getFieldIDCheck(env, gRectF_class, "left", "F");
678    gRectF_topFieldID = getFieldIDCheck(env, gRectF_class, "top", "F");
679    gRectF_rightFieldID = getFieldIDCheck(env, gRectF_class, "right", "F");
680    gRectF_bottomFieldID = getFieldIDCheck(env, gRectF_class, "bottom", "F");
681
682    gPoint_class = make_globalref(env, "android/graphics/Point");
683    gPoint_xFieldID = getFieldIDCheck(env, gPoint_class, "x", "I");
684    gPoint_yFieldID = getFieldIDCheck(env, gPoint_class, "y", "I");
685
686    gPointF_class = make_globalref(env, "android/graphics/PointF");
687    gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F");
688    gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F");
689
690    gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
691    gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
692    gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
693                                            "(I[BZ[BI)V");
694    gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
695    gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
696
697    gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
698    gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
699                                                     "nativeInt", "I");
700
701    gCanvas_class = make_globalref(env, "android/graphics/Canvas");
702    gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I");
703
704    gPaint_class = make_globalref(env, "android/graphics/Paint");
705    gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I");
706
707    gPicture_class = make_globalref(env, "android/graphics/Picture");
708    gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "I");
709
710    gRegion_class = make_globalref(env, "android/graphics/Region");
711    gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "I");
712    gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>",
713        "(II)V");
714
715    // Get the VMRuntime class.
716    c = env->FindClass("dalvik/system/VMRuntime");
717    SkASSERT(c);
718    // Look up VMRuntime.getRuntime().
719    m = env->GetStaticMethodID(c, "getRuntime", "()Ldalvik/system/VMRuntime;");
720    SkASSERT(m);
721    // Call VMRuntime.getRuntime() and hold onto its result.
722    gVMRuntime_singleton = env->CallStaticObjectMethod(c, m);
723    SkASSERT(gVMRuntime_singleton);
724    gVMRuntime_singleton = (jobject)env->NewGlobalRef(gVMRuntime_singleton);
725    // Look up the VMRuntime methods we'll be using.
726    gVMRuntime_trackExternalAllocationMethodID =
727                        env->GetMethodID(c, "trackExternalAllocation", "(J)Z");
728    gVMRuntime_trackExternalFreeMethodID =
729                            env->GetMethodID(c, "trackExternalFree", "(J)V");
730
731    return 0;
732}
733