Graphics.cpp revision d24b8183b93e781080b2c16c487e60d51c12da31
1#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
588