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