blob: fc358a10a851fef8f141a7664941d9bbaf580963 [file] [log] [blame]
Ficus Kirkpatrick1a9c27c2010-03-05 17:05:08 -08001#define LOG_TAG "GraphicsJNI"
2
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003#include "jni.h"
4#include "GraphicsJNI.h"
Patrick Dubroye4ac2d62010-12-01 11:23:13 -08005
6#include "SkCanvas.h"
7#include "SkDevice.h"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008#include "SkPicture.h"
9#include "SkRegion.h"
10#include <android_runtime/AndroidRuntime.h>
11
Mike Reed1b22b972009-07-17 11:21:47 -040012//#define REPORT_SIZE_TO_JVM
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080013//#define TRACK_LOCK_COUNT
14
15void doThrow(JNIEnv* env, const char* exc, const char* msg) {
16 // don't throw a new exception if we already have one pending
17 if (env->ExceptionCheck() == JNI_FALSE) {
18 jclass npeClazz;
19
20 npeClazz = env->FindClass(exc);
21 LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
22
23 env->ThrowNew(npeClazz, msg);
24 }
25}
26
27void doThrowNPE(JNIEnv* env) {
28 doThrow(env, "java/lang/NullPointerException");
29}
30
31void doThrowAIOOBE(JNIEnv* env) {
32 doThrow(env, "java/lang/ArrayIndexOutOfBoundsException");
33}
34
35void doThrowRE(JNIEnv* env, const char* msg) {
36 doThrow(env, "java/lang/RuntimeException", msg);
37}
38
39void doThrowIAE(JNIEnv* env, const char* msg) {
40 doThrow(env, "java/lang/IllegalArgumentException", msg);
41}
42
43void doThrowISE(JNIEnv* env, const char* msg) {
44 doThrow(env, "java/lang/IllegalStateException", msg);
45}
46
47void doThrowOOME(JNIEnv* env, const char* msg) {
48 doThrow(env, "java/lang/OutOfMemoryError", msg);
49}
50
Joseph Wenf1f48bc2010-07-19 16:59:51 +080051void doThrowIOE(JNIEnv* env, const char* msg) {
Owen Line224fab2010-09-30 16:41:23 +080052 doThrow(env, "java/io/IOException", msg);
Joseph Wenf1f48bc2010-07-19 16:59:51 +080053}
54
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055bool GraphicsJNI::hasException(JNIEnv *env) {
56 if (env->ExceptionCheck() != 0) {
57 LOGE("*** Uncaught exception returned from Java call!\n");
58 env->ExceptionDescribe();
59 return true;
60 }
61 return false;
62}
63
64///////////////////////////////////////////////////////////////////////////////
65
66AutoJavaFloatArray::AutoJavaFloatArray(JNIEnv* env, jfloatArray array,
Mike Reedc04851f2009-10-28 15:09:45 -040067 int minLength, JNIAccess access)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
69 SkASSERT(env);
70 if (array) {
71 fLen = env->GetArrayLength(array);
72 if (fLen < minLength) {
73 sk_throw();
74 }
75 fPtr = env->GetFloatArrayElements(array, NULL);
76 }
Mike Reedc04851f2009-10-28 15:09:45 -040077 fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078}
79
80AutoJavaFloatArray::~AutoJavaFloatArray() {
81 if (fPtr) {
Mike Reedc04851f2009-10-28 15:09:45 -040082 fEnv->ReleaseFloatArrayElements(fArray, fPtr, fReleaseMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 }
84}
85
86AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array,
87 int minLength)
88: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
89 SkASSERT(env);
90 if (array) {
91 fLen = env->GetArrayLength(array);
92 if (fLen < minLength) {
93 sk_throw();
94 }
95 fPtr = env->GetIntArrayElements(array, NULL);
96 }
97}
98
99AutoJavaIntArray::~AutoJavaIntArray() {
100 if (fPtr) {
101 fEnv->ReleaseIntArrayElements(fArray, fPtr, 0);
102 }
103}
104
105AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array,
Mike Reedc04851f2009-10-28 15:09:45 -0400106 int minLength, JNIAccess access)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
108 SkASSERT(env);
109 if (array) {
110 fLen = env->GetArrayLength(array);
111 if (fLen < minLength) {
112 sk_throw();
113 }
114 fPtr = env->GetShortArrayElements(array, NULL);
115 }
Mike Reedc04851f2009-10-28 15:09:45 -0400116 fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117}
118
119AutoJavaShortArray::~AutoJavaShortArray() {
120 if (fPtr) {
Mike Reedc04851f2009-10-28 15:09:45 -0400121 fEnv->ReleaseShortArrayElements(fArray, fPtr, fReleaseMode);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 }
123}
124
125AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array,
126 int minLength)
127: fEnv(env), fArray(array), fPtr(NULL), fLen(0) {
128 SkASSERT(env);
129 if (array) {
130 fLen = env->GetArrayLength(array);
131 if (fLen < minLength) {
132 sk_throw();
133 }
134 fPtr = env->GetByteArrayElements(array, NULL);
135 }
136}
137
138AutoJavaByteArray::~AutoJavaByteArray() {
139 if (fPtr) {
140 fEnv->ReleaseByteArrayElements(fArray, fPtr, 0);
141 }
142}
143
144///////////////////////////////////////////////////////////////////////////////
145
146static jclass gRect_class;
147static jfieldID gRect_leftFieldID;
148static jfieldID gRect_topFieldID;
149static jfieldID gRect_rightFieldID;
150static jfieldID gRect_bottomFieldID;
151
152static jclass gRectF_class;
153static jfieldID gRectF_leftFieldID;
154static jfieldID gRectF_topFieldID;
155static jfieldID gRectF_rightFieldID;
156static jfieldID gRectF_bottomFieldID;
157
158static jclass gPoint_class;
159static jfieldID gPoint_xFieldID;
160static jfieldID gPoint_yFieldID;
161
162static jclass gPointF_class;
163static jfieldID gPointF_xFieldID;
164static jfieldID gPointF_yFieldID;
165
166static jclass gBitmap_class;
167static jfieldID gBitmap_nativeInstanceID;
168static jmethodID gBitmap_constructorMethodID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169
170static jclass gBitmapConfig_class;
171static jfieldID gBitmapConfig_nativeInstanceID;
172
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800173static jclass gBitmapRegionDecoder_class;
174static jmethodID gBitmapRegionDecoder_constructorMethodID;
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176static jclass gCanvas_class;
177static jfieldID gCanvas_nativeInstanceID;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800178
179static jclass gPaint_class;
180static jfieldID gPaint_nativeInstanceID;
181
182static jclass gPicture_class;
183static jfieldID gPicture_nativeInstanceID;
184
185static jclass gRegion_class;
186static jfieldID gRegion_nativeInstanceID;
187static jmethodID gRegion_constructorMethodID;
188
189static jobject gVMRuntime_singleton;
190static jmethodID gVMRuntime_trackExternalAllocationMethodID;
191static jmethodID gVMRuntime_trackExternalFreeMethodID;
192
193///////////////////////////////////////////////////////////////////////////////
194
195void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B)
196{
197 SkASSERT(env->IsInstanceOf(obj, gRect_class));
198
199 *L = env->GetIntField(obj, gRect_leftFieldID);
200 *T = env->GetIntField(obj, gRect_topFieldID);
201 *R = env->GetIntField(obj, gRect_rightFieldID);
202 *B = env->GetIntField(obj, gRect_bottomFieldID);
203}
204
205void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B)
206{
207 SkASSERT(env->IsInstanceOf(obj, gRect_class));
208
209 env->SetIntField(obj, gRect_leftFieldID, L);
210 env->SetIntField(obj, gRect_topFieldID, T);
211 env->SetIntField(obj, gRect_rightFieldID, R);
212 env->SetIntField(obj, gRect_bottomFieldID, B);
213}
214
215SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir)
216{
217 SkASSERT(env->IsInstanceOf(obj, gRect_class));
218
219 ir->set(env->GetIntField(obj, gRect_leftFieldID),
220 env->GetIntField(obj, gRect_topFieldID),
221 env->GetIntField(obj, gRect_rightFieldID),
222 env->GetIntField(obj, gRect_bottomFieldID));
223 return ir;
224}
225
226void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj)
227{
228 SkASSERT(env->IsInstanceOf(obj, gRect_class));
229
230 env->SetIntField(obj, gRect_leftFieldID, ir.fLeft);
231 env->SetIntField(obj, gRect_topFieldID, ir.fTop);
232 env->SetIntField(obj, gRect_rightFieldID, ir.fRight);
233 env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom);
234}
235
236SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r)
237{
238 SkASSERT(env->IsInstanceOf(obj, gRectF_class));
239
240 r->set(SkFloatToScalar(env->GetFloatField(obj, gRectF_leftFieldID)),
241 SkFloatToScalar(env->GetFloatField(obj, gRectF_topFieldID)),
242 SkFloatToScalar(env->GetFloatField(obj, gRectF_rightFieldID)),
243 SkFloatToScalar(env->GetFloatField(obj, gRectF_bottomFieldID)));
244 return r;
245}
246
247SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r)
248{
249 SkASSERT(env->IsInstanceOf(obj, gRect_class));
250
251 r->set(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)),
252 SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)),
253 SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)),
254 SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID)));
255 return r;
256}
257
258void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj)
259{
260 SkASSERT(env->IsInstanceOf(obj, gRectF_class));
261
262 env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft));
263 env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop));
264 env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight));
265 env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom));
266}
267
268SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point)
269{
270 SkASSERT(env->IsInstanceOf(obj, gPoint_class));
271
272 point->set(env->GetIntField(obj, gPoint_xFieldID),
273 env->GetIntField(obj, gPoint_yFieldID));
274 return point;
275}
276
277void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj)
278{
279 SkASSERT(env->IsInstanceOf(obj, gPoint_class));
280
281 env->SetIntField(obj, gPoint_xFieldID, ir.fX);
282 env->SetIntField(obj, gPoint_yFieldID, ir.fY);
283}
284
285SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point)
286{
287 SkASSERT(env->IsInstanceOf(obj, gPointF_class));
288
289 point->set(SkFloatToScalar(env->GetIntField(obj, gPointF_xFieldID)),
290 SkFloatToScalar(env->GetIntField(obj, gPointF_yFieldID)));
291 return point;
292}
293
294void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj)
295{
296 SkASSERT(env->IsInstanceOf(obj, gPointF_class));
297
298 env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX));
299 env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY));
300}
301
302SkBitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) {
303 SkASSERT(env);
304 SkASSERT(bitmap);
305 SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class));
306 SkBitmap* b = (SkBitmap*)env->GetIntField(bitmap, gBitmap_nativeInstanceID);
307 SkASSERT(b);
308 return b;
309}
310
311SkBitmap::Config GraphicsJNI::getNativeBitmapConfig(JNIEnv* env,
312 jobject jconfig) {
313 SkASSERT(env);
314 if (NULL == jconfig) {
315 return SkBitmap::kNo_Config;
316 }
317 SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class));
318 int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID);
319 if (c < 0 || c >= SkBitmap::kConfigCount) {
320 c = SkBitmap::kNo_Config;
321 }
322 return static_cast<SkBitmap::Config>(c);
323}
324
325SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) {
326 SkASSERT(env);
327 SkASSERT(canvas);
328 SkASSERT(env->IsInstanceOf(canvas, gCanvas_class));
329 SkCanvas* c = (SkCanvas*)env->GetIntField(canvas, gCanvas_nativeInstanceID);
330 SkASSERT(c);
331 return c;
332}
333
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) {
335 SkASSERT(env);
336 SkASSERT(paint);
337 SkASSERT(env->IsInstanceOf(paint, gPaint_class));
338 SkPaint* p = (SkPaint*)env->GetIntField(paint, gPaint_nativeInstanceID);
339 SkASSERT(p);
340 return p;
341}
342
343SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture)
344{
345 SkASSERT(env);
346 SkASSERT(picture);
347 SkASSERT(env->IsInstanceOf(picture, gPicture_class));
348 SkPicture* p = (SkPicture*)env->GetIntField(picture, gPicture_nativeInstanceID);
349 SkASSERT(p);
350 return p;
351}
352
353SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
354{
355 SkASSERT(env);
356 SkASSERT(region);
357 SkASSERT(env->IsInstanceOf(region, gRegion_class));
358 SkRegion* r = (SkRegion*)env->GetIntField(region, gRegion_nativeInstanceID);
359 SkASSERT(r);
360 return r;
361}
362
363///////////////////////////////////////////////////////////////////////////////////////////
364
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800365jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
366 bool isMutable, jbyteArray ninepatch, int density)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367{
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800368 SkASSERT(bitmap);
369 SkASSERT(bitmap->pixelRef());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370
371 jobject obj = env->AllocObject(gBitmap_class);
372 if (obj) {
373 env->CallVoidMethod(obj, gBitmap_constructorMethodID,
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800374 (jint)bitmap, buffer, isMutable, ninepatch, density);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 if (hasException(env)) {
376 obj = NULL;
377 }
378 }
379 return obj;
380}
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800381
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800382jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
383 jbyteArray ninepatch, int density)
384{
385 return createBitmap(env, bitmap, NULL, isMutable, ninepatch, density);
386}
387
388
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800389jobject GraphicsJNI::createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap)
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800390{
391 SkASSERT(bitmap != NULL);
392
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800393 jobject obj = env->AllocObject(gBitmapRegionDecoder_class);
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800394 if (hasException(env)) {
395 obj = NULL;
396 return obj;
397 }
398 if (obj) {
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800399 env->CallVoidMethod(obj, gBitmapRegionDecoder_constructorMethodID, (jint)bitmap);
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800400 if (hasException(env)) {
401 obj = NULL;
402 }
403 }
404 return obj;
405}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406
407jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region)
408{
409 SkASSERT(region != NULL);
410 jobject obj = env->AllocObject(gRegion_class);
411 if (obj) {
412 env->CallVoidMethod(obj, gRegion_constructorMethodID, (jint)region, 0);
413 if (hasException(env)) {
414 obj = NULL;
415 }
416 }
417 return obj;
418}
419
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420static JNIEnv* vm2env(JavaVM* vm)
421{
422 JNIEnv* env = NULL;
423 if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || NULL == env)
424 {
425 SkDebugf("------- [%p] vm->GetEnv() failed\n", vm);
426 sk_throw();
427 }
428 return env;
429}
430
431#ifdef TRACK_LOCK_COUNT
432 static int gLockCount;
433#endif
434
435///////////////////////////////////////////////////////////////////////////////
436
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800437AndroidPixelRef::AndroidPixelRef(JNIEnv* env, void* storage, size_t size, jbyteArray storageObj,
438 SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) {
439 SkASSERT(storage);
440 SkASSERT(env);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800442 if (env->GetJavaVM(&fVM) != JNI_OK) {
443 SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
444 sk_throw();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 }
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800446 fStorageObj = storageObj;
447 fHasGlobalRef = false;
448 fGlobalRefCnt = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800450 // If storageObj is NULL, the memory was NOT allocated on the Java heap
451 fOnJavaHeap = (storageObj != NULL);
452
453}
454
455AndroidPixelRef::~AndroidPixelRef() {
456 if (fOnJavaHeap) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 JNIEnv* env = vm2env(fVM);
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800458
459 if (fStorageObj && fHasGlobalRef) {
460 env->DeleteGlobalRef(fStorageObj);
461 }
462 fStorageObj = NULL;
463
464 // Set this to NULL to prevent the SkMallocPixelRef destructor
465 // from freeing the memory.
466 fStorage = NULL;
467 }
468}
469
470void AndroidPixelRef::setLocalJNIRef(jbyteArray arr) {
471 if (!fHasGlobalRef) {
472 fStorageObj = arr;
473 }
474}
475
476void AndroidPixelRef::globalRef() {
477 if (fOnJavaHeap && sk_atomic_inc(&fGlobalRefCnt) == 0) {
478 JNIEnv *env = vm2env(fVM);
479 if (fStorageObj == NULL) {
480 SkDebugf("Cannot create a global ref, fStorage obj is NULL");
481 sk_throw();
482 }
483 if (fHasGlobalRef) {
484 // This should never happen
485 SkDebugf("Already holding a global ref");
486 sk_throw();
487 }
488
489 fStorageObj = (jbyteArray) env->NewGlobalRef(fStorageObj);
490 // TODO: Check for failure here
491 fHasGlobalRef = true;
492 }
493 ref();
494}
495
496void AndroidPixelRef::globalUnref() {
497 if (fOnJavaHeap && sk_atomic_dec(&fGlobalRefCnt) == 1) {
498 JNIEnv *env = vm2env(fVM);
499 if (!fHasGlobalRef) {
500 SkDebugf("We don't have a global ref!");
501 sk_throw();
502 }
503 env->DeleteGlobalRef(fStorageObj);
504 fStorageObj = NULL;
505 fHasGlobalRef = false;
506 }
507 unref();
508}
509
510///////////////////////////////////////////////////////////////////////////////
511
512jbyteArray GraphicsJNI::allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
513 SkColorTable* ctable) {
514 Sk64 size64 = bitmap->getSize64();
515 if (size64.isNeg() || !size64.is32()) {
516 doThrow(env, "java/lang/IllegalArgumentException",
517 "bitmap size exceeds 32bits");
518 return NULL;
519 }
520
521 size_t size = size64.get32();
522 jbyteArray arrayObj = env->NewByteArray(size);
523 if (arrayObj) {
524 jbyte *addr = env->GetByteArrayElements(arrayObj, NULL);
525 env->ReleaseByteArrayElements(arrayObj, addr, 0);
526 if (addr) {
527 SkPixelRef* pr = new AndroidPixelRef(env, (void*) addr, size, arrayObj, ctable);
528 bitmap->setPixelRef(pr)->unref();
529 // since we're already allocated, we lockPixels right away
530 // HeapAllocator behaves this way too
531 bitmap->lockPixels();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 }
533 }
534
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800535 return arrayObj;
536}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800538bool GraphicsJNI::mallocPixelRef(JNIEnv* env, SkBitmap* bitmap, SkColorTable* ctable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 Sk64 size64 = bitmap->getSize64();
540 if (size64.isNeg() || !size64.is32()) {
541 doThrow(env, "java/lang/IllegalArgumentException",
542 "bitmap size exceeds 32bits");
543 return false;
544 }
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800545
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 size_t size = size64.get32();
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800547
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800548 // call the version of malloc that returns null on failure
549 void* addr = sk_malloc_flags(size, 0);
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800550
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 if (NULL == addr) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 return false;
553 }
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800554
555 SkPixelRef* pr = new AndroidPixelRef(env, addr, size, NULL, ctable);
Mike Reed1b22b972009-07-17 11:21:47 -0400556 bitmap->setPixelRef(pr)->unref();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 // since we're already allocated, we lockPixels right away
558 // HeapAllocator behaves this way too
559 bitmap->lockPixels();
560 return true;
561}
562
563///////////////////////////////////////////////////////////////////////////////
564
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800565JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool allocateInJavaHeap)
566 : fAllocateInJavaHeap(allocateInJavaHeap),
567 fStorageObj(NULL) {
Wei-Ta Chen291303b2010-08-18 15:40:29 +0800568 if (env->GetJavaVM(&fVM) != JNI_OK) {
569 SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
570 sk_throw();
571 }
572}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573
574bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) {
Wei-Ta Chen291303b2010-08-18 15:40:29 +0800575 JNIEnv* env = vm2env(fVM);
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800576
577 // If allocating in the Java heap, only allow a single object to be
578 // allocated for the lifetime of this object.
579 if (fStorageObj != NULL) {
580 SkDebugf("ERROR: One-shot allocator has already allocated\n");
581 sk_throw();
582 }
583
584 if (fAllocateInJavaHeap) {
585 fStorageObj = GraphicsJNI::allocateJavaPixelRef(env, bitmap, ctable);
586 return fStorageObj != NULL;
587 }
588 return GraphicsJNI::mallocPixelRef(env, bitmap, ctable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589}
590
591////////////////////////////////////////////////////////////////////////////////
592
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800593JavaMemoryUsageReporter::JavaMemoryUsageReporter(JNIEnv* env)
Wei-Ta Chen291303b2010-08-18 15:40:29 +0800594 : fTotalSize(0) {
595 if (env->GetJavaVM(&fVM) != JNI_OK) {
596 SkDebugf("------ [%p] env->GetJavaVM failed\n", env);
597 sk_throw();
598 }
599}
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800600
601JavaMemoryUsageReporter::~JavaMemoryUsageReporter() {
Wei-Ta Chen291303b2010-08-18 15:40:29 +0800602 JNIEnv* env = vm2env(fVM);
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800603 jlong jtotalSize = fTotalSize;
Wei-Ta Chen291303b2010-08-18 15:40:29 +0800604 env->CallVoidMethod(gVMRuntime_singleton,
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800605 gVMRuntime_trackExternalFreeMethodID,
606 jtotalSize);
607}
608
609bool JavaMemoryUsageReporter::reportMemory(size_t memorySize) {
610 jlong jsize = memorySize; // the VM wants longs for the size
Wei-Ta Chen291303b2010-08-18 15:40:29 +0800611 JNIEnv* env = vm2env(fVM);
612 bool r = env->CallBooleanMethod(gVMRuntime_singleton,
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800613 gVMRuntime_trackExternalAllocationMethodID,
614 jsize);
Wei-Ta Chen291303b2010-08-18 15:40:29 +0800615 if (GraphicsJNI::hasException(env)) {
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800616 return false;
617 }
618 if (!r) {
619 LOGE("VM won't let us allocate %zd bytes\n", memorySize);
Wei-Ta Chen291303b2010-08-18 15:40:29 +0800620 doThrowOOME(env, "bitmap size exceeds VM budget");
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800621 return false;
622 }
623 fTotalSize += memorySize;
624 return true;
625}
626
627////////////////////////////////////////////////////////////////////////////////
628
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800629JavaHeapBitmapRef::JavaHeapBitmapRef(JNIEnv* env, SkBitmap* nativeBitmap, jbyteArray buffer) {
630 fEnv = env;
631 fNativeBitmap = nativeBitmap;
632 fBuffer = buffer;
633
634 // If the buffer is NULL, the backing memory wasn't allocated on the Java heap
635 if (fBuffer) {
636 ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(fBuffer);
637 }
638}
639
640JavaHeapBitmapRef::~JavaHeapBitmapRef() {
641 if (fBuffer) {
642 ((AndroidPixelRef*) fNativeBitmap->pixelRef())->setLocalJNIRef(NULL);
643 }
644}
645
646////////////////////////////////////////////////////////////////////////////////
647
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648static jclass make_globalref(JNIEnv* env, const char classname[])
649{
650 jclass c = env->FindClass(classname);
651 SkASSERT(c);
652 return (jclass)env->NewGlobalRef(c);
653}
654
655static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
656 const char fieldname[], const char type[])
657{
658 jfieldID id = env->GetFieldID(clazz, fieldname, type);
659 SkASSERT(id);
660 return id;
661}
662
663int register_android_graphics_Graphics(JNIEnv* env)
664{
665 jmethodID m;
666 jclass c;
667
668 gRect_class = make_globalref(env, "android/graphics/Rect");
669 gRect_leftFieldID = getFieldIDCheck(env, gRect_class, "left", "I");
670 gRect_topFieldID = getFieldIDCheck(env, gRect_class, "top", "I");
671 gRect_rightFieldID = getFieldIDCheck(env, gRect_class, "right", "I");
672 gRect_bottomFieldID = getFieldIDCheck(env, gRect_class, "bottom", "I");
673
674 gRectF_class = make_globalref(env, "android/graphics/RectF");
675 gRectF_leftFieldID = getFieldIDCheck(env, gRectF_class, "left", "F");
676 gRectF_topFieldID = getFieldIDCheck(env, gRectF_class, "top", "F");
677 gRectF_rightFieldID = getFieldIDCheck(env, gRectF_class, "right", "F");
678 gRectF_bottomFieldID = getFieldIDCheck(env, gRectF_class, "bottom", "F");
679
680 gPoint_class = make_globalref(env, "android/graphics/Point");
681 gPoint_xFieldID = getFieldIDCheck(env, gPoint_class, "x", "I");
682 gPoint_yFieldID = getFieldIDCheck(env, gPoint_class, "y", "I");
683
684 gPointF_class = make_globalref(env, "android/graphics/PointF");
685 gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F");
686 gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F");
687
688 gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800689 gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
Patrick Dubroye4ac2d62010-12-01 11:23:13 -0800691 "(I[BZ[BI)V");
Wei-Ta Chen6b849e22010-09-07 17:32:18 +0800692 gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
693 gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
Joseph Wenf1f48bc2010-07-19 16:59:51 +0800694
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config");
696 gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class,
697 "nativeInt", "I");
698
699 gCanvas_class = make_globalref(env, "android/graphics/Canvas");
700 gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701
702 gPaint_class = make_globalref(env, "android/graphics/Paint");
703 gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I");
704
705 gPicture_class = make_globalref(env, "android/graphics/Picture");
706 gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "I");
707
708 gRegion_class = make_globalref(env, "android/graphics/Region");
709 gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "I");
710 gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>",
711 "(II)V");
712
713 // Get the VMRuntime class.
714 c = env->FindClass("dalvik/system/VMRuntime");
715 SkASSERT(c);
716 // Look up VMRuntime.getRuntime().
717 m = env->GetStaticMethodID(c, "getRuntime", "()Ldalvik/system/VMRuntime;");
718 SkASSERT(m);
719 // Call VMRuntime.getRuntime() and hold onto its result.
720 gVMRuntime_singleton = env->CallStaticObjectMethod(c, m);
721 SkASSERT(gVMRuntime_singleton);
722 gVMRuntime_singleton = (jobject)env->NewGlobalRef(gVMRuntime_singleton);
723 // Look up the VMRuntime methods we'll be using.
724 gVMRuntime_trackExternalAllocationMethodID =
725 env->GetMethodID(c, "trackExternalAllocation", "(J)Z");
726 gVMRuntime_trackExternalFreeMethodID =
727 env->GetMethodID(c, "trackExternalFree", "(J)V");
728
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 return 0;
730}