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