Bitmap.cpp revision 9aaa8269a3e7291aab84d01c3fc9c744d8f2d2f4
1#include "SkBitmap.h" 2#include "SkImageEncoder.h" 3#include "SkColorPriv.h" 4#include "GraphicsJNI.h" 5#include "SkDither.h" 6#include "SkUnPreMultiply.h" 7 8#include <binder/Parcel.h> 9#include "android_util_Binder.h" 10#include "android_nio_utils.h" 11#include "CreateJavaOutputStreamAdaptor.h" 12 13#include <jni.h> 14 15#include <Caches.h> 16 17#if 0 18 #define TRACE_BITMAP(code) code 19#else 20 #define TRACE_BITMAP(code) 21#endif 22 23/////////////////////////////////////////////////////////////////////////////// 24// Conversions to/from SkColor, for get/setPixels, and the create method, which 25// is basically like setPixels 26 27typedef void (*FromColorProc)(void* dst, const SkColor src[], int width, 28 int x, int y); 29 30static void FromColor_D32(void* dst, const SkColor src[], int width, 31 int, int) { 32 SkPMColor* d = (SkPMColor*)dst; 33 34 for (int i = 0; i < width; i++) { 35 *d++ = SkPreMultiplyColor(*src++); 36 } 37} 38 39static void FromColor_D565(void* dst, const SkColor src[], int width, 40 int x, int y) { 41 uint16_t* d = (uint16_t*)dst; 42 43 DITHER_565_SCAN(y); 44 for (int stop = x + width; x < stop; x++) { 45 SkColor c = *src++; 46 *d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c), 47 DITHER_VALUE(x)); 48 } 49} 50 51static void FromColor_D4444(void* dst, const SkColor src[], int width, 52 int x, int y) { 53 SkPMColor16* d = (SkPMColor16*)dst; 54 55 DITHER_4444_SCAN(y); 56 for (int stop = x + width; x < stop; x++) { 57 SkPMColor c = SkPreMultiplyColor(*src++); 58 *d++ = SkDitherARGB32To4444(c, DITHER_VALUE(x)); 59// *d++ = SkPixel32ToPixel4444(c); 60 } 61} 62 63// can return NULL 64static FromColorProc ChooseFromColorProc(SkBitmap::Config config) { 65 switch (config) { 66 case SkBitmap::kARGB_8888_Config: 67 return FromColor_D32; 68 case SkBitmap::kARGB_4444_Config: 69 return FromColor_D4444; 70 case SkBitmap::kRGB_565_Config: 71 return FromColor_D565; 72 default: 73 break; 74 } 75 return NULL; 76} 77 78bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, 79 int srcOffset, int srcStride, 80 int x, int y, int width, int height, 81 const SkBitmap& dstBitmap) { 82 SkAutoLockPixels alp(dstBitmap); 83 void* dst = dstBitmap.getPixels(); 84 FromColorProc proc = ChooseFromColorProc(dstBitmap.config()); 85 86 if (NULL == dst || NULL == proc) { 87 return false; 88 } 89 90 const jint* array = env->GetIntArrayElements(srcColors, NULL); 91 const SkColor* src = (const SkColor*)array + srcOffset; 92 93 // reset to to actual choice from caller 94 dst = dstBitmap.getAddr(x, y); 95 // now copy/convert each scanline 96 for (int y = 0; y < height; y++) { 97 proc(dst, src, width, x, y); 98 src += srcStride; 99 dst = (char*)dst + dstBitmap.rowBytes(); 100 } 101 102 env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array), 103 JNI_ABORT); 104 return true; 105} 106 107//////////////////// ToColor procs 108 109typedef void (*ToColorProc)(SkColor dst[], const void* src, int width, 110 SkColorTable*); 111 112static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width, 113 SkColorTable*) { 114 SkASSERT(width > 0); 115 const SkPMColor* s = (const SkPMColor*)src; 116 do { 117 *dst++ = SkUnPreMultiply::PMColorToColor(*s++); 118 } while (--width != 0); 119} 120 121static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width, 122 SkColorTable*) { 123 SkASSERT(width > 0); 124 const SkPMColor* s = (const SkPMColor*)src; 125 do { 126 SkPMColor c = *s++; 127 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), 128 SkGetPackedB32(c)); 129 } while (--width != 0); 130} 131 132static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width, 133 SkColorTable*) { 134 SkASSERT(width > 0); 135 const SkPMColor16* s = (const SkPMColor16*)src; 136 do { 137 *dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++)); 138 } while (--width != 0); 139} 140 141static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width, 142 SkColorTable*) { 143 SkASSERT(width > 0); 144 const SkPMColor* s = (const SkPMColor*)src; 145 do { 146 SkPMColor c = SkPixel4444ToPixel32(*s++); 147 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), 148 SkGetPackedB32(c)); 149 } while (--width != 0); 150} 151 152static void ToColor_S565(SkColor dst[], const void* src, int width, 153 SkColorTable*) { 154 SkASSERT(width > 0); 155 const uint16_t* s = (const uint16_t*)src; 156 do { 157 uint16_t c = *s++; 158 *dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c), 159 SkPacked16ToB32(c)); 160 } while (--width != 0); 161} 162 163static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width, 164 SkColorTable* ctable) { 165 SkASSERT(width > 0); 166 const uint8_t* s = (const uint8_t*)src; 167 const SkPMColor* colors = ctable->lockColors(); 168 do { 169 *dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]); 170 } while (--width != 0); 171 ctable->unlockColors(false); 172} 173 174static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width, 175 SkColorTable* ctable) { 176 SkASSERT(width > 0); 177 const uint8_t* s = (const uint8_t*)src; 178 const SkPMColor* colors = ctable->lockColors(); 179 do { 180 SkPMColor c = colors[*s++]; 181 *dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c), 182 SkGetPackedB32(c)); 183 } while (--width != 0); 184 ctable->unlockColors(false); 185} 186 187// can return NULL 188static ToColorProc ChooseToColorProc(const SkBitmap& src) { 189 switch (src.config()) { 190 case SkBitmap::kARGB_8888_Config: 191 return src.isOpaque() ? ToColor_S32_Opaque : ToColor_S32_Alpha; 192 case SkBitmap::kARGB_4444_Config: 193 return src.isOpaque() ? ToColor_S4444_Opaque : ToColor_S4444_Alpha; 194 case SkBitmap::kRGB_565_Config: 195 return ToColor_S565; 196 case SkBitmap::kIndex8_Config: 197 if (src.getColorTable() == NULL) { 198 return NULL; 199 } 200 return src.isOpaque() ? ToColor_SI8_Opaque : ToColor_SI8_Alpha; 201 default: 202 break; 203 } 204 return NULL; 205} 206 207/////////////////////////////////////////////////////////////////////////////// 208/////////////////////////////////////////////////////////////////////////////// 209 210static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors, 211 int offset, int stride, int width, int height, 212 SkBitmap::Config config, jboolean isMutable) { 213 if (width <= 0 || height <= 0) { 214 doThrowIAE(env, "width and height must be > 0"); 215 return NULL; 216 } 217 218 if (NULL != jColors) { 219 size_t n = env->GetArrayLength(jColors); 220 if (n < SkAbs32(stride) * (size_t)height) { 221 doThrowAIOOBE(env); 222 return NULL; 223 } 224 } 225 226 SkBitmap bitmap; 227 228 bitmap.setConfig(config, width, height); 229 if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL, true)) { 230 return NULL; 231 } 232 233 if (jColors != NULL) { 234 GraphicsJNI::SetPixels(env, jColors, offset, stride, 235 0, 0, width, height, bitmap); 236 } 237 238 return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable, 239 NULL); 240} 241 242static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src, 243 SkBitmap::Config dstConfig, jboolean isMutable) { 244 SkBitmap result; 245 JavaPixelAllocator allocator(env, true); 246 247 if (!src->copyTo(&result, dstConfig, &allocator)) { 248 return NULL; 249 } 250 251 return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable, 252 NULL); 253} 254 255static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) { 256#ifdef USE_OPENGL_RENDERER 257 android::uirenderer::Caches::getInstance().textureCache.remove(bitmap); 258#endif 259 delete bitmap; 260} 261 262static void Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) { 263 bitmap->setPixels(NULL, NULL); 264} 265 266// These must match the int values in Bitmap.java 267enum JavaEncodeFormat { 268 kJPEG_JavaEncodeFormat = 0, 269 kPNG_JavaEncodeFormat = 1 270}; 271 272static bool Bitmap_compress(JNIEnv* env, jobject clazz, SkBitmap* bitmap, 273 int format, int quality, 274 jobject jstream, jbyteArray jstorage) { 275 SkImageEncoder::Type fm; 276 277 switch (format) { 278 case kJPEG_JavaEncodeFormat: 279 fm = SkImageEncoder::kJPEG_Type; 280 break; 281 case kPNG_JavaEncodeFormat: 282 fm = SkImageEncoder::kPNG_Type; 283 break; 284 default: 285 return false; 286 } 287 288 bool success = false; 289 SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage); 290 if (NULL != strm) { 291 SkImageEncoder* encoder = SkImageEncoder::Create(fm); 292 if (NULL != encoder) { 293 success = encoder->encodeStream(strm, *bitmap, quality); 294 delete encoder; 295 } 296 delete strm; 297 } 298 return success; 299} 300 301static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) { 302 bitmap->eraseColor(color); 303} 304 305static int Bitmap_width(JNIEnv* env, jobject, SkBitmap* bitmap) { 306 return bitmap->width(); 307} 308 309static int Bitmap_height(JNIEnv* env, jobject, SkBitmap* bitmap) { 310 return bitmap->height(); 311} 312 313static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) { 314 return bitmap->rowBytes(); 315} 316 317static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) { 318 return bitmap->config(); 319} 320 321static int Bitmap_getGenerationId(JNIEnv* env, jobject, SkBitmap* bitmap) { 322 return bitmap->getGenerationID(); 323} 324 325static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) { 326 return !bitmap->isOpaque(); 327} 328 329static void Bitmap_setHasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap, 330 jboolean hasAlpha) { 331 bitmap->setIsOpaque(!hasAlpha); 332} 333 334/////////////////////////////////////////////////////////////////////////////// 335 336static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) { 337 if (parcel == NULL) { 338 SkDebugf("-------- unparcel parcel is NULL\n"); 339 return NULL; 340 } 341 342 android::Parcel* p = android::parcelForJavaObject(env, parcel); 343 344 const bool isMutable = p->readInt32() != 0; 345 const SkBitmap::Config config = (SkBitmap::Config)p->readInt32(); 346 const int width = p->readInt32(); 347 const int height = p->readInt32(); 348 const int rowBytes = p->readInt32(); 349 const int density = p->readInt32(); 350 351 if (SkBitmap::kARGB_8888_Config != config && 352 SkBitmap::kRGB_565_Config != config && 353 SkBitmap::kARGB_4444_Config != config && 354 SkBitmap::kIndex8_Config != config && 355 SkBitmap::kA8_Config != config) { 356 SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config); 357 return NULL; 358 } 359 360 SkBitmap* bitmap = new SkBitmap; 361 362 bitmap->setConfig(config, width, height, rowBytes); 363 364 SkColorTable* ctable = NULL; 365 if (config == SkBitmap::kIndex8_Config) { 366 int count = p->readInt32(); 367 if (count > 0) { 368 size_t size = count * sizeof(SkPMColor); 369 const SkPMColor* src = (const SkPMColor*)p->readInplace(size); 370 ctable = new SkColorTable(src, count); 371 } 372 } 373 374 if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable, true)) { 375 ctable->safeUnref(); 376 delete bitmap; 377 return NULL; 378 } 379 380 ctable->safeUnref(); 381 382 size_t size = bitmap->getSize(); 383 bitmap->lockPixels(); 384 memcpy(bitmap->getPixels(), p->readInplace(size), size); 385 bitmap->unlockPixels(); 386 387 return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL, density); 388} 389 390static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject, 391 const SkBitmap* bitmap, 392 jboolean isMutable, jint density, 393 jobject parcel) { 394 if (parcel == NULL) { 395 SkDebugf("------- writeToParcel null parcel\n"); 396 return false; 397 } 398 399 android::Parcel* p = android::parcelForJavaObject(env, parcel); 400 401 p->writeInt32(isMutable); 402 p->writeInt32(bitmap->config()); 403 p->writeInt32(bitmap->width()); 404 p->writeInt32(bitmap->height()); 405 p->writeInt32(bitmap->rowBytes()); 406 p->writeInt32(density); 407 408 if (bitmap->getConfig() == SkBitmap::kIndex8_Config) { 409 SkColorTable* ctable = bitmap->getColorTable(); 410 if (ctable != NULL) { 411 int count = ctable->count(); 412 p->writeInt32(count); 413 memcpy(p->writeInplace(count * sizeof(SkPMColor)), 414 ctable->lockColors(), count * sizeof(SkPMColor)); 415 ctable->unlockColors(false); 416 } else { 417 p->writeInt32(0); // indicate no ctable 418 } 419 } 420 421 size_t size = bitmap->getSize(); 422 bitmap->lockPixels(); 423 memcpy(p->writeInplace(size), bitmap->getPixels(), size); 424 bitmap->unlockPixels(); 425 return true; 426} 427 428static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz, 429 const SkBitmap* src, const SkPaint* paint, 430 jintArray offsetXY) { 431 SkIPoint offset; 432 SkBitmap* dst = new SkBitmap; 433 434 src->extractAlpha(dst, paint, &offset); 435 if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) { 436 int* array = env->GetIntArrayElements(offsetXY, NULL); 437 array[0] = offset.fX; 438 array[1] = offset.fY; 439 env->ReleaseIntArrayElements(offsetXY, array, 0); 440 } 441 442 return GraphicsJNI::createBitmap(env, dst, true, NULL); 443} 444 445/////////////////////////////////////////////////////////////////////////////// 446 447static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap, 448 int x, int y) { 449 SkAutoLockPixels alp(*bitmap); 450 451 ToColorProc proc = ChooseToColorProc(*bitmap); 452 if (NULL == proc) { 453 return 0; 454 } 455 const void* src = bitmap->getAddr(x, y); 456 if (NULL == src) { 457 return 0; 458 } 459 460 SkColor dst[1]; 461 proc(dst, src, 1, bitmap->getColorTable()); 462 return dst[0]; 463} 464 465static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap, 466 jintArray pixelArray, int offset, int stride, 467 int x, int y, int width, int height) { 468 SkAutoLockPixels alp(*bitmap); 469 470 ToColorProc proc = ChooseToColorProc(*bitmap); 471 if (NULL == proc) { 472 return; 473 } 474 const void* src = bitmap->getAddr(x, y); 475 if (NULL == src) { 476 return; 477 } 478 479 SkColorTable* ctable = bitmap->getColorTable(); 480 jint* dst = env->GetIntArrayElements(pixelArray, NULL); 481 SkColor* d = (SkColor*)dst + offset; 482 while (--height >= 0) { 483 proc(d, src, width, ctable); 484 d += stride; 485 src = (void*)((const char*)src + bitmap->rowBytes()); 486 } 487 env->ReleaseIntArrayElements(pixelArray, dst, 0); 488} 489 490/////////////////////////////////////////////////////////////////////////////// 491 492static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap, 493 int x, int y, SkColor color) { 494 SkAutoLockPixels alp(*bitmap); 495 if (NULL == bitmap->getPixels()) { 496 return; 497 } 498 499 FromColorProc proc = ChooseFromColorProc(bitmap->config()); 500 if (NULL == proc) { 501 return; 502 } 503 504 proc(bitmap->getAddr(x, y), &color, 1, x, y); 505} 506 507static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap, 508 jintArray pixelArray, int offset, int stride, 509 int x, int y, int width, int height) { 510 GraphicsJNI::SetPixels(env, pixelArray, offset, stride, 511 x, y, width, height, *bitmap); 512} 513 514static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject, 515 const SkBitmap* bitmap, jobject jbuffer) { 516 SkAutoLockPixels alp(*bitmap); 517 const void* src = bitmap->getPixels(); 518 519 if (NULL != src) { 520 android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE); 521 522 // the java side has already checked that buffer is large enough 523 memcpy(abp.pointer(), src, bitmap->getSize()); 524 } 525} 526 527static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject, 528 const SkBitmap* bitmap, jobject jbuffer) { 529 SkAutoLockPixels alp(*bitmap); 530 void* dst = bitmap->getPixels(); 531 532 if (NULL != dst) { 533 android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE); 534 // the java side has already checked that buffer is large enough 535 memcpy(dst, abp.pointer(), bitmap->getSize()); 536 } 537} 538 539static bool Bitmap_sameAs(JNIEnv* env, jobject, const SkBitmap* bm0, 540 const SkBitmap* bm1) { 541 if (bm0->width() != bm1->width() || 542 bm0->height() != bm1->height() || 543 bm0->config() != bm1->config()) { 544 return false; 545 } 546 547 SkAutoLockPixels alp0(*bm0); 548 SkAutoLockPixels alp1(*bm1); 549 550 // if we can't load the pixels, return false 551 if (NULL == bm0->getPixels() || NULL == bm1->getPixels()) { 552 return false; 553 } 554 555 if (bm0->config() == SkBitmap::kIndex8_Config) { 556 SkColorTable* ct0 = bm0->getColorTable(); 557 SkColorTable* ct1 = bm1->getColorTable(); 558 if (NULL == ct0 || NULL == ct1) { 559 return false; 560 } 561 if (ct0->count() != ct1->count()) { 562 return false; 563 } 564 565 SkAutoLockColors alc0(ct0); 566 SkAutoLockColors alc1(ct1); 567 const size_t size = ct0->count() * sizeof(SkPMColor); 568 if (memcmp(alc0.colors(), alc1.colors(), size) != 0) { 569 return false; 570 } 571 } 572 573 // now compare each scanline. We can't do the entire buffer at once, 574 // since we don't care about the pixel values that might extend beyond 575 // the width (since the scanline might be larger than the logical width) 576 const int h = bm0->height(); 577 const size_t size = bm0->width() * bm0->bytesPerPixel(); 578 for (int y = 0; y < h; y++) { 579 if (memcmp(bm0->getAddr(0, y), bm1->getAddr(0, y), size) != 0) { 580 return false; 581 } 582 } 583 return true; 584} 585 586static void Bitmap_prepareToDraw(JNIEnv* env, jobject, SkBitmap* bitmap) { 587 bitmap->lockPixels(); 588 bitmap->unlockPixels(); 589} 590 591/////////////////////////////////////////////////////////////////////////////// 592 593#include <android_runtime/AndroidRuntime.h> 594 595static JNINativeMethod gBitmapMethods[] = { 596 { "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;", 597 (void*)Bitmap_creator }, 598 { "nativeCopy", "(IIZ)Landroid/graphics/Bitmap;", 599 (void*)Bitmap_copy }, 600 { "nativeDestructor", "(I)V", (void*)Bitmap_destructor }, 601 { "nativeRecycle", "(I)V", (void*)Bitmap_recycle }, 602 { "nativeCompress", "(IIILjava/io/OutputStream;[B)Z", 603 (void*)Bitmap_compress }, 604 { "nativeErase", "(II)V", (void*)Bitmap_erase }, 605 { "nativeWidth", "(I)I", (void*)Bitmap_width }, 606 { "nativeHeight", "(I)I", (void*)Bitmap_height }, 607 { "nativeRowBytes", "(I)I", (void*)Bitmap_rowBytes }, 608 { "nativeConfig", "(I)I", (void*)Bitmap_config }, 609 { "nativeHasAlpha", "(I)Z", (void*)Bitmap_hasAlpha }, 610 { "nativeSetHasAlpha", "(IZ)V", (void*)Bitmap_setHasAlpha }, 611 { "nativeCreateFromParcel", 612 "(Landroid/os/Parcel;)Landroid/graphics/Bitmap;", 613 (void*)Bitmap_createFromParcel }, 614 { "nativeWriteToParcel", "(IZILandroid/os/Parcel;)Z", 615 (void*)Bitmap_writeToParcel }, 616 { "nativeExtractAlpha", "(II[I)Landroid/graphics/Bitmap;", 617 (void*)Bitmap_extractAlpha }, 618 { "nativeGenerationId", "(I)I", (void*)Bitmap_getGenerationId }, 619 { "nativeGetPixel", "(III)I", (void*)Bitmap_getPixel }, 620 { "nativeGetPixels", "(I[IIIIIII)V", (void*)Bitmap_getPixels }, 621 { "nativeSetPixel", "(IIII)V", (void*)Bitmap_setPixel }, 622 { "nativeSetPixels", "(I[IIIIIII)V", (void*)Bitmap_setPixels }, 623 { "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V", 624 (void*)Bitmap_copyPixelsToBuffer }, 625 { "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V", 626 (void*)Bitmap_copyPixelsFromBuffer }, 627 { "nativeSameAs", "(II)Z", (void*)Bitmap_sameAs }, 628 { "nativePrepareToDraw", "(I)V", (void*)Bitmap_prepareToDraw }, 629}; 630 631#define kClassPathName "android/graphics/Bitmap" 632 633int register_android_graphics_Bitmap(JNIEnv* env); 634int register_android_graphics_Bitmap(JNIEnv* env) 635{ 636 return android::AndroidRuntime::registerNativeMethods(env, kClassPathName, 637 gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods)); 638} 639 640