• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
doThrow(JNIEnv * env,const char * exc,const char * msg)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 
doThrowNPE(JNIEnv * env)24 void doThrowNPE(JNIEnv* env) {
25     doThrow(env, "java/lang/NullPointerException");
26 }
27 
doThrowAIOOBE(JNIEnv * env)28 void doThrowAIOOBE(JNIEnv* env) {
29     doThrow(env, "java/lang/ArrayIndexOutOfBoundsException");
30 }
31 
doThrowRE(JNIEnv * env,const char * msg)32 void doThrowRE(JNIEnv* env, const char* msg) {
33     doThrow(env, "java/lang/RuntimeException", msg);
34 }
35 
doThrowIAE(JNIEnv * env,const char * msg)36 void doThrowIAE(JNIEnv* env, const char* msg) {
37     doThrow(env, "java/lang/IllegalArgumentException", msg);
38 }
39 
doThrowISE(JNIEnv * env,const char * msg)40 void doThrowISE(JNIEnv* env, const char* msg) {
41     doThrow(env, "java/lang/IllegalStateException", msg);
42 }
43 
doThrowOOME(JNIEnv * env,const char * msg)44 void doThrowOOME(JNIEnv* env, const char* msg) {
45     doThrow(env, "java/lang/OutOfMemoryError", msg);
46 }
47 
doThrowIOE(JNIEnv * env,const char * msg)48 void doThrowIOE(JNIEnv* env, const char* msg) {
49     doThrow(env, "java/io/IOException", msg);
50 }
51 
hasException(JNIEnv * env)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 
AutoJavaFloatArray(JNIEnv * env,jfloatArray array,int minLength,JNIAccess access)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 
~AutoJavaFloatArray()77 AutoJavaFloatArray::~AutoJavaFloatArray() {
78     if (fPtr) {
79         fEnv->ReleaseFloatArrayElements(fArray, fPtr, fReleaseMode);
80     }
81 }
82 
AutoJavaIntArray(JNIEnv * env,jintArray array,int minLength)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 
~AutoJavaIntArray()96 AutoJavaIntArray::~AutoJavaIntArray() {
97     if (fPtr) {
98         fEnv->ReleaseIntArrayElements(fArray, fPtr, 0);
99     }
100 }
101 
AutoJavaShortArray(JNIEnv * env,jshortArray array,int minLength,JNIAccess access)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 
~AutoJavaShortArray()116 AutoJavaShortArray::~AutoJavaShortArray() {
117     if (fPtr) {
118         fEnv->ReleaseShortArrayElements(fArray, fPtr, fReleaseMode);
119     }
120 }
121 
AutoJavaByteArray(JNIEnv * env,jbyteArray array,int minLength)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 
~AutoJavaByteArray()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 
get_jrect(JNIEnv * env,jobject obj,int * L,int * T,int * R,int * B)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 
set_jrect(JNIEnv * env,jobject obj,int L,int T,int R,int B)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 
jrect_to_irect(JNIEnv * env,jobject obj,SkIRect * ir)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 
irect_to_jrect(const SkIRect & ir,JNIEnv * env,jobject obj)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 
jrectf_to_rect(JNIEnv * env,jobject obj,SkRect * r)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 
jrect_to_rect(JNIEnv * env,jobject obj,SkRect * r)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 
rect_to_jrectf(const SkRect & r,JNIEnv * env,jobject obj)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 
jpoint_to_ipoint(JNIEnv * env,jobject obj,SkIPoint * point)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 
ipoint_to_jpoint(const SkIPoint & ir,JNIEnv * env,jobject obj)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 
jpointf_to_point(JNIEnv * env,jobject obj,SkPoint * point)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 
point_to_jpointf(const SkPoint & r,JNIEnv * env,jobject obj)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 
getNativeBitmap(JNIEnv * env,jobject bitmap)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 
getNativeBitmapConfig(JNIEnv * env,jobject jconfig)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 
getNativeCanvas(JNIEnv * env,jobject canvas)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 
getNativePaint(JNIEnv * env,jobject paint)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 
getNativePicture(JNIEnv * env,jobject picture)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 
getNativeRegion(JNIEnv * env,jobject region)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 
createBitmap(JNIEnv * env,SkBitmap * bitmap,bool isMutable,jbyteArray ninepatch,int density)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 
createBitmapRegionDecoder(JNIEnv * env,SkBitmapRegionDecoder * bitmap)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 
createRegion(JNIEnv * env,SkRegion * region)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 
vm2env(JavaVM * vm)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     */
AndroidPixelRef(JNIEnv * env,void * storage,size_t size,SkColorTable * ctable)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 
~AndroidPixelRef()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 
setJavaPixelRef(JNIEnv * env,SkBitmap * bitmap,SkColorTable * ctable,bool reportSizeToVM)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 
JavaPixelAllocator(JNIEnv * env,bool reportSizeToVM)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 
allocPixelRef(SkBitmap * bitmap,SkColorTable * ctable)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 
JavaMemoryUsageReporter(JNIEnv * env)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 
~JavaMemoryUsageReporter()543 JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
544     JNIEnv* env = vm2env(fVM);
545     jlong jtotalSize = fTotalSize;
546     env->CallVoidMethod(gVMRuntime_singleton,
547             gVMRuntime_trackExternalFreeMethodID,
548             jtotalSize);
549 }
550 
reportMemory(size_t memorySize)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 
make_globalref(JNIEnv * env,const char classname[])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 
getFieldIDCheck(JNIEnv * env,jclass clazz,const char fieldname[],const char type[])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 
register_android_graphics_Graphics(JNIEnv * env)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