Graphics.cpp revision a2f0e2d6b7f9dff3a52dd78d6db307070a71e9b2
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(void* localref) {
477    if (fOnJavaHeap && sk_atomic_inc(&fGlobalRefCnt) == 0) {
478        JNIEnv *env = vm2env(fVM);
479
480        // If JNI ref was passed, it is always used
481        if (localref) fStorageObj = (jbyteArray) localref;
482
483        if (fStorageObj == NULL) {
484            SkDebugf("No valid local ref to create a JNI global ref\n");
485            sk_throw();
486        }
487        if (fHasGlobalRef) {
488            // This should never happen
489            SkDebugf("Already holding a JNI global ref");
490            sk_throw();
491        }
492
493        fStorageObj = (jbyteArray) env->NewGlobalRef(fStorageObj);
494        // TODO: Check for failure here
495        fHasGlobalRef = true;
496    }
497    ref();
498}
499
500void AndroidPixelRef::globalUnref() {
501    if (fOnJavaHeap && sk_atomic_dec(&fGlobalRefCnt) == 1) {
502        JNIEnv *env = vm2env(fVM);
503        if (!fHasGlobalRef) {
504            SkDebugf("We don't have a global ref!");
505            sk_throw();
506        }
507        env->DeleteGlobalRef(fStorageObj);
508        fStorageObj = NULL;
509        fHasGlobalRef = false;
510    }
511    unref();
512}
513
514///////////////////////////////////////////////////////////////////////////////
515
516jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
517                                             SkColorTable* ctable) {
518    Sk64 size64 = bitmap->getSize64();
519    if (size64.isNeg() || !size64.is32()) {
520        doThrow(env, "java/lang/IllegalArgumentException",
521                     "bitmap size exceeds 32bits");
522        return NULL;
523    }
524
525    size_t size = size64.get32();
526    jbyteArray arrayObj = env->NewByteArray(size);
527    if (arrayObj) {
528        jbyte *addr = env->GetByteArrayElements(arrayObj, NULL);
529        env->ReleaseByteArrayElements(arrayObj, addr, 0);
530        if (addr) {
531            SkPixelRef* pr = new AndroidPixelRef(env, (void*) addr, size, arrayObj, ctable);
532            bitmap->setPixelRef(pr)->unref();
533            // since we're already allocated, we lockPixels right away
534            // HeapAllocator behaves this way too
535            bitmap->lockPixels();
536        }
537    }
538
539    return arrayObj;
540}
541
542bool GraphicsJNI::mallocPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) {
543    Sk64 size64 = bitmap->getSize64();
544    if (size64.isNeg() || !size64.is32()) {
545        doThrow(env, "java/lang/IllegalArgumentException",
546                     "bitmap size exceeds 32bits");
547        return false;
548    }
549
550    size_t size = size64.get32();
551
552    // call the version of malloc that returns null on failure
553    void* addr = sk_malloc_flags(size, 0);
554
555    if (NULL == addr) {
556        return false;
557    }
558
559    SkPixelRef* pr = new AndroidPixelRef(env, addr, size, NULL, ctable);
560    bitmap->setPixelRef(pr)->unref();
561    // since we're already allocated, we lockPixels right away
562    // HeapAllocator behaves this way too
563    bitmap->lockPixels();
564    return true;
565}
566
567///////////////////////////////////////////////////////////////////////////////
568
569JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool allocateInJavaHeap)
570    : fAllocateInJavaHeap(allocateInJavaHeap),
571      fStorageObj(NULL),
572      fAllocCount(0) {
573    if (env->GetJavaVM(&fVM) != JNI_OK) {
574        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
575        sk_throw();
576    }
577}
578
579bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
580    JNIEnv* env = vm2env(fVM);
581
582    // If allocating in the Java heap, only allow a single object to be
583    // allocated for the lifetime of this object.
584    if (fStorageObj != NULL) {
585        SkDebugf("WARNING: One-shot allocator has already allocated (alloc count = %d)\n", fAllocCount);
586//        sk_throw();
587    }
588
589    if (fAllocateInJavaHeap) {
590        fStorageObj = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
591        fAllocCount += 1;
592        return fStorageObj != NULL;
593    }
594    return GraphicsJNI::mallocPixelRef(env, bitmap, ctable);
595}
596
597////////////////////////////////////////////////////////////////////////////////
598
599JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
600    : fTotalSize(0) {
601    if (env->GetJavaVM(&fVM) != JNI_OK) {
602        SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
603        sk_throw();
604    }
605}
606
607JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
608    JNIEnv* env = vm2env(fVM);
609    jlong jtotalSize = fTotalSize;
610    env->CallVoidMethod(gVMRuntime_singleton,
611            gVMRuntime_trackExternalFreeMethodID,
612            jtotalSize);
613}
614
615bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
616    jlong jsize = memorySize;  // the VM wants longs for the size
617    JNIEnv* env = vm2env(fVM);
618    bool r = env->CallBooleanMethod(gVMRuntime_singleton,
619            gVMRuntime_trackExternalAllocationMethodID,
620            jsize);
621    if (GraphicsJNI::hasException(env)) {
622        return false;
623    }
624    if (!r) {
625        LOGE("VM won't let us allocate %zd bytes\n", memorySize);
626        doThrowOOME(env, "bitmap size exceeds VM budget");
627        return false;
628    }
629    fTotalSize += memorySize;
630    return true;
631}
632
633////////////////////////////////////////////////////////////////////////////////
634
635JavaHeapBitmapRef::JavaHeapBitmapRef(JNIEnv* env, SkBitmap* nativeBitmap, jbyteArray buffer) {
636    fEnv = env;
637    fNativeBitmap = nativeBitmap;
638    fBuffer = buffer;
639
640    // If the buffer is NULL, the backing memory wasn't allocated on the Java heap
641    if (fBuffer) {
642        ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(fBuffer);
643    }
644}
645
646JavaHeapBitmapRef::~JavaHeapBitmapRef() {
647    if (fBuffer) {
648        ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(NULL);
649    }
650}
651
652////////////////////////////////////////////////////////////////////////////////
653
654static jclass make_globalref(JNIEnv* env, const char classname[])
655{
656    jclass c = env->FindClass(classname);
657    SkASSERT(c);
658    return (jclass)env->NewGlobalRef(c);
659}
660
661static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
662                                const char fieldname[], const char type[])
663{
664    jfieldID id = env->GetFieldID(clazz, fieldname, type);
665    SkASSERT(id);
666    return id;
667}
668
669int register_android_graphics_Graphics(JNIEnv* env)
670{
671    jmethodID m;
672    jclass c;
673
674    gRect_class = make_globalref(env, "android/graphics/Rect");
675    gRect_leftFieldID = getFieldIDCheck(env, gRect_class, "left", "I");
676    gRect_topFieldID = getFieldIDCheck(env, gRect_class, "top", "I");
677    gRect_rightFieldID = getFieldIDCheck(env, gRect_class, "right", "I");
678    gRect_bottomFieldID = getFieldIDCheck(env, gRect_class, "bottom", "I");
679
680    gRectF_class = make_globalref(env, "android/graphics/RectF");
681    gRectF_leftFieldID = getFieldIDCheck(env, gRectF_class, "left", "F");
682    gRectF_topFieldID = getFieldIDCheck(env, gRectF_class, "top", "F");
683    gRectF_rightFieldID = getFieldIDCheck(env, gRectF_class, "right", "F");
684    gRectF_bottomFieldID = getFieldIDCheck(env, gRectF_class, "bottom", "F");
685
686    gPoint_class = make_globalref(env, "android/graphics/Point");
687    gPoint_xFieldID = getFieldIDCheck(env, gPoint_class, "x", "I");
688    gPoint_yFieldID = getFieldIDCheck(env, gPoint_class, "y", "I");
689
690    gPointF_class = make_globalref(env, "android/graphics/PointF");
691    gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F");
692    gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F");
693
694    gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
695    gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
696    gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
697                                            "(I[BZ[BI)V");
698    gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
699    gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
700
701    gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
702    gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
703                                                     "nativeInt", "I");
704
705    gCanvas_class = make_globalref(env, "android/graphics/Canvas");
706    gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I");
707
708    gPaint_class = make_globalref(env, "android/graphics/Paint");
709    gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I");
710
711    gPicture_class = make_globalref(env, "android/graphics/Picture");
712    gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "I");
713
714    gRegion_class = make_globalref(env, "android/graphics/Region");
715    gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "I");
716    gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>",
717        "(II)V");
718
719    // Get the VMRuntime class.
720    c = env->FindClass("dalvik/system/VMRuntime");
721    SkASSERT(c);
722    // Look up VMRuntime.getRuntime().
723    m = env->GetStaticMethodID(c, "getRuntime", "()Ldalvik/system/VMRuntime;");
724    SkASSERT(m);
725    // Call VMRuntime.getRuntime() and hold onto its result.
726    gVMRuntime_singleton = env->CallStaticObjectMethod(c, m);
727    SkASSERT(gVMRuntime_singleton);
728    gVMRuntime_singleton = (jobject)env->NewGlobalRef(gVMRuntime_singleton);
729    // Look up the VMRuntime methods we'll be using.
730    gVMRuntime_trackExternalAllocationMethodID =
731                        env->GetMethodID(c, "trackExternalAllocation", "(J)Z");
732    gVMRuntime_trackExternalFreeMethodID =
733                            env->GetMethodID(c, "trackExternalFree", "(J)V");
734
735    return 0;
736}
737