Graphics.cpp revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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; 165 166static jclass gPaint_class; 167static jfieldID gPaint_nativeInstanceID; 168 169static jclass gPicture_class; 170static jfieldID gPicture_nativeInstanceID; 171 172static jclass gRegion_class; 173static jfieldID gRegion_nativeInstanceID; 174static jmethodID gRegion_constructorMethodID; 175 176static jobject gVMRuntime_singleton; 177static jmethodID gVMRuntime_trackExternalAllocationMethodID; 178static jmethodID gVMRuntime_trackExternalFreeMethodID; 179 180/////////////////////////////////////////////////////////////////////////////// 181 182void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B) 183{ 184 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 185 186 *L = env->GetIntField(obj, gRect_leftFieldID); 187 *T = env->GetIntField(obj, gRect_topFieldID); 188 *R = env->GetIntField(obj, gRect_rightFieldID); 189 *B = env->GetIntField(obj, gRect_bottomFieldID); 190} 191 192void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B) 193{ 194 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 195 196 env->SetIntField(obj, gRect_leftFieldID, L); 197 env->SetIntField(obj, gRect_topFieldID, T); 198 env->SetIntField(obj, gRect_rightFieldID, R); 199 env->SetIntField(obj, gRect_bottomFieldID, B); 200} 201 202SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir) 203{ 204 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 205 206 ir->set(env->GetIntField(obj, gRect_leftFieldID), 207 env->GetIntField(obj, gRect_topFieldID), 208 env->GetIntField(obj, gRect_rightFieldID), 209 env->GetIntField(obj, gRect_bottomFieldID)); 210 return ir; 211} 212 213void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj) 214{ 215 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 216 217 env->SetIntField(obj, gRect_leftFieldID, ir.fLeft); 218 env->SetIntField(obj, gRect_topFieldID, ir.fTop); 219 env->SetIntField(obj, gRect_rightFieldID, ir.fRight); 220 env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom); 221} 222 223SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r) 224{ 225 SkASSERT(env->IsInstanceOf(obj, gRectF_class)); 226 227 r->set(SkFloatToScalar(env->GetFloatField(obj, gRectF_leftFieldID)), 228 SkFloatToScalar(env->GetFloatField(obj, gRectF_topFieldID)), 229 SkFloatToScalar(env->GetFloatField(obj, gRectF_rightFieldID)), 230 SkFloatToScalar(env->GetFloatField(obj, gRectF_bottomFieldID))); 231 return r; 232} 233 234SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r) 235{ 236 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 237 238 r->set(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)), 239 SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)), 240 SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)), 241 SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID))); 242 return r; 243} 244 245void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj) 246{ 247 SkASSERT(env->IsInstanceOf(obj, gRectF_class)); 248 249 env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft)); 250 env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop)); 251 env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight)); 252 env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom)); 253} 254 255SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point) 256{ 257 SkASSERT(env->IsInstanceOf(obj, gPoint_class)); 258 259 point->set(env->GetIntField(obj, gPoint_xFieldID), 260 env->GetIntField(obj, gPoint_yFieldID)); 261 return point; 262} 263 264void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj) 265{ 266 SkASSERT(env->IsInstanceOf(obj, gPoint_class)); 267 268 env->SetIntField(obj, gPointF_xFieldID, ir.fX); 269 env->SetIntField(obj, gPointF_yFieldID, ir.fY); 270} 271 272SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point) 273{ 274 SkASSERT(env->IsInstanceOf(obj, gPointF_class)); 275 276 point->set(SkFloatToScalar(env->GetIntField(obj, gPointF_xFieldID)), 277 SkFloatToScalar(env->GetIntField(obj, gPointF_yFieldID))); 278 return point; 279} 280 281void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj) 282{ 283 SkASSERT(env->IsInstanceOf(obj, gPointF_class)); 284 285 env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX)); 286 env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY)); 287} 288 289SkBitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) { 290 SkASSERT(env); 291 SkASSERT(bitmap); 292 SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); 293 SkBitmap* b = (SkBitmap*)env->GetIntField(bitmap, gBitmap_nativeInstanceID); 294 SkASSERT(b); 295 return b; 296} 297 298SkBitmap::Config GraphicsJNI::getNativeBitmapConfig(JNIEnv* env, 299 jobject jconfig) { 300 SkASSERT(env); 301 if (NULL == jconfig) { 302 return SkBitmap::kNo_Config; 303 } 304 SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class)); 305 int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID); 306 if (c < 0 || c >= SkBitmap::kConfigCount) { 307 c = SkBitmap::kNo_Config; 308 } 309 return static_cast<SkBitmap::Config>(c); 310} 311 312SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) { 313 SkASSERT(env); 314 SkASSERT(canvas); 315 SkASSERT(env->IsInstanceOf(canvas, gCanvas_class)); 316 SkCanvas* c = (SkCanvas*)env->GetIntField(canvas, gCanvas_nativeInstanceID); 317 SkASSERT(c); 318 return c; 319} 320 321SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) { 322 SkASSERT(env); 323 SkASSERT(paint); 324 SkASSERT(env->IsInstanceOf(paint, gPaint_class)); 325 SkPaint* p = (SkPaint*)env->GetIntField(paint, gPaint_nativeInstanceID); 326 SkASSERT(p); 327 return p; 328} 329 330SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) 331{ 332 SkASSERT(env); 333 SkASSERT(picture); 334 SkASSERT(env->IsInstanceOf(picture, gPicture_class)); 335 SkPicture* p = (SkPicture*)env->GetIntField(picture, gPicture_nativeInstanceID); 336 SkASSERT(p); 337 return p; 338} 339 340SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) 341{ 342 SkASSERT(env); 343 SkASSERT(region); 344 SkASSERT(env->IsInstanceOf(region, gRegion_class)); 345 SkRegion* r = (SkRegion*)env->GetIntField(region, gRegion_nativeInstanceID); 346 SkASSERT(r); 347 return r; 348} 349 350/////////////////////////////////////////////////////////////////////////////////////////// 351 352jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, 353 jbyteArray ninepatch) 354{ 355 SkASSERT(bitmap != NULL); 356 SkASSERT(NULL != bitmap->pixelRef()); 357 358 jobject obj = env->AllocObject(gBitmap_class); 359 if (obj) { 360 env->CallVoidMethod(obj, gBitmap_constructorMethodID, 361 (jint)bitmap, isMutable, ninepatch); 362 if (hasException(env)) { 363 obj = NULL; 364 } 365 } 366 return obj; 367} 368 369jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) 370{ 371 SkASSERT(region != NULL); 372 jobject obj = env->AllocObject(gRegion_class); 373 if (obj) { 374 env->CallVoidMethod(obj, gRegion_constructorMethodID, (jint)region, 0); 375 if (hasException(env)) { 376 obj = NULL; 377 } 378 } 379 return obj; 380} 381 382#include "SkPixelRef.h" 383 384static JNIEnv* vm2env(JavaVM* vm) 385{ 386 JNIEnv* env = NULL; 387 if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || NULL == env) 388 { 389 SkDebugf("------- [%p] vm->GetEnv() failed\n", vm); 390 sk_throw(); 391 } 392 return env; 393} 394 395#ifdef TRACK_LOCK_COUNT 396 static int gLockCount; 397#endif 398 399/////////////////////////////////////////////////////////////////////////////// 400 401#include "SkMallocPixelRef.h" 402 403/* Extend SkMallocPixelRef to inform the VM when we free up the storage 404*/ 405class AndroidPixelRef : public SkMallocPixelRef { 406public: 407 /** Allocate the specified buffer for pixels. The memory is freed when the 408 last owner of this pixelref is gone. Our caller has already informed 409 the VM of our allocation. 410 */ 411 AndroidPixelRef(JNIEnv* env, void* storage, size_t size, 412 SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { 413 SkASSERT(storage); 414 SkASSERT(env); 415 416 if (env->GetJavaVM(&fVM) != JNI_OK) { 417 SkDebugf("------ [%p] env->GetJavaVM failed\n", env); 418 sk_throw(); 419 } 420 } 421 422 virtual ~AndroidPixelRef() { 423 JNIEnv* env = vm2env(fVM); 424// SkDebugf("-------------- inform VM we're releasing %d bytes\n", this->getSize()); 425 jlong jsize = this->getSize(); // the VM wants longs for the size 426 env->CallVoidMethod(gVMRuntime_singleton, 427 gVMRuntime_trackExternalFreeMethodID, 428 jsize); 429 if (GraphicsJNI::hasException(env)) { 430 env->ExceptionClear(); 431 } 432 } 433 434private: 435 JavaVM* fVM; 436}; 437 438bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, 439 SkColorTable* ctable) { 440 Sk64 size64 = bitmap->getSize64(); 441 if (size64.isNeg() || !size64.is32()) { 442 doThrow(env, "java/lang/IllegalArgumentException", 443 "bitmap size exceeds 32bits"); 444 return false; 445 } 446 447 size_t size = size64.get32(); 448 // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size); 449 jlong jsize = size; // the VM wants longs for the size 450 bool r = env->CallBooleanMethod(gVMRuntime_singleton, 451 gVMRuntime_trackExternalAllocationMethodID, 452 jsize); 453 if (GraphicsJNI::hasException(env)) { 454 return false; 455 } 456 if (!r) { 457 LOGE("VM won't let us allocate %zd bytes\n", size); 458 doThrowOOME(env, "bitmap size exceeds VM budget"); 459 return false; 460 } 461 462 // call the version of malloc that returns null on failure 463 void* addr = sk_malloc_flags(size, 0); 464 if (NULL == addr) { 465 // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size); 466 // we didn't actually allocate it, so inform the VM 467 env->CallVoidMethod(gVMRuntime_singleton, 468 gVMRuntime_trackExternalFreeMethodID, 469 jsize); 470 if (!GraphicsJNI::hasException(env)) { 471 doThrowOOME(env, "bitmap size too large for malloc"); 472 } 473 return false; 474 } 475 476 bitmap->setPixelRef(new AndroidPixelRef(env, addr, size, ctable))->unref(); 477 // since we're already allocated, we lockPixels right away 478 // HeapAllocator behaves this way too 479 bitmap->lockPixels(); 480 return true; 481} 482 483/////////////////////////////////////////////////////////////////////////////// 484 485JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env) : fEnv(env) 486{ 487} 488 489bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { 490 return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable); 491} 492 493//////////////////////////////////////////////////////////////////////////////// 494 495static jclass make_globalref(JNIEnv* env, const char classname[]) 496{ 497 jclass c = env->FindClass(classname); 498 SkASSERT(c); 499 return (jclass)env->NewGlobalRef(c); 500} 501 502static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 503 const char fieldname[], const char type[]) 504{ 505 jfieldID id = env->GetFieldID(clazz, fieldname, type); 506 SkASSERT(id); 507 return id; 508} 509 510int register_android_graphics_Graphics(JNIEnv* env) 511{ 512 jmethodID m; 513 jclass c; 514 515 gRect_class = make_globalref(env, "android/graphics/Rect"); 516 gRect_leftFieldID = getFieldIDCheck(env, gRect_class, "left", "I"); 517 gRect_topFieldID = getFieldIDCheck(env, gRect_class, "top", "I"); 518 gRect_rightFieldID = getFieldIDCheck(env, gRect_class, "right", "I"); 519 gRect_bottomFieldID = getFieldIDCheck(env, gRect_class, "bottom", "I"); 520 521 gRectF_class = make_globalref(env, "android/graphics/RectF"); 522 gRectF_leftFieldID = getFieldIDCheck(env, gRectF_class, "left", "F"); 523 gRectF_topFieldID = getFieldIDCheck(env, gRectF_class, "top", "F"); 524 gRectF_rightFieldID = getFieldIDCheck(env, gRectF_class, "right", "F"); 525 gRectF_bottomFieldID = getFieldIDCheck(env, gRectF_class, "bottom", "F"); 526 527 gPoint_class = make_globalref(env, "android/graphics/Point"); 528 gPoint_xFieldID = getFieldIDCheck(env, gPoint_class, "x", "I"); 529 gPoint_yFieldID = getFieldIDCheck(env, gPoint_class, "y", "I"); 530 531 gPointF_class = make_globalref(env, "android/graphics/PointF"); 532 gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F"); 533 gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F"); 534 535 gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); 536 gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); 537 gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", 538 "(IZ[B)V"); 539 540 gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config"); 541 gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class, 542 "nativeInt", "I"); 543 544 gCanvas_class = make_globalref(env, "android/graphics/Canvas"); 545 gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I"); 546 547 gPaint_class = make_globalref(env, "android/graphics/Paint"); 548 gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I"); 549 550 gPicture_class = make_globalref(env, "android/graphics/Picture"); 551 gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "I"); 552 553 gRegion_class = make_globalref(env, "android/graphics/Region"); 554 gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "I"); 555 gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>", 556 "(II)V"); 557 558 // Get the VMRuntime class. 559 c = env->FindClass("dalvik/system/VMRuntime"); 560 SkASSERT(c); 561 // Look up VMRuntime.getRuntime(). 562 m = env->GetStaticMethodID(c, "getRuntime", "()Ldalvik/system/VMRuntime;"); 563 SkASSERT(m); 564 // Call VMRuntime.getRuntime() and hold onto its result. 565 gVMRuntime_singleton = env->CallStaticObjectMethod(c, m); 566 SkASSERT(gVMRuntime_singleton); 567 gVMRuntime_singleton = (jobject)env->NewGlobalRef(gVMRuntime_singleton); 568 // Look up the VMRuntime methods we'll be using. 569 gVMRuntime_trackExternalAllocationMethodID = 570 env->GetMethodID(c, "trackExternalAllocation", "(J)Z"); 571 gVMRuntime_trackExternalFreeMethodID = 572 env->GetMethodID(c, "trackExternalFree", "(J)V"); 573 574 NIOBuffer::RegisterJNI(env); 575 576 return 0; 577} 578 579