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