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