Graphics.cpp revision 11ea33471e1a14a8594f0b2cd012d86340dd3bd8
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 REPORT_SIZE_TO_JVM 9//#define TRACK_LOCK_COUNT 10 11void doThrow(JNIEnv* env, const char* exc, const char* msg) { 12 // don't throw a new exception if we already have one pending 13 if (env->ExceptionCheck() == JNI_FALSE) { 14 jclass npeClazz; 15 16 npeClazz = env->FindClass(exc); 17 LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc); 18 19 env->ThrowNew(npeClazz, msg); 20 } 21} 22 23void doThrowNPE(JNIEnv* env) { 24 doThrow(env, "java/lang/NullPointerException"); 25} 26 27void doThrowAIOOBE(JNIEnv* env) { 28 doThrow(env, "java/lang/ArrayIndexOutOfBoundsException"); 29} 30 31void doThrowRE(JNIEnv* env, const char* msg) { 32 doThrow(env, "java/lang/RuntimeException", msg); 33} 34 35void doThrowIAE(JNIEnv* env, const char* msg) { 36 doThrow(env, "java/lang/IllegalArgumentException", msg); 37} 38 39void doThrowISE(JNIEnv* env, const char* msg) { 40 doThrow(env, "java/lang/IllegalStateException", msg); 41} 42 43void doThrowOOME(JNIEnv* env, const char* msg) { 44 doThrow(env, "java/lang/OutOfMemoryError", msg); 45} 46 47bool GraphicsJNI::hasException(JNIEnv *env) { 48 if (env->ExceptionCheck() != 0) { 49 LOGE("*** Uncaught exception returned from Java call!\n"); 50 env->ExceptionDescribe(); 51 return true; 52 } 53 return false; 54} 55 56/////////////////////////////////////////////////////////////////////////////// 57 58AutoJavaFloatArray::AutoJavaFloatArray(JNIEnv* env, jfloatArray array, 59 int minLength) 60: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 61 SkASSERT(env); 62 if (array) { 63 fLen = env->GetArrayLength(array); 64 if (fLen < minLength) { 65 sk_throw(); 66 } 67 fPtr = env->GetFloatArrayElements(array, NULL); 68 } 69} 70 71AutoJavaFloatArray::~AutoJavaFloatArray() { 72 if (fPtr) { 73 fEnv->ReleaseFloatArrayElements(fArray, fPtr, 0); 74 } 75} 76 77AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array, 78 int minLength) 79: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 80 SkASSERT(env); 81 if (array) { 82 fLen = env->GetArrayLength(array); 83 if (fLen < minLength) { 84 sk_throw(); 85 } 86 fPtr = env->GetIntArrayElements(array, NULL); 87 } 88} 89 90AutoJavaIntArray::~AutoJavaIntArray() { 91 if (fPtr) { 92 fEnv->ReleaseIntArrayElements(fArray, fPtr, 0); 93 } 94} 95 96AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array, 97 int minLength) 98: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 99 SkASSERT(env); 100 if (array) { 101 fLen = env->GetArrayLength(array); 102 if (fLen < minLength) { 103 sk_throw(); 104 } 105 fPtr = env->GetShortArrayElements(array, NULL); 106 } 107} 108 109AutoJavaShortArray::~AutoJavaShortArray() { 110 if (fPtr) { 111 fEnv->ReleaseShortArrayElements(fArray, fPtr, 0); 112 } 113} 114 115AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array, 116 int minLength) 117: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 118 SkASSERT(env); 119 if (array) { 120 fLen = env->GetArrayLength(array); 121 if (fLen < minLength) { 122 sk_throw(); 123 } 124 fPtr = env->GetByteArrayElements(array, NULL); 125 } 126} 127 128AutoJavaByteArray::~AutoJavaByteArray() { 129 if (fPtr) { 130 fEnv->ReleaseByteArrayElements(fArray, fPtr, 0); 131 } 132} 133 134/////////////////////////////////////////////////////////////////////////////// 135 136static jclass gRect_class; 137static jfieldID gRect_leftFieldID; 138static jfieldID gRect_topFieldID; 139static jfieldID gRect_rightFieldID; 140static jfieldID gRect_bottomFieldID; 141 142static jclass gRectF_class; 143static jfieldID gRectF_leftFieldID; 144static jfieldID gRectF_topFieldID; 145static jfieldID gRectF_rightFieldID; 146static jfieldID gRectF_bottomFieldID; 147 148static jclass gPoint_class; 149static jfieldID gPoint_xFieldID; 150static jfieldID gPoint_yFieldID; 151 152static jclass gPointF_class; 153static jfieldID gPointF_xFieldID; 154static jfieldID gPointF_yFieldID; 155 156static jclass gBitmap_class; 157static jfieldID gBitmap_nativeInstanceID; 158static jmethodID gBitmap_constructorMethodID; 159static jmethodID gBitmap_allocBufferMethodID; 160 161static jclass gBitmapConfig_class; 162static jfieldID gBitmapConfig_nativeInstanceID; 163 164static jclass gCanvas_class; 165static jfieldID gCanvas_nativeInstanceID; 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 322SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) { 323 SkASSERT(env); 324 SkASSERT(paint); 325 SkASSERT(env->IsInstanceOf(paint, gPaint_class)); 326 SkPaint* p = (SkPaint*)env->GetIntField(paint, gPaint_nativeInstanceID); 327 SkASSERT(p); 328 return p; 329} 330 331SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) 332{ 333 SkASSERT(env); 334 SkASSERT(picture); 335 SkASSERT(env->IsInstanceOf(picture, gPicture_class)); 336 SkPicture* p = (SkPicture*)env->GetIntField(picture, gPicture_nativeInstanceID); 337 SkASSERT(p); 338 return p; 339} 340 341SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) 342{ 343 SkASSERT(env); 344 SkASSERT(region); 345 SkASSERT(env->IsInstanceOf(region, gRegion_class)); 346 SkRegion* r = (SkRegion*)env->GetIntField(region, gRegion_nativeInstanceID); 347 SkASSERT(r); 348 return r; 349} 350 351/////////////////////////////////////////////////////////////////////////////////////////// 352 353jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, 354 jbyteArray ninepatch) 355{ 356 SkASSERT(bitmap != NULL); 357 SkASSERT(NULL != bitmap->pixelRef()); 358 359 jobject obj = env->AllocObject(gBitmap_class); 360 if (obj) { 361 env->CallVoidMethod(obj, gBitmap_constructorMethodID, 362 (jint)bitmap, isMutable, ninepatch); 363 if (hasException(env)) { 364 obj = NULL; 365 } 366 } 367 return obj; 368} 369 370jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) 371{ 372 SkASSERT(region != NULL); 373 jobject obj = env->AllocObject(gRegion_class); 374 if (obj) { 375 env->CallVoidMethod(obj, gRegion_constructorMethodID, (jint)region, 0); 376 if (hasException(env)) { 377 obj = NULL; 378 } 379 } 380 return obj; 381} 382 383#include "SkPixelRef.h" 384 385static JNIEnv* vm2env(JavaVM* vm) 386{ 387 JNIEnv* env = NULL; 388 if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || NULL == env) 389 { 390 SkDebugf("------- [%p] vm->GetEnv() failed\n", vm); 391 sk_throw(); 392 } 393 return env; 394} 395 396#ifdef TRACK_LOCK_COUNT 397 static int gLockCount; 398#endif 399 400/////////////////////////////////////////////////////////////////////////////// 401 402#include "SkMallocPixelRef.h" 403 404/* Extend SkMallocPixelRef to inform the VM when we free up the storage 405*/ 406class AndroidPixelRef : public SkMallocPixelRef { 407public: 408 /** Allocate the specified buffer for pixels. The memory is freed when the 409 last owner of this pixelref is gone. Our caller has already informed 410 the VM of our allocation. 411 */ 412 AndroidPixelRef(JNIEnv* env, void* storage, size_t size, 413 SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { 414 SkASSERT(storage); 415 SkASSERT(env); 416 417 if (env->GetJavaVM(&fVM) != JNI_OK) { 418 SkDebugf("------ [%p] env->GetJavaVM failed\n", env); 419 sk_throw(); 420 } 421 } 422 423 virtual ~AndroidPixelRef() { 424 JNIEnv* env = vm2env(fVM); 425// SkDebugf("-------------- inform VM we're releasing %d bytes\n", this->getSize()); 426 jlong jsize = this->getSize(); // the VM wants longs for the size 427 env->CallVoidMethod(gVMRuntime_singleton, 428 gVMRuntime_trackExternalFreeMethodID, 429 jsize); 430 if (GraphicsJNI::hasException(env)) { 431 env->ExceptionClear(); 432 } 433 } 434 435private: 436 JavaVM* fVM; 437}; 438 439bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, 440 SkColorTable* ctable, bool reportSizeToVM) { 441 Sk64 size64 = bitmap->getSize64(); 442 if (size64.isNeg() || !size64.is32()) { 443 doThrow(env, "java/lang/IllegalArgumentException", 444 "bitmap size exceeds 32bits"); 445 return false; 446 } 447 448 size_t size = size64.get32(); 449 jlong jsize = size; // the VM wants longs for the size 450 if (reportSizeToVM) { 451 // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size); 452 bool r = env->CallBooleanMethod(gVMRuntime_singleton, 453 gVMRuntime_trackExternalAllocationMethodID, 454 jsize); 455 if (GraphicsJNI::hasException(env)) { 456 return false; 457 } 458 if (!r) { 459 LOGE("VM won't let us allocate %zd bytes\n", size); 460 doThrowOOME(env, "bitmap size exceeds VM budget"); 461 return false; 462 } 463 } 464 // call the version of malloc that returns null on failure 465 void* addr = sk_malloc_flags(size, 0); 466 if (NULL == addr) { 467 if (reportSizeToVM) { 468 // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size); 469 // we didn't actually allocate it, so inform the VM 470 env->CallVoidMethod(gVMRuntime_singleton, 471 gVMRuntime_trackExternalFreeMethodID, 472 jsize); 473 if (!GraphicsJNI::hasException(env)) { 474 doThrowOOME(env, "bitmap size too large for malloc"); 475 } 476 } 477 return false; 478 } 479 480 SkPixelRef* pr = reportSizeToVM ? 481 new AndroidPixelRef(env, addr, size, ctable) : 482 new SkMallocPixelRef(addr, size, ctable); 483 bitmap->setPixelRef(pr)->unref(); 484 // since we're already allocated, we lockPixels right away 485 // HeapAllocator behaves this way too 486 bitmap->lockPixels(); 487 return true; 488} 489 490/////////////////////////////////////////////////////////////////////////////// 491 492JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM) 493 : fEnv(env), fReportSizeToVM(reportSizeToVM) {} 494 495bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { 496 return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM); 497} 498 499//////////////////////////////////////////////////////////////////////////////// 500 501static jclass make_globalref(JNIEnv* env, const char classname[]) 502{ 503 jclass c = env->FindClass(classname); 504 SkASSERT(c); 505 return (jclass)env->NewGlobalRef(c); 506} 507 508static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz, 509 const char fieldname[], const char type[]) 510{ 511 jfieldID id = env->GetFieldID(clazz, fieldname, type); 512 SkASSERT(id); 513 return id; 514} 515 516int register_android_graphics_Graphics(JNIEnv* env) 517{ 518 jmethodID m; 519 jclass c; 520 521 gRect_class = make_globalref(env, "android/graphics/Rect"); 522 gRect_leftFieldID = getFieldIDCheck(env, gRect_class, "left", "I"); 523 gRect_topFieldID = getFieldIDCheck(env, gRect_class, "top", "I"); 524 gRect_rightFieldID = getFieldIDCheck(env, gRect_class, "right", "I"); 525 gRect_bottomFieldID = getFieldIDCheck(env, gRect_class, "bottom", "I"); 526 527 gRectF_class = make_globalref(env, "android/graphics/RectF"); 528 gRectF_leftFieldID = getFieldIDCheck(env, gRectF_class, "left", "F"); 529 gRectF_topFieldID = getFieldIDCheck(env, gRectF_class, "top", "F"); 530 gRectF_rightFieldID = getFieldIDCheck(env, gRectF_class, "right", "F"); 531 gRectF_bottomFieldID = getFieldIDCheck(env, gRectF_class, "bottom", "F"); 532 533 gPoint_class = make_globalref(env, "android/graphics/Point"); 534 gPoint_xFieldID = getFieldIDCheck(env, gPoint_class, "x", "I"); 535 gPoint_yFieldID = getFieldIDCheck(env, gPoint_class, "y", "I"); 536 537 gPointF_class = make_globalref(env, "android/graphics/PointF"); 538 gPointF_xFieldID = getFieldIDCheck(env, gPointF_class, "x", "F"); 539 gPointF_yFieldID = getFieldIDCheck(env, gPointF_class, "y", "F"); 540 541 gBitmap_class = make_globalref(env, "android/graphics/Bitmap"); 542 gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I"); 543 gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", 544 "(IZ[B)V"); 545 546 gBitmapConfig_class = make_globalref(env, "android/graphics/Bitmap$Config"); 547 gBitmapConfig_nativeInstanceID = getFieldIDCheck(env, gBitmapConfig_class, 548 "nativeInt", "I"); 549 550 gCanvas_class = make_globalref(env, "android/graphics/Canvas"); 551 gCanvas_nativeInstanceID = getFieldIDCheck(env, gCanvas_class, "mNativeCanvas", "I"); 552 553 gPaint_class = make_globalref(env, "android/graphics/Paint"); 554 gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I"); 555 556 gPicture_class = make_globalref(env, "android/graphics/Picture"); 557 gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "I"); 558 559 gRegion_class = make_globalref(env, "android/graphics/Region"); 560 gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "I"); 561 gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>", 562 "(II)V"); 563 564 // Get the VMRuntime class. 565 c = env->FindClass("dalvik/system/VMRuntime"); 566 SkASSERT(c); 567 // Look up VMRuntime.getRuntime(). 568 m = env->GetStaticMethodID(c, "getRuntime", "()Ldalvik/system/VMRuntime;"); 569 SkASSERT(m); 570 // Call VMRuntime.getRuntime() and hold onto its result. 571 gVMRuntime_singleton = env->CallStaticObjectMethod(c, m); 572 SkASSERT(gVMRuntime_singleton); 573 gVMRuntime_singleton = (jobject)env->NewGlobalRef(gVMRuntime_singleton); 574 // Look up the VMRuntime methods we'll be using. 575 gVMRuntime_trackExternalAllocationMethodID = 576 env->GetMethodID(c, "trackExternalAllocation", "(J)Z"); 577 gVMRuntime_trackExternalFreeMethodID = 578 env->GetMethodID(c, "trackExternalFree", "(J)V"); 579 580 NIOBuffer::RegisterJNI(env); 581 582 return 0; 583} 584 585