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