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