Graphics.cpp revision c04851fd0af87f44a7d7351e0c17442fa1d3fc28
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, JNIAccess access) 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 fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0; 70} 71 72AutoJavaFloatArray::~AutoJavaFloatArray() { 73 if (fPtr) { 74 fEnv->ReleaseFloatArrayElements(fArray, fPtr, fReleaseMode); 75 } 76} 77 78AutoJavaIntArray::AutoJavaIntArray(JNIEnv* env, jintArray array, 79 int minLength) 80: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 81 SkASSERT(env); 82 if (array) { 83 fLen = env->GetArrayLength(array); 84 if (fLen < minLength) { 85 sk_throw(); 86 } 87 fPtr = env->GetIntArrayElements(array, NULL); 88 } 89} 90 91AutoJavaIntArray::~AutoJavaIntArray() { 92 if (fPtr) { 93 fEnv->ReleaseIntArrayElements(fArray, fPtr, 0); 94 } 95} 96 97AutoJavaShortArray::AutoJavaShortArray(JNIEnv* env, jshortArray array, 98 int minLength, JNIAccess access) 99: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 100 SkASSERT(env); 101 if (array) { 102 fLen = env->GetArrayLength(array); 103 if (fLen < minLength) { 104 sk_throw(); 105 } 106 fPtr = env->GetShortArrayElements(array, NULL); 107 } 108 fReleaseMode = (access == kRO_JNIAccess) ? JNI_ABORT : 0; 109} 110 111AutoJavaShortArray::~AutoJavaShortArray() { 112 if (fPtr) { 113 fEnv->ReleaseShortArrayElements(fArray, fPtr, fReleaseMode); 114 } 115} 116 117AutoJavaByteArray::AutoJavaByteArray(JNIEnv* env, jbyteArray array, 118 int minLength) 119: fEnv(env), fArray(array), fPtr(NULL), fLen(0) { 120 SkASSERT(env); 121 if (array) { 122 fLen = env->GetArrayLength(array); 123 if (fLen < minLength) { 124 sk_throw(); 125 } 126 fPtr = env->GetByteArrayElements(array, NULL); 127 } 128} 129 130AutoJavaByteArray::~AutoJavaByteArray() { 131 if (fPtr) { 132 fEnv->ReleaseByteArrayElements(fArray, fPtr, 0); 133 } 134} 135 136/////////////////////////////////////////////////////////////////////////////// 137 138static jclass gRect_class; 139static jfieldID gRect_leftFieldID; 140static jfieldID gRect_topFieldID; 141static jfieldID gRect_rightFieldID; 142static jfieldID gRect_bottomFieldID; 143 144static jclass gRectF_class; 145static jfieldID gRectF_leftFieldID; 146static jfieldID gRectF_topFieldID; 147static jfieldID gRectF_rightFieldID; 148static jfieldID gRectF_bottomFieldID; 149 150static jclass gPoint_class; 151static jfieldID gPoint_xFieldID; 152static jfieldID gPoint_yFieldID; 153 154static jclass gPointF_class; 155static jfieldID gPointF_xFieldID; 156static jfieldID gPointF_yFieldID; 157 158static jclass gBitmap_class; 159static jfieldID gBitmap_nativeInstanceID; 160static jmethodID gBitmap_constructorMethodID; 161static jmethodID gBitmap_allocBufferMethodID; 162 163static jclass gBitmapConfig_class; 164static jfieldID gBitmapConfig_nativeInstanceID; 165 166static jclass gCanvas_class; 167static jfieldID gCanvas_nativeInstanceID; 168 169static jclass gPaint_class; 170static jfieldID gPaint_nativeInstanceID; 171 172static jclass gPicture_class; 173static jfieldID gPicture_nativeInstanceID; 174 175static jclass gRegion_class; 176static jfieldID gRegion_nativeInstanceID; 177static jmethodID gRegion_constructorMethodID; 178 179static jobject gVMRuntime_singleton; 180static jmethodID gVMRuntime_trackExternalAllocationMethodID; 181static jmethodID gVMRuntime_trackExternalFreeMethodID; 182 183/////////////////////////////////////////////////////////////////////////////// 184 185void GraphicsJNI::get_jrect(JNIEnv* env, jobject obj, int* L, int* T, int* R, int* B) 186{ 187 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 188 189 *L = env->GetIntField(obj, gRect_leftFieldID); 190 *T = env->GetIntField(obj, gRect_topFieldID); 191 *R = env->GetIntField(obj, gRect_rightFieldID); 192 *B = env->GetIntField(obj, gRect_bottomFieldID); 193} 194 195void GraphicsJNI::set_jrect(JNIEnv* env, jobject obj, int L, int T, int R, int B) 196{ 197 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 198 199 env->SetIntField(obj, gRect_leftFieldID, L); 200 env->SetIntField(obj, gRect_topFieldID, T); 201 env->SetIntField(obj, gRect_rightFieldID, R); 202 env->SetIntField(obj, gRect_bottomFieldID, B); 203} 204 205SkIRect* GraphicsJNI::jrect_to_irect(JNIEnv* env, jobject obj, SkIRect* ir) 206{ 207 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 208 209 ir->set(env->GetIntField(obj, gRect_leftFieldID), 210 env->GetIntField(obj, gRect_topFieldID), 211 env->GetIntField(obj, gRect_rightFieldID), 212 env->GetIntField(obj, gRect_bottomFieldID)); 213 return ir; 214} 215 216void GraphicsJNI::irect_to_jrect(const SkIRect& ir, JNIEnv* env, jobject obj) 217{ 218 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 219 220 env->SetIntField(obj, gRect_leftFieldID, ir.fLeft); 221 env->SetIntField(obj, gRect_topFieldID, ir.fTop); 222 env->SetIntField(obj, gRect_rightFieldID, ir.fRight); 223 env->SetIntField(obj, gRect_bottomFieldID, ir.fBottom); 224} 225 226SkRect* GraphicsJNI::jrectf_to_rect(JNIEnv* env, jobject obj, SkRect* r) 227{ 228 SkASSERT(env->IsInstanceOf(obj, gRectF_class)); 229 230 r->set(SkFloatToScalar(env->GetFloatField(obj, gRectF_leftFieldID)), 231 SkFloatToScalar(env->GetFloatField(obj, gRectF_topFieldID)), 232 SkFloatToScalar(env->GetFloatField(obj, gRectF_rightFieldID)), 233 SkFloatToScalar(env->GetFloatField(obj, gRectF_bottomFieldID))); 234 return r; 235} 236 237SkRect* GraphicsJNI::jrect_to_rect(JNIEnv* env, jobject obj, SkRect* r) 238{ 239 SkASSERT(env->IsInstanceOf(obj, gRect_class)); 240 241 r->set(SkIntToScalar(env->GetIntField(obj, gRect_leftFieldID)), 242 SkIntToScalar(env->GetIntField(obj, gRect_topFieldID)), 243 SkIntToScalar(env->GetIntField(obj, gRect_rightFieldID)), 244 SkIntToScalar(env->GetIntField(obj, gRect_bottomFieldID))); 245 return r; 246} 247 248void GraphicsJNI::rect_to_jrectf(const SkRect& r, JNIEnv* env, jobject obj) 249{ 250 SkASSERT(env->IsInstanceOf(obj, gRectF_class)); 251 252 env->SetFloatField(obj, gRectF_leftFieldID, SkScalarToFloat(r.fLeft)); 253 env->SetFloatField(obj, gRectF_topFieldID, SkScalarToFloat(r.fTop)); 254 env->SetFloatField(obj, gRectF_rightFieldID, SkScalarToFloat(r.fRight)); 255 env->SetFloatField(obj, gRectF_bottomFieldID, SkScalarToFloat(r.fBottom)); 256} 257 258SkIPoint* GraphicsJNI::jpoint_to_ipoint(JNIEnv* env, jobject obj, SkIPoint* point) 259{ 260 SkASSERT(env->IsInstanceOf(obj, gPoint_class)); 261 262 point->set(env->GetIntField(obj, gPoint_xFieldID), 263 env->GetIntField(obj, gPoint_yFieldID)); 264 return point; 265} 266 267void GraphicsJNI::ipoint_to_jpoint(const SkIPoint& ir, JNIEnv* env, jobject obj) 268{ 269 SkASSERT(env->IsInstanceOf(obj, gPoint_class)); 270 271 env->SetIntField(obj, gPoint_xFieldID, ir.fX); 272 env->SetIntField(obj, gPoint_yFieldID, ir.fY); 273} 274 275SkPoint* GraphicsJNI::jpointf_to_point(JNIEnv* env, jobject obj, SkPoint* point) 276{ 277 SkASSERT(env->IsInstanceOf(obj, gPointF_class)); 278 279 point->set(SkFloatToScalar(env->GetIntField(obj, gPointF_xFieldID)), 280 SkFloatToScalar(env->GetIntField(obj, gPointF_yFieldID))); 281 return point; 282} 283 284void GraphicsJNI::point_to_jpointf(const SkPoint& r, JNIEnv* env, jobject obj) 285{ 286 SkASSERT(env->IsInstanceOf(obj, gPointF_class)); 287 288 env->SetFloatField(obj, gPointF_xFieldID, SkScalarToFloat(r.fX)); 289 env->SetFloatField(obj, gPointF_yFieldID, SkScalarToFloat(r.fY)); 290} 291 292SkBitmap* GraphicsJNI::getNativeBitmap(JNIEnv* env, jobject bitmap) { 293 SkASSERT(env); 294 SkASSERT(bitmap); 295 SkASSERT(env->IsInstanceOf(bitmap, gBitmap_class)); 296 SkBitmap* b = (SkBitmap*)env->GetIntField(bitmap, gBitmap_nativeInstanceID); 297 SkASSERT(b); 298 return b; 299} 300 301SkBitmap::Config GraphicsJNI::getNativeBitmapConfig(JNIEnv* env, 302 jobject jconfig) { 303 SkASSERT(env); 304 if (NULL == jconfig) { 305 return SkBitmap::kNo_Config; 306 } 307 SkASSERT(env->IsInstanceOf(jconfig, gBitmapConfig_class)); 308 int c = env->GetIntField(jconfig, gBitmapConfig_nativeInstanceID); 309 if (c < 0 || c >= SkBitmap::kConfigCount) { 310 c = SkBitmap::kNo_Config; 311 } 312 return static_cast<SkBitmap::Config>(c); 313} 314 315SkCanvas* GraphicsJNI::getNativeCanvas(JNIEnv* env, jobject canvas) { 316 SkASSERT(env); 317 SkASSERT(canvas); 318 SkASSERT(env->IsInstanceOf(canvas, gCanvas_class)); 319 SkCanvas* c = (SkCanvas*)env->GetIntField(canvas, gCanvas_nativeInstanceID); 320 SkASSERT(c); 321 return c; 322} 323 324SkPaint* GraphicsJNI::getNativePaint(JNIEnv* env, jobject paint) { 325 SkASSERT(env); 326 SkASSERT(paint); 327 SkASSERT(env->IsInstanceOf(paint, gPaint_class)); 328 SkPaint* p = (SkPaint*)env->GetIntField(paint, gPaint_nativeInstanceID); 329 SkASSERT(p); 330 return p; 331} 332 333SkPicture* GraphicsJNI::getNativePicture(JNIEnv* env, jobject picture) 334{ 335 SkASSERT(env); 336 SkASSERT(picture); 337 SkASSERT(env->IsInstanceOf(picture, gPicture_class)); 338 SkPicture* p = (SkPicture*)env->GetIntField(picture, gPicture_nativeInstanceID); 339 SkASSERT(p); 340 return p; 341} 342 343SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region) 344{ 345 SkASSERT(env); 346 SkASSERT(region); 347 SkASSERT(env->IsInstanceOf(region, gRegion_class)); 348 SkRegion* r = (SkRegion*)env->GetIntField(region, gRegion_nativeInstanceID); 349 SkASSERT(r); 350 return r; 351} 352 353/////////////////////////////////////////////////////////////////////////////////////////// 354 355jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable, 356 jbyteArray ninepatch, int density) 357{ 358 SkASSERT(bitmap != NULL); 359 SkASSERT(NULL != bitmap->pixelRef()); 360 361 jobject obj = env->AllocObject(gBitmap_class); 362 if (obj) { 363 env->CallVoidMethod(obj, gBitmap_constructorMethodID, 364 (jint)bitmap, isMutable, ninepatch, density); 365 if (hasException(env)) { 366 obj = NULL; 367 } 368 } 369 return obj; 370} 371 372jobject GraphicsJNI::createRegion(JNIEnv* env, SkRegion* region) 373{ 374 SkASSERT(region != NULL); 375 jobject obj = env->AllocObject(gRegion_class); 376 if (obj) { 377 env->CallVoidMethod(obj, gRegion_constructorMethodID, (jint)region, 0); 378 if (hasException(env)) { 379 obj = NULL; 380 } 381 } 382 return obj; 383} 384 385#include "SkPixelRef.h" 386 387static JNIEnv* vm2env(JavaVM* vm) 388{ 389 JNIEnv* env = NULL; 390 if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK || NULL == env) 391 { 392 SkDebugf("------- [%p] vm->GetEnv() failed\n", vm); 393 sk_throw(); 394 } 395 return env; 396} 397 398#ifdef TRACK_LOCK_COUNT 399 static int gLockCount; 400#endif 401 402/////////////////////////////////////////////////////////////////////////////// 403 404#include "SkMallocPixelRef.h" 405 406/* Extend SkMallocPixelRef to inform the VM when we free up the storage 407*/ 408class AndroidPixelRef : public SkMallocPixelRef { 409public: 410 /** Allocate the specified buffer for pixels. The memory is freed when the 411 last owner of this pixelref is gone. Our caller has already informed 412 the VM of our allocation. 413 */ 414 AndroidPixelRef(JNIEnv* env, void* storage, size_t size, 415 SkColorTable* ctable) : SkMallocPixelRef(storage, size, ctable) { 416 SkASSERT(storage); 417 SkASSERT(env); 418 419 if (env->GetJavaVM(&fVM) != JNI_OK) { 420 SkDebugf("------ [%p] env->GetJavaVM failed\n", env); 421 sk_throw(); 422 } 423 } 424 425 virtual ~AndroidPixelRef() { 426 JNIEnv* env = vm2env(fVM); 427// SkDebugf("-------------- inform VM we're releasing %d bytes\n", this->getSize()); 428 jlong jsize = this->getSize(); // the VM wants longs for the size 429 env->CallVoidMethod(gVMRuntime_singleton, 430 gVMRuntime_trackExternalFreeMethodID, 431 jsize); 432 if (GraphicsJNI::hasException(env)) { 433 env->ExceptionClear(); 434 } 435 } 436 437private: 438 JavaVM* fVM; 439}; 440 441bool GraphicsJNI::setJavaPixelRef(JNIEnv* env, SkBitmap* bitmap, 442 SkColorTable* ctable, bool reportSizeToVM) { 443 Sk64 size64 = bitmap->getSize64(); 444 if (size64.isNeg() || !size64.is32()) { 445 doThrow(env, "java/lang/IllegalArgumentException", 446 "bitmap size exceeds 32bits"); 447 return false; 448 } 449 450 size_t size = size64.get32(); 451 jlong jsize = size; // the VM wants longs for the size 452 if (reportSizeToVM) { 453 // SkDebugf("-------------- inform VM we've allocated %d bytes\n", size); 454 bool r = env->CallBooleanMethod(gVMRuntime_singleton, 455 gVMRuntime_trackExternalAllocationMethodID, 456 jsize); 457 if (GraphicsJNI::hasException(env)) { 458 return false; 459 } 460 if (!r) { 461 LOGE("VM won't let us allocate %zd bytes\n", size); 462 doThrowOOME(env, "bitmap size exceeds VM budget"); 463 return false; 464 } 465 } 466 // call the version of malloc that returns null on failure 467 void* addr = sk_malloc_flags(size, 0); 468 if (NULL == addr) { 469 if (reportSizeToVM) { 470 // SkDebugf("-------------- inform VM we're releasing %d bytes which we couldn't allocate\n", size); 471 // we didn't actually allocate it, so inform the VM 472 env->CallVoidMethod(gVMRuntime_singleton, 473 gVMRuntime_trackExternalFreeMethodID, 474 jsize); 475 if (!GraphicsJNI::hasException(env)) { 476 doThrowOOME(env, "bitmap size too large for malloc"); 477 } 478 } 479 return false; 480 } 481 482 SkPixelRef* pr = reportSizeToVM ? 483 new AndroidPixelRef(env, addr, size, ctable) : 484 new SkMallocPixelRef(addr, size, ctable); 485 bitmap->setPixelRef(pr)->unref(); 486 // since we're already allocated, we lockPixels right away 487 // HeapAllocator behaves this way too 488 bitmap->lockPixels(); 489 return true; 490} 491 492/////////////////////////////////////////////////////////////////////////////// 493 494JavaPixelAllocator::JavaPixelAllocator(JNIEnv* env, bool reportSizeToVM) 495 : fEnv(env), fReportSizeToVM(reportSizeToVM) {} 496 497bool JavaPixelAllocator::allocPixelRef(SkBitmap* bitmap, SkColorTable* ctable) { 498 return GraphicsJNI::setJavaPixelRef(fEnv, bitmap, ctable, fReportSizeToVM); 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[BI)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 555 gPaint_class = make_globalref(env, "android/graphics/Paint"); 556 gPaint_nativeInstanceID = getFieldIDCheck(env, gPaint_class, "mNativePaint", "I"); 557 558 gPicture_class = make_globalref(env, "android/graphics/Picture"); 559 gPicture_nativeInstanceID = getFieldIDCheck(env, gPicture_class, "mNativePicture", "I"); 560 561 gRegion_class = make_globalref(env, "android/graphics/Region"); 562 gRegion_nativeInstanceID = getFieldIDCheck(env, gRegion_class, "mNativeRegion", "I"); 563 gRegion_constructorMethodID = env->GetMethodID(gRegion_class, "<init>", 564 "(II)V"); 565 566 // Get the VMRuntime class. 567 c = env->FindClass("dalvik/system/VMRuntime"); 568 SkASSERT(c); 569 // Look up VMRuntime.getRuntime(). 570 m = env->GetStaticMethodID(c, "getRuntime", "()Ldalvik/system/VMRuntime;"); 571 SkASSERT(m); 572 // Call VMRuntime.getRuntime() and hold onto its result. 573 gVMRuntime_singleton = env->CallStaticObjectMethod(c, m); 574 SkASSERT(gVMRuntime_singleton); 575 gVMRuntime_singleton = (jobject)env->NewGlobalRef(gVMRuntime_singleton); 576 // Look up the VMRuntime methods we'll be using. 577 gVMRuntime_trackExternalAllocationMethodID = 578 env->GetMethodID(c, "trackExternalAllocation", "(J)Z"); 579 gVMRuntime_trackExternalFreeMethodID = 580 env->GetMethodID(c, "trackExternalFree", "(J)V"); 581 582 NIOBuffer::RegisterJNI(env); 583 584 return 0; 585} 586 587