blob: 65f44d5042576a55c0d4bd05ec839a4b9a177ddd [file] [log] [blame]
#include "SkBitmap.h"
#include "SkImageEncoder.h"
#include "SkColorPriv.h"
#include "GraphicsJNI.h"
#include "SkDither.h"
#include "SkUnPreMultiply.h"
#include "Parcel.h"
#include "android_util_Binder.h"
#include "android_nio_utils.h"
#include "CreateJavaOutputStreamAdaptor.h"
#include <jni.h>
#if 0
#define TRACE_BITMAP(code) code
#else
#define TRACE_BITMAP(code)
#endif
///////////////////////////////////////////////////////////////////////////////
// Conversions to/from SkColor, for get/setPixels, and the create method, which
// is basically like setPixels
typedef void (*FromColorProc)(void* dst, const SkColor src[], int width,
int x, int y);
static void FromColor_D32(void* dst, const SkColor src[], int width,
int, int) {
SkPMColor* d = (SkPMColor*)dst;
for (int i = 0; i < width; i++) {
*d++ = SkPreMultiplyColor(*src++);
}
}
static void FromColor_D565(void* dst, const SkColor src[], int width,
int x, int y) {
uint16_t* d = (uint16_t*)dst;
DITHER_565_SCAN(y);
for (int stop = x + width; x < stop; x++) {
SkColor c = *src++;
*d++ = SkDitherRGBTo565(SkColorGetR(c), SkColorGetG(c), SkColorGetB(c),
DITHER_VALUE(x));
}
}
static void FromColor_D4444(void* dst, const SkColor src[], int width,
int x, int y) {
SkPMColor16* d = (SkPMColor16*)dst;
DITHER_4444_SCAN(y);
for (int stop = x + width; x < stop; x++) {
SkPMColor c = SkPreMultiplyColor(*src++);
*d++ = SkDitherARGB32To4444(c, DITHER_VALUE(x));
// *d++ = SkPixel32ToPixel4444(c);
}
}
// can return NULL
static FromColorProc ChooseFromColorProc(SkBitmap::Config config) {
switch (config) {
case SkBitmap::kARGB_8888_Config:
return FromColor_D32;
case SkBitmap::kARGB_4444_Config:
return FromColor_D4444;
case SkBitmap::kRGB_565_Config:
return FromColor_D565;
default:
break;
}
return NULL;
}
bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors,
int srcOffset, int srcStride,
int x, int y, int width, int height,
const SkBitmap& dstBitmap) {
SkAutoLockPixels alp(dstBitmap);
void* dst = dstBitmap.getPixels();
FromColorProc proc = ChooseFromColorProc(dstBitmap.config());
if (NULL == dst || NULL == proc) {
return false;
}
const jint* array = env->GetIntArrayElements(srcColors, NULL);
const SkColor* src = (const SkColor*)array + srcOffset;
// reset to to actual choice from caller
dst = dstBitmap.getAddr(x, y);
// now copy/convert each scanline
for (int y = 0; y < height; y++) {
proc(dst, src, width, x, y);
src += srcStride;
dst = (char*)dst + dstBitmap.rowBytes();
}
env->ReleaseIntArrayElements(srcColors, const_cast<jint*>(array),
JNI_ABORT);
return true;
}
//////////////////// ToColor procs
typedef void (*ToColorProc)(SkColor dst[], const void* src, int width,
SkColorTable*);
static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
SkColorTable*) {
SkASSERT(width > 0);
const SkPMColor* s = (const SkPMColor*)src;
do {
*dst++ = SkUnPreMultiply::PMColorToColor(*s++);
} while (--width != 0);
}
static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
SkColorTable*) {
SkASSERT(width > 0);
const SkPMColor* s = (const SkPMColor*)src;
do {
SkPMColor c = *s++;
*dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
SkGetPackedB32(c));
} while (--width != 0);
}
static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
SkColorTable*) {
SkASSERT(width > 0);
const SkPMColor16* s = (const SkPMColor16*)src;
do {
*dst++ = SkUnPreMultiply::PMColorToColor(SkPixel4444ToPixel32(*s++));
} while (--width != 0);
}
static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
SkColorTable*) {
SkASSERT(width > 0);
const SkPMColor* s = (const SkPMColor*)src;
do {
SkPMColor c = SkPixel4444ToPixel32(*s++);
*dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
SkGetPackedB32(c));
} while (--width != 0);
}
static void ToColor_S565(SkColor dst[], const void* src, int width,
SkColorTable*) {
SkASSERT(width > 0);
const uint16_t* s = (const uint16_t*)src;
do {
uint16_t c = *s++;
*dst++ = SkColorSetRGB(SkPacked16ToR32(c), SkPacked16ToG32(c),
SkPacked16ToB32(c));
} while (--width != 0);
}
static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
SkColorTable* ctable) {
SkASSERT(width > 0);
const uint8_t* s = (const uint8_t*)src;
const SkPMColor* colors = ctable->lockColors();
do {
*dst++ = SkUnPreMultiply::PMColorToColor(colors[*s++]);
} while (--width != 0);
ctable->unlockColors(false);
}
static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
SkColorTable* ctable) {
SkASSERT(width > 0);
const uint8_t* s = (const uint8_t*)src;
const SkPMColor* colors = ctable->lockColors();
do {
SkPMColor c = colors[*s++];
*dst++ = SkColorSetRGB(SkGetPackedR32(c), SkGetPackedG32(c),
SkGetPackedB32(c));
} while (--width != 0);
ctable->unlockColors(false);
}
// can return NULL
static ToColorProc ChooseToColorProc(const SkBitmap& src) {
switch (src.config()) {
case SkBitmap::kARGB_8888_Config:
return src.isOpaque() ? ToColor_S32_Opaque : ToColor_S32_Alpha;
case SkBitmap::kARGB_4444_Config:
return src.isOpaque() ? ToColor_S4444_Opaque : ToColor_S4444_Alpha;
case SkBitmap::kRGB_565_Config:
return ToColor_S565;
case SkBitmap::kIndex8_Config:
if (src.getColorTable() == NULL) {
return NULL;
}
return src.isOpaque() ? ToColor_SI8_Opaque : ToColor_SI8_Alpha;
default:
break;
}
return NULL;
}
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
int offset, int stride, int width, int height,
SkBitmap::Config config, jboolean isMutable) {
if (width <= 0 || height <= 0) {
doThrowIAE(env, "width and height must be > 0");
return NULL;
}
if (NULL != jColors) {
size_t n = env->GetArrayLength(jColors);
if (n < SkAbs32(stride) * (size_t)height) {
doThrowAIOOBE(env);
return NULL;
}
}
SkBitmap bitmap;
bitmap.setConfig(config, width, height);
if (!GraphicsJNI::setJavaPixelRef(env, &bitmap, NULL)) {
return NULL;
}
if (jColors != NULL) {
GraphicsJNI::SetPixels(env, jColors, offset, stride,
0, 0, width, height, bitmap);
}
return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), isMutable,
NULL);
}
static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
SkBitmap::Config dstConfig, jboolean isMutable) {
SkBitmap result;
JavaPixelAllocator allocator(env);
if (!src->copyTo(&result, dstConfig, &allocator)) {
return NULL;
}
return GraphicsJNI::createBitmap(env, new SkBitmap(result), isMutable,
NULL);
}
static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
delete bitmap;
}
static void Bitmap_recycle(JNIEnv* env, jobject, SkBitmap* bitmap) {
bitmap->setPixels(NULL, NULL);
}
// These must match the int values in Bitmap.java
enum JavaEncodeFormat {
kJPEG_JavaEncodeFormat = 0,
kPNG_JavaEncodeFormat = 1
};
static bool Bitmap_compress(JNIEnv* env, jobject clazz, SkBitmap* bitmap,
int format, int quality,
jobject jstream, jbyteArray jstorage) {
SkImageEncoder::Type fm;
switch (format) {
case kJPEG_JavaEncodeFormat:
fm = SkImageEncoder::kJPEG_Type;
break;
case kPNG_JavaEncodeFormat:
fm = SkImageEncoder::kPNG_Type;
break;
default:
return false;
}
bool success = false;
SkWStream* strm = CreateJavaOutputStreamAdaptor(env, jstream, jstorage);
if (NULL != strm) {
SkImageEncoder* encoder = SkImageEncoder::Create(fm);
if (NULL != encoder) {
success = encoder->encodeStream(strm, *bitmap, quality);
delete encoder;
}
delete strm;
}
return success;
}
static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) {
bitmap->eraseColor(color);
}
static int Bitmap_width(JNIEnv* env, jobject, SkBitmap* bitmap) {
return bitmap->width();
}
static int Bitmap_height(JNIEnv* env, jobject, SkBitmap* bitmap) {
return bitmap->height();
}
static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {
return bitmap->rowBytes();
}
static int Bitmap_config(JNIEnv* env, jobject, SkBitmap* bitmap) {
return bitmap->config();
}
static jboolean Bitmap_hasAlpha(JNIEnv* env, jobject, SkBitmap* bitmap) {
return !bitmap->isOpaque();
}
///////////////////////////////////////////////////////////////////////////////
static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
if (parcel == NULL) {
SkDebugf("-------- unparcel parcel is NULL\n");
return NULL;
}
android::Parcel* p = android::parcelForJavaObject(env, parcel);
const bool isMutable = p->readInt32() != 0;
const SkBitmap::Config config = (SkBitmap::Config)p->readInt32();
const int width = p->readInt32();
const int height = p->readInt32();
const int rowBytes = p->readInt32();
if (SkBitmap::kARGB_8888_Config != config &&
SkBitmap::kRGB_565_Config != config &&
SkBitmap::kARGB_4444_Config != config &&
SkBitmap::kIndex8_Config != config &&
SkBitmap::kA8_Config != config) {
SkDebugf("Bitmap_createFromParcel unknown config: %d\n", config);
return NULL;
}
SkBitmap* bitmap = new SkBitmap;
bitmap->setConfig(config, width, height, rowBytes);
SkColorTable* ctable = NULL;
if (config == SkBitmap::kIndex8_Config) {
int count = p->readInt32();
if (count > 0) {
size_t size = count * sizeof(SkPMColor);
const SkPMColor* src = (const SkPMColor*)p->readInplace(size);
ctable = new SkColorTable(src, count);
}
}
if (!GraphicsJNI::setJavaPixelRef(env, bitmap, ctable)) {
ctable->safeUnref();
delete bitmap;
return NULL;
}
ctable->safeUnref();
size_t size = bitmap->getSize();
bitmap->lockPixels();
memcpy(bitmap->getPixels(), p->readInplace(size), size);
bitmap->unlockPixels();
return GraphicsJNI::createBitmap(env, bitmap, isMutable, NULL);
}
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
const SkBitmap* bitmap,
jboolean isMutable, jobject parcel) {
if (parcel == NULL) {
SkDebugf("------- writeToParcel null parcel\n");
return false;
}
android::Parcel* p = android::parcelForJavaObject(env, parcel);
p->writeInt32(isMutable);
p->writeInt32(bitmap->config());
p->writeInt32(bitmap->width());
p->writeInt32(bitmap->height());
p->writeInt32(bitmap->rowBytes());
if (bitmap->getConfig() == SkBitmap::kIndex8_Config) {
SkColorTable* ctable = bitmap->getColorTable();
if (ctable != NULL) {
int count = ctable->count();
p->writeInt32(count);
memcpy(p->writeInplace(count * sizeof(SkPMColor)),
ctable->lockColors(), count * sizeof(SkPMColor));
ctable->unlockColors(false);
} else {
p->writeInt32(0); // indicate no ctable
}
}
size_t size = bitmap->getSize();
bitmap->lockPixels();
memcpy(p->writeInplace(size), bitmap->getPixels(), size);
bitmap->unlockPixels();
return true;
}
static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
const SkBitmap* src, const SkPaint* paint,
jintArray offsetXY) {
SkIPoint offset;
SkBitmap* dst = new SkBitmap;
src->extractAlpha(dst, paint, &offset);
if (offsetXY != 0 && env->GetArrayLength(offsetXY) >= 2) {
int* array = env->GetIntArrayElements(offsetXY, NULL);
array[0] = offset.fX;
array[1] = offset.fY;
env->ReleaseIntArrayElements(offsetXY, array, 0);
}
return GraphicsJNI::createBitmap(env, dst, true, NULL);
}
///////////////////////////////////////////////////////////////////////////////
static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
int x, int y) {
SkAutoLockPixels alp(*bitmap);
ToColorProc proc = ChooseToColorProc(*bitmap);
if (NULL == proc) {
return 0;
}
const void* src = bitmap->getAddr(x, y);
if (NULL == src) {
return 0;
}
SkColor dst[1];
proc(dst, src, 1, bitmap->getColorTable());
return dst[0];
}
static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
jintArray pixelArray, int offset, int stride,
int x, int y, int width, int height) {
SkAutoLockPixels alp(*bitmap);
ToColorProc proc = ChooseToColorProc(*bitmap);
if (NULL == proc) {
return;
}
const void* src = bitmap->getAddr(x, y);
if (NULL == src) {
return;
}
SkColorTable* ctable = bitmap->getColorTable();
jint* dst = env->GetIntArrayElements(pixelArray, NULL);
SkColor* d = (SkColor*)dst + offset;
while (--height >= 0) {
proc(d, src, width, ctable);
d += stride;
src = (void*)((const char*)src + bitmap->rowBytes());
}
env->ReleaseIntArrayElements(pixelArray, dst, 0);
}
///////////////////////////////////////////////////////////////////////////////
static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
int x, int y, SkColor color) {
SkAutoLockPixels alp(*bitmap);
if (NULL == bitmap->getPixels()) {
return;
}
FromColorProc proc = ChooseFromColorProc(bitmap->config());
if (NULL == proc) {
return;
}
proc(bitmap->getAddr(x, y), &color, 1, x, y);
}
static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
jintArray pixelArray, int offset, int stride,
int x, int y, int width, int height) {
GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
x, y, width, height, *bitmap);
}
static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
const SkBitmap* bitmap, jobject jbuffer) {
SkAutoLockPixels alp(*bitmap);
const void* src = bitmap->getPixels();
if (NULL != src) {
android::AutoBufferPointer abp(env, jbuffer, JNI_TRUE);
// the java side has already checked that buffer is large enough
memcpy(abp.pointer(), src, bitmap->getSize());
}
}
static void Bitmap_copyPixelsFromBuffer(JNIEnv* env, jobject,
const SkBitmap* bitmap, jobject jbuffer) {
SkAutoLockPixels alp(*bitmap);
void* dst = bitmap->getPixels();
if (NULL != dst) {
android::AutoBufferPointer abp(env, jbuffer, JNI_FALSE);
// the java side has already checked that buffer is large enough
memcpy(dst, abp.pointer(), bitmap->getSize());
}
}
///////////////////////////////////////////////////////////////////////////////
#include <android_runtime/AndroidRuntime.h>
static JNINativeMethod gBitmapMethods[] = {
{ "nativeCreate", "([IIIIIIZ)Landroid/graphics/Bitmap;",
(void*)Bitmap_creator },
{ "nativeCopy", "(IIZ)Landroid/graphics/Bitmap;",
(void*)Bitmap_copy },
{ "nativeDestructor", "(I)V", (void*)Bitmap_destructor },
{ "nativeRecycle", "(I)V", (void*)Bitmap_recycle },
{ "nativeCompress", "(IIILjava/io/OutputStream;[B)Z",
(void*)Bitmap_compress },
{ "nativeErase", "(II)V", (void*)Bitmap_erase },
{ "nativeWidth", "(I)I", (void*)Bitmap_width },
{ "nativeHeight", "(I)I", (void*)Bitmap_height },
{ "nativeRowBytes", "(I)I", (void*)Bitmap_rowBytes },
{ "nativeConfig", "(I)I", (void*)Bitmap_config },
{ "nativeHasAlpha", "(I)Z", (void*)Bitmap_hasAlpha },
{ "nativeCreateFromParcel",
"(Landroid/os/Parcel;)Landroid/graphics/Bitmap;",
(void*)Bitmap_createFromParcel },
{ "nativeWriteToParcel", "(IZLandroid/os/Parcel;)Z",
(void*)Bitmap_writeToParcel },
{ "nativeExtractAlpha", "(II[I)Landroid/graphics/Bitmap;",
(void*)Bitmap_extractAlpha },
{ "nativeGetPixel", "(III)I", (void*)Bitmap_getPixel },
{ "nativeGetPixels", "(I[IIIIIII)V", (void*)Bitmap_getPixels },
{ "nativeSetPixel", "(IIII)V", (void*)Bitmap_setPixel },
{ "nativeSetPixels", "(I[IIIIIII)V", (void*)Bitmap_setPixels },
{ "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",
(void*)Bitmap_copyPixelsToBuffer },
{ "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",
(void*)Bitmap_copyPixelsFromBuffer }
};
#define kClassPathName "android/graphics/Bitmap"
int register_android_graphics_Bitmap(JNIEnv* env);
int register_android_graphics_Bitmap(JNIEnv* env)
{
return android::AndroidRuntime::registerNativeMethods(env, kClassPathName,
gBitmapMethods, SK_ARRAY_COUNT(gBitmapMethods));
}