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