Add inPremutiplied option to BitmapFactory.Options, functionality in Bitmap
bug:2248948 Change-Id: I8fdd649332667598504a1076d5a447572bd53086
This commit is contained in:
@@ -8831,6 +8831,7 @@ package android.graphics {
|
||||
method public void setHeight(int);
|
||||
method public void setPixel(int, int, int);
|
||||
method public void setPixels(int[], int, int, int, int, int, int);
|
||||
method public final void setPremultiplied(boolean);
|
||||
method public void setWidth(int);
|
||||
method public void writeToParcel(android.os.Parcel, int);
|
||||
field public static final android.os.Parcelable.Creator CREATOR;
|
||||
@@ -8880,6 +8881,7 @@ package android.graphics {
|
||||
field public boolean inMutable;
|
||||
field public boolean inPreferQualityOverSpeed;
|
||||
field public android.graphics.Bitmap.Config inPreferredConfig;
|
||||
field public boolean inPremultiplied;
|
||||
field public boolean inPurgeable;
|
||||
field public int inSampleSize;
|
||||
field public boolean inScaled;
|
||||
|
||||
@@ -776,7 +776,7 @@ class GLES20Canvas extends HardwareCanvas {
|
||||
@Override
|
||||
public void drawPatch(NinePatch patch, Rect dst, Paint paint) {
|
||||
Bitmap bitmap = patch.getBitmap();
|
||||
if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
|
||||
throwIfCannotDraw(bitmap);
|
||||
// Shaders are ignored when drawing patches
|
||||
int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
|
||||
try {
|
||||
@@ -791,7 +791,7 @@ class GLES20Canvas extends HardwareCanvas {
|
||||
@Override
|
||||
public void drawPatch(NinePatch patch, RectF dst, Paint paint) {
|
||||
Bitmap bitmap = patch.getBitmap();
|
||||
if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
|
||||
throwIfCannotDraw(bitmap);
|
||||
// Shaders are ignored when drawing patches
|
||||
int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE;
|
||||
try {
|
||||
@@ -808,7 +808,7 @@ class GLES20Canvas extends HardwareCanvas {
|
||||
|
||||
@Override
|
||||
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
|
||||
if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
|
||||
throwIfCannotDraw(bitmap);
|
||||
// Shaders are ignored when drawing bitmaps
|
||||
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
|
||||
try {
|
||||
@@ -824,7 +824,7 @@ class GLES20Canvas extends HardwareCanvas {
|
||||
|
||||
@Override
|
||||
public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
|
||||
if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
|
||||
throwIfCannotDraw(bitmap);
|
||||
// Shaders are ignored when drawing bitmaps
|
||||
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
|
||||
try {
|
||||
@@ -841,7 +841,7 @@ class GLES20Canvas extends HardwareCanvas {
|
||||
|
||||
@Override
|
||||
public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
|
||||
if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
|
||||
throwIfCannotDraw(bitmap);
|
||||
// Shaders are ignored when drawing bitmaps
|
||||
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
|
||||
try {
|
||||
@@ -868,7 +868,7 @@ class GLES20Canvas extends HardwareCanvas {
|
||||
|
||||
@Override
|
||||
public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
|
||||
if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
|
||||
throwIfCannotDraw(bitmap);
|
||||
// Shaders are ignored when drawing bitmaps
|
||||
int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE;
|
||||
try {
|
||||
@@ -944,7 +944,7 @@ class GLES20Canvas extends HardwareCanvas {
|
||||
@Override
|
||||
public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts,
|
||||
int vertOffset, int[] colors, int colorOffset, Paint paint) {
|
||||
if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps");
|
||||
throwIfCannotDraw(bitmap);
|
||||
if (meshWidth < 0 || meshHeight < 0 || vertOffset < 0 || colorOffset < 0) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
@@ -38,6 +38,23 @@ static void FromColor_D32(void* dst, const SkColor src[], int width,
|
||||
}
|
||||
}
|
||||
|
||||
static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,
|
||||
int, int) {
|
||||
// SkColor's ordering may be different from SkPMColor
|
||||
if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) {
|
||||
memcpy(dst, src, width * sizeof(SkColor));
|
||||
return;
|
||||
}
|
||||
|
||||
// order isn't same, repack each pixel manually
|
||||
SkPMColor* d = (SkPMColor*)dst;
|
||||
for (int i = 0; i < width; i++) {
|
||||
SkColor c = *src++;
|
||||
*d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
|
||||
SkColorGetG(c), SkColorGetB(c));
|
||||
}
|
||||
}
|
||||
|
||||
static void FromColor_D565(void* dst, const SkColor src[], int width,
|
||||
int x, int y) {
|
||||
uint16_t* d = (uint16_t*)dst;
|
||||
@@ -56,19 +73,35 @@ static void FromColor_D4444(void* dst, const SkColor src[], int width,
|
||||
|
||||
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);
|
||||
SkPMColor pmc = SkPreMultiplyColor(*src++);
|
||||
*d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
|
||||
// *d++ = SkPixel32ToPixel4444(pmc);
|
||||
}
|
||||
}
|
||||
|
||||
static void FromColor_D4444_Raw(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++) {
|
||||
SkColor c = *src++;
|
||||
|
||||
// SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied
|
||||
SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),
|
||||
SkColorGetG(c), SkColorGetB(c));
|
||||
*d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));
|
||||
// *d++ = SkPixel32ToPixel4444(pmc);
|
||||
}
|
||||
}
|
||||
|
||||
// can return NULL
|
||||
static FromColorProc ChooseFromColorProc(SkBitmap::Config config) {
|
||||
static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {
|
||||
switch (config) {
|
||||
case SkBitmap::kARGB_8888_Config:
|
||||
return FromColor_D32;
|
||||
return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;
|
||||
case SkBitmap::kARGB_4444_Config:
|
||||
return FromColor_D4444;
|
||||
return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;
|
||||
case SkBitmap::kRGB_565_Config:
|
||||
return FromColor_D565;
|
||||
default:
|
||||
@@ -77,13 +110,12 @@ static FromColorProc ChooseFromColorProc(SkBitmap::Config config) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors,
|
||||
int srcOffset, int srcStride,
|
||||
int x, int y, int width, int height,
|
||||
const SkBitmap& dstBitmap) {
|
||||
bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,
|
||||
int x, int y, int width, int height,
|
||||
const SkBitmap& dstBitmap, bool isPremultiplied) {
|
||||
SkAutoLockPixels alp(dstBitmap);
|
||||
void* dst = dstBitmap.getPixels();
|
||||
FromColorProc proc = ChooseFromColorProc(dstBitmap.config());
|
||||
FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);
|
||||
|
||||
if (NULL == dst || NULL == proc) {
|
||||
return false;
|
||||
@@ -122,6 +154,17 @@ static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
|
||||
} while (--width != 0);
|
||||
}
|
||||
|
||||
static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,
|
||||
SkColorTable*) {
|
||||
SkASSERT(width > 0);
|
||||
const SkPMColor* s = (const SkPMColor*)src;
|
||||
do {
|
||||
SkPMColor c = *s++;
|
||||
*dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
|
||||
SkGetPackedG32(c), SkGetPackedB32(c));
|
||||
} while (--width != 0);
|
||||
}
|
||||
|
||||
static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,
|
||||
SkColorTable*) {
|
||||
SkASSERT(width > 0);
|
||||
@@ -142,6 +185,17 @@ static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
|
||||
} while (--width != 0);
|
||||
}
|
||||
|
||||
static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,
|
||||
SkColorTable*) {
|
||||
SkASSERT(width > 0);
|
||||
const SkPMColor16* s = (const SkPMColor16*)src;
|
||||
do {
|
||||
SkPMColor c = SkPixel4444ToPixel32(*s++);
|
||||
*dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
|
||||
SkGetPackedG32(c), SkGetPackedB32(c));
|
||||
} while (--width != 0);
|
||||
}
|
||||
|
||||
static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,
|
||||
SkColorTable*) {
|
||||
SkASSERT(width > 0);
|
||||
@@ -175,6 +229,19 @@ static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
|
||||
ctable->unlockColors(false);
|
||||
}
|
||||
|
||||
static void ToColor_SI8_Raw(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++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),
|
||||
SkGetPackedG32(c), SkGetPackedB32(c));
|
||||
} while (--width != 0);
|
||||
ctable->unlockColors(false);
|
||||
}
|
||||
|
||||
static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
|
||||
SkColorTable* ctable) {
|
||||
SkASSERT(width > 0);
|
||||
@@ -189,19 +256,22 @@ static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
|
||||
}
|
||||
|
||||
// can return NULL
|
||||
static ToColorProc ChooseToColorProc(const SkBitmap& src) {
|
||||
static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {
|
||||
switch (src.config()) {
|
||||
case SkBitmap::kARGB_8888_Config:
|
||||
return src.isOpaque() ? ToColor_S32_Opaque : ToColor_S32_Alpha;
|
||||
if (src.isOpaque()) return ToColor_S32_Opaque;
|
||||
return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;
|
||||
case SkBitmap::kARGB_4444_Config:
|
||||
return src.isOpaque() ? ToColor_S4444_Opaque : ToColor_S4444_Alpha;
|
||||
if (src.isOpaque()) return ToColor_S4444_Opaque;
|
||||
return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;
|
||||
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;
|
||||
if (src.isOpaque()) return ToColor_SI8_Opaque;
|
||||
return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -211,6 +281,12 @@ static ToColorProc ChooseToColorProc(const SkBitmap& src) {
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static int getPremulBitmapCreateFlags(bool isMutable) {
|
||||
int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;
|
||||
if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
|
||||
return flags;
|
||||
}
|
||||
|
||||
static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
|
||||
int offset, int stride, int width, int height,
|
||||
SkBitmap::Config config, jboolean isMutable) {
|
||||
@@ -236,10 +312,12 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
|
||||
}
|
||||
|
||||
if (jColors != NULL) {
|
||||
GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap);
|
||||
GraphicsJNI::SetPixels(env, jColors, offset, stride,
|
||||
0, 0, width, height, bitmap, true);
|
||||
}
|
||||
|
||||
return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL, NULL);
|
||||
return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,
|
||||
getPremulBitmapCreateFlags(isMutable), NULL, NULL);
|
||||
}
|
||||
|
||||
static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
|
||||
@@ -250,8 +328,8 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
|
||||
if (!src->copyTo(&result, dstConfig, &allocator)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL, NULL);
|
||||
return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),
|
||||
getPremulBitmapCreateFlags(isMutable), NULL, NULL);
|
||||
}
|
||||
|
||||
static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {
|
||||
@@ -347,14 +425,6 @@ 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();
|
||||
}
|
||||
@@ -449,7 +519,9 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
|
||||
bitmap->unlockPixels();
|
||||
|
||||
blob.release();
|
||||
return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, NULL, density);
|
||||
|
||||
return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),
|
||||
NULL, NULL, density);
|
||||
}
|
||||
|
||||
static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,
|
||||
@@ -527,16 +599,17 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
|
||||
env->ReleaseIntArrayElements(offsetXY, array, 0);
|
||||
}
|
||||
|
||||
return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL, NULL);
|
||||
return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),
|
||||
GraphicsJNI::kBitmapCreateFlag_Mutable, NULL, NULL);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
|
||||
int x, int y) {
|
||||
int x, int y, bool isPremultiplied) {
|
||||
SkAutoLockPixels alp(*bitmap);
|
||||
|
||||
ToColorProc proc = ChooseToColorProc(*bitmap);
|
||||
ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
|
||||
if (NULL == proc) {
|
||||
return 0;
|
||||
}
|
||||
@@ -551,11 +624,11 @@ static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
|
||||
}
|
||||
|
||||
static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
|
||||
jintArray pixelArray, int offset, int stride,
|
||||
int x, int y, int width, int height) {
|
||||
jintArray pixelArray, int offset, int stride,
|
||||
int x, int y, int width, int height, bool isPremultiplied) {
|
||||
SkAutoLockPixels alp(*bitmap);
|
||||
|
||||
ToColorProc proc = ChooseToColorProc(*bitmap);
|
||||
ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);
|
||||
if (NULL == proc) {
|
||||
return;
|
||||
}
|
||||
@@ -578,13 +651,13 @@ static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
|
||||
int x, int y, SkColor color) {
|
||||
int x, int y, SkColor color, bool isPremultiplied) {
|
||||
SkAutoLockPixels alp(*bitmap);
|
||||
if (NULL == bitmap->getPixels()) {
|
||||
return;
|
||||
}
|
||||
|
||||
FromColorProc proc = ChooseFromColorProc(bitmap->config());
|
||||
FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);
|
||||
if (NULL == proc) {
|
||||
return;
|
||||
}
|
||||
@@ -594,10 +667,10 @@ static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
|
||||
}
|
||||
|
||||
static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
|
||||
jintArray pixelArray, int offset, int stride,
|
||||
int x, int y, int width, int height) {
|
||||
jintArray pixelArray, int offset, int stride,
|
||||
int x, int y, int width, int height, bool isPremultiplied) {
|
||||
GraphicsJNI::SetPixels(env, pixelArray, offset, stride,
|
||||
x, y, width, height, *bitmap);
|
||||
x, y, width, height, *bitmap, isPremultiplied);
|
||||
}
|
||||
|
||||
static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,
|
||||
@@ -693,8 +766,6 @@ static JNINativeMethod gBitmapMethods[] = {
|
||||
{ "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 },
|
||||
@@ -709,10 +780,10 @@ static JNINativeMethod gBitmapMethods[] = {
|
||||
{ "nativeExtractAlpha", "(II[I)Landroid/graphics/Bitmap;",
|
||||
(void*)Bitmap_extractAlpha },
|
||||
{ "nativeGenerationId", "(I)I", (void*)Bitmap_getGenerationId },
|
||||
{ "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 },
|
||||
{ "nativeGetPixel", "(IIIZ)I", (void*)Bitmap_getPixel },
|
||||
{ "nativeGetPixels", "(I[IIIIIIIZ)V", (void*)Bitmap_getPixels },
|
||||
{ "nativeSetPixel", "(IIIIZ)V", (void*)Bitmap_setPixel },
|
||||
{ "nativeSetPixels", "(I[IIIIIIIZ)V", (void*)Bitmap_setPixels },
|
||||
{ "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",
|
||||
(void*)Bitmap_copyPixelsToBuffer },
|
||||
{ "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include "AutoDecodeCancel.h"
|
||||
#include "Utils.h"
|
||||
#include "JNIHelp.h"
|
||||
#include "GraphicsJNI.h"
|
||||
|
||||
#include <android_runtime/AndroidRuntime.h>
|
||||
#include <androidfw/Asset.h>
|
||||
@@ -25,6 +26,7 @@
|
||||
jfieldID gOptions_justBoundsFieldID;
|
||||
jfieldID gOptions_sampleSizeFieldID;
|
||||
jfieldID gOptions_configFieldID;
|
||||
jfieldID gOptions_premultipliedFieldID;
|
||||
jfieldID gOptions_mutableFieldID;
|
||||
jfieldID gOptions_ditherFieldID;
|
||||
jfieldID gOptions_purgeableFieldID;
|
||||
@@ -213,6 +215,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
|
||||
float scale = 1.0f;
|
||||
bool isPurgeable = forcePurgeable || (allowPurgeable && optionsPurgeable(env, options));
|
||||
bool preferQualityOverSpeed = false;
|
||||
bool requireUnpremultiplied = false;
|
||||
|
||||
jobject javaBitmap = NULL;
|
||||
|
||||
@@ -233,6 +236,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
|
||||
doDither = env->GetBooleanField(options, gOptions_ditherFieldID);
|
||||
preferQualityOverSpeed = env->GetBooleanField(options,
|
||||
gOptions_preferQualityOverSpeedFieldID);
|
||||
requireUnpremultiplied = !env->GetBooleanField(options, gOptions_premultipliedFieldID);
|
||||
javaBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
|
||||
|
||||
if (env->GetBooleanField(options, gOptions_scaledFieldID)) {
|
||||
@@ -256,6 +260,7 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
|
||||
decoder->setSampleSize(sampleSize);
|
||||
decoder->setDitherImage(doDither);
|
||||
decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
|
||||
decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
|
||||
|
||||
SkBitmap* outputBitmap = NULL;
|
||||
unsigned int existingBufferSize = 0;
|
||||
@@ -434,14 +439,20 @@ static jobject doDecode(JNIEnv* env, SkStream* stream, jobject padding,
|
||||
adb.detach();
|
||||
|
||||
if (javaBitmap != NULL) {
|
||||
GraphicsJNI::reinitBitmap(env, javaBitmap);
|
||||
bool isPremultiplied = !requireUnpremultiplied;
|
||||
GraphicsJNI::reinitBitmap(env, javaBitmap, outputBitmap, isPremultiplied);
|
||||
outputBitmap->notifyPixelsChanged();
|
||||
// If a java bitmap was passed in for reuse, pass it back
|
||||
return javaBitmap;
|
||||
}
|
||||
|
||||
int bitmapCreateFlags = 0x0;
|
||||
if (isMutable) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Mutable;
|
||||
if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
|
||||
|
||||
// now create the java bitmap
|
||||
return GraphicsJNI::createBitmap(env, outputBitmap, javaAllocator.getStorageObj(),
|
||||
isMutable, ninePatchChunk, layoutBounds, -1);
|
||||
bitmapCreateFlags, ninePatchChunk, layoutBounds, -1);
|
||||
}
|
||||
|
||||
static jobject nativeDecodeStream(JNIEnv* env, jobject clazz, jobject is, jbyteArray storage,
|
||||
@@ -624,6 +635,7 @@ int register_android_graphics_BitmapFactory(JNIEnv* env) {
|
||||
gOptions_sampleSizeFieldID = getFieldIDCheck(env, options_class, "inSampleSize", "I");
|
||||
gOptions_configFieldID = getFieldIDCheck(env, options_class, "inPreferredConfig",
|
||||
"Landroid/graphics/Bitmap$Config;");
|
||||
gOptions_premultipliedFieldID = getFieldIDCheck(env, options_class, "inPremultiplied", "Z");
|
||||
gOptions_mutableFieldID = getFieldIDCheck(env, options_class, "inMutable", "Z");
|
||||
gOptions_ditherFieldID = getFieldIDCheck(env, options_class, "inDither", "Z");
|
||||
gOptions_purgeableFieldID = getFieldIDCheck(env, options_class, "inPurgeable", "Z");
|
||||
|
||||
@@ -7,6 +7,7 @@ extern jclass gOptions_class;
|
||||
extern jfieldID gOptions_justBoundsFieldID;
|
||||
extern jfieldID gOptions_sampleSizeFieldID;
|
||||
extern jfieldID gOptions_configFieldID;
|
||||
extern jfieldID gOptions_premultipliedFieldID;
|
||||
extern jfieldID gOptions_ditherFieldID;
|
||||
extern jfieldID gOptions_purgeableFieldID;
|
||||
extern jfieldID gOptions_shareableFieldID;
|
||||
|
||||
@@ -201,6 +201,7 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *b
|
||||
SkBitmap::Config prefConfig = SkBitmap::kNo_Config;
|
||||
bool doDither = true;
|
||||
bool preferQualityOverSpeed = false;
|
||||
bool requireUnpremultiplied = false;
|
||||
|
||||
if (NULL != options) {
|
||||
sampleSize = env->GetIntField(options, gOptions_sampleSizeFieldID);
|
||||
@@ -216,11 +217,13 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *b
|
||||
gOptions_preferQualityOverSpeedFieldID);
|
||||
// Get the bitmap for re-use if it exists.
|
||||
tileBitmap = env->GetObjectField(options, gOptions_bitmapFieldID);
|
||||
requireUnpremultiplied = env->GetBooleanField(options, gOptions_premultipliedFieldID);
|
||||
}
|
||||
|
||||
decoder->setDitherImage(doDither);
|
||||
decoder->setPreferQualityOverSpeed(preferQualityOverSpeed);
|
||||
AutoDecoderCancel adc(options, decoder);
|
||||
decoder->setRequireUnpremultipliedColors(requireUnpremultiplied);
|
||||
AutoDecoderCancel adc(options, decoder);
|
||||
|
||||
// To fix the race condition in case "requestCancelDecode"
|
||||
// happens earlier than AutoDecoderCancel object is added
|
||||
@@ -270,7 +273,10 @@ static jobject nativeDecodeRegion(JNIEnv* env, jobject, SkBitmapRegionDecoder *b
|
||||
|
||||
JavaPixelAllocator* allocator = (JavaPixelAllocator*) decoder->getAllocator();
|
||||
jbyteArray buff = allocator->getStorageObjAndReset();
|
||||
return GraphicsJNI::createBitmap(env, bitmap, buff, false, NULL, NULL, -1);
|
||||
|
||||
int bitmapCreateFlags = 0;
|
||||
if (!requireUnpremultiplied) bitmapCreateFlags |= GraphicsJNI::kBitmapCreateFlag_Premultiplied;
|
||||
return GraphicsJNI::createBitmap(env, bitmap, buff, bitmapCreateFlags, NULL, NULL, -1);
|
||||
}
|
||||
|
||||
static int nativeGetHeight(JNIEnv* env, jobject, SkBitmapRegionDecoder *brd) {
|
||||
|
||||
@@ -547,18 +547,17 @@ public:
|
||||
jboolean hasAlpha, SkPaint* paint)
|
||||
{
|
||||
SkBitmap bitmap;
|
||||
|
||||
bitmap.setConfig(hasAlpha ? SkBitmap::kARGB_8888_Config :
|
||||
SkBitmap::kRGB_565_Config, width, height);
|
||||
if (!bitmap.allocPixels()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!GraphicsJNI::SetPixels(env, jcolors, offset, stride,
|
||||
0, 0, width, height, bitmap)) {
|
||||
0, 0, width, height, bitmap, true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
canvas->drawBitmap(bitmap, SkFloatToScalar(x), SkFloatToScalar(y),
|
||||
paint);
|
||||
}
|
||||
|
||||
@@ -347,27 +347,32 @@ SkRegion* GraphicsJNI::getNativeRegion(JNIEnv* env, jobject region)
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
|
||||
bool isMutable, jbyteArray ninepatch, jintArray layoutbounds,
|
||||
int density)
|
||||
int bitmapCreateFlags, jbyteArray ninepatch, jintArray layoutbounds, int density)
|
||||
{
|
||||
SkASSERT(bitmap);
|
||||
SkASSERT(bitmap->pixelRef());
|
||||
bool isMutable = bitmapCreateFlags & kBitmapCreateFlag_Mutable;
|
||||
bool isPremultiplied = bitmapCreateFlags & kBitmapCreateFlag_Premultiplied;
|
||||
|
||||
jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
|
||||
static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)),
|
||||
buffer, isMutable, ninepatch, layoutbounds, density);
|
||||
static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)), buffer,
|
||||
bitmap->width(), bitmap->height(), density, isMutable, isPremultiplied,
|
||||
ninepatch, layoutbounds);
|
||||
hasException(env); // For the side effect of logging.
|
||||
return obj;
|
||||
}
|
||||
|
||||
jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
|
||||
jbyteArray ninepatch, int density)
|
||||
jobject GraphicsJNI::createBitmap(JNIEnv* env, SkBitmap* bitmap, int bitmapCreateFlags,
|
||||
jbyteArray ninepatch, int density)
|
||||
{
|
||||
return createBitmap(env, bitmap, NULL, isMutable, ninepatch, NULL, density);
|
||||
return createBitmap(env, bitmap, NULL, bitmapCreateFlags, ninepatch, NULL, density);
|
||||
}
|
||||
|
||||
void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap)
|
||||
void GraphicsJNI::reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap,
|
||||
bool isPremultiplied)
|
||||
{
|
||||
env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID);
|
||||
env->CallVoidMethod(javaBitmap, gBitmap_reinitMethodID,
|
||||
bitmap->width(), bitmap->height(), isPremultiplied);
|
||||
}
|
||||
|
||||
int GraphicsJNI::getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap)
|
||||
@@ -593,9 +598,8 @@ int register_android_graphics_Graphics(JNIEnv* env)
|
||||
|
||||
gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
|
||||
gBitmap_nativeInstanceID = getFieldIDCheck(env, gBitmap_class, "mNativeBitmap", "I");
|
||||
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
|
||||
"(I[BZ[B[II)V");
|
||||
gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "()V");
|
||||
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>", "(I[BIIIZZ[B[I)V");
|
||||
gBitmap_reinitMethodID = env->GetMethodID(gBitmap_class, "reinit", "(IIZ)V");
|
||||
gBitmap_getAllocationByteCountMethodID = env->GetMethodID(gBitmap_class, "getAllocationByteCount", "()I");
|
||||
gBitmapRegionDecoder_class = make_globalref(env, "android/graphics/BitmapRegionDecoder");
|
||||
gBitmapRegionDecoder_constructorMethodID = env->GetMethodID(gBitmapRegionDecoder_class, "<init>", "(I)V");
|
||||
|
||||
@@ -17,6 +17,12 @@ class SkPicture;
|
||||
|
||||
class GraphicsJNI {
|
||||
public:
|
||||
enum BitmapCreateFlags {
|
||||
kBitmapCreateFlag_None = 0x0,
|
||||
kBitmapCreateFlag_Mutable = 0x1,
|
||||
kBitmapCreateFlag_Premultiplied = 0x2,
|
||||
};
|
||||
|
||||
// returns true if an exception is set (and dumps it out to the Log)
|
||||
static bool hasException(JNIEnv*);
|
||||
|
||||
@@ -53,13 +59,13 @@ public:
|
||||
storage array (may be null).
|
||||
*/
|
||||
static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, jbyteArray buffer,
|
||||
bool isMutable, jbyteArray ninepatch, jintArray layoutbounds,
|
||||
int density = -1);
|
||||
int bitmapCreateFlags, jbyteArray ninepatch, jintArray layoutbounds, int density = -1);
|
||||
|
||||
static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, bool isMutable,
|
||||
jbyteArray ninepatch, int density = -1);
|
||||
static jobject createBitmap(JNIEnv* env, SkBitmap* bitmap, int bitmapCreateFlags,
|
||||
jbyteArray ninepatch, int density = -1);
|
||||
|
||||
static void reinitBitmap(JNIEnv* env, jobject javaBitmap);
|
||||
static void reinitBitmap(JNIEnv* env, jobject javaBitmap, SkBitmap* bitmap,
|
||||
bool isPremultiplied);
|
||||
|
||||
static int getBitmapAllocationByteCount(JNIEnv* env, jobject javaBitmap);
|
||||
|
||||
@@ -68,14 +74,14 @@ public:
|
||||
static jobject createBitmapRegionDecoder(JNIEnv* env, SkBitmapRegionDecoder* bitmap);
|
||||
|
||||
static jbyteArray allocateJavaPixelRef(JNIEnv* env, SkBitmap* bitmap,
|
||||
SkColorTable* ctable);
|
||||
SkColorTable* ctable);
|
||||
|
||||
/** Copy the colors in colors[] to the bitmap, convert to the correct
|
||||
format along the way.
|
||||
*/
|
||||
static bool SetPixels(JNIEnv* env, jintArray colors, int srcOffset,
|
||||
int srcStride, int x, int y, int width, int height,
|
||||
const SkBitmap& dstBitmap);
|
||||
int srcStride, int x, int y, int width, int height,
|
||||
const SkBitmap& dstBitmap, bool isPremultiplied);
|
||||
|
||||
static jbyteArray getBitmapStorageObj(SkPixelRef *pixref);
|
||||
};
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <ScopedUtfChars.h>
|
||||
|
||||
#include "EmojiFactory.h"
|
||||
#include "GraphicsJNI.h"
|
||||
#include <nativehelper/JNIHelp.h>
|
||||
|
||||
#include <dlfcn.h>
|
||||
@@ -92,9 +93,6 @@ static EmojiFactoryCaller* gCaller;
|
||||
static pthread_once_t g_once = PTHREAD_ONCE_INIT;
|
||||
static bool lib_emoji_factory_is_ready;
|
||||
|
||||
static jclass gBitmap_class;
|
||||
static jmethodID gBitmap_constructorMethodID;
|
||||
|
||||
static jclass gEmojiFactory_class;
|
||||
static jmethodID gEmojiFactory_constructorMethodID;
|
||||
|
||||
@@ -172,13 +170,8 @@ static jobject android_emoji_EmojiFactory_getBitmapFromAndroidPua(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject obj = env->NewObject(gBitmap_class, gBitmap_constructorMethodID,
|
||||
static_cast<jint>(reinterpret_cast<uintptr_t>(bitmap)), NULL, false, NULL, -1);
|
||||
if (env->ExceptionCheck() != 0) {
|
||||
ALOGE("*** Uncaught exception returned from Java call!\n");
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
return obj;
|
||||
return GraphicsJNI::createBitmap(env, bitmap,
|
||||
GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL);
|
||||
}
|
||||
|
||||
static void android_emoji_EmojiFactory_destructor(
|
||||
@@ -281,9 +274,6 @@ static jfieldID getFieldIDCheck(JNIEnv* env, jclass clazz,
|
||||
}
|
||||
|
||||
int register_android_emoji_EmojiFactory(JNIEnv* env) {
|
||||
gBitmap_class = make_globalref(env, "android/graphics/Bitmap");
|
||||
gBitmap_constructorMethodID = env->GetMethodID(gBitmap_class, "<init>",
|
||||
"(I[BZ[BI)V");
|
||||
gEmojiFactory_class = make_globalref(env, "android/emoji/EmojiFactory");
|
||||
gEmojiFactory_constructorMethodID = env->GetMethodID(
|
||||
gEmojiFactory_class, "<init>", "(ILjava/lang/String;)V");
|
||||
|
||||
@@ -417,7 +417,7 @@ static void android_view_GLES20Canvas_drawBitmapData(JNIEnv* env, jobject clazz,
|
||||
return;
|
||||
}
|
||||
|
||||
if (!GraphicsJNI::SetPixels(env, colors, offset, stride, 0, 0, width, height, *bitmap)) {
|
||||
if (!GraphicsJNI::SetPixels(env, colors, offset, stride, 0, 0, width, height, *bitmap, true)) {
|
||||
delete bitmap;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -195,7 +195,8 @@ static jobject nativeScreenshotBitmap(JNIEnv* env, jclass clazz, jobject display
|
||||
bitmap->setPixels(NULL);
|
||||
}
|
||||
|
||||
return GraphicsJNI::createBitmap(env, bitmap, false, NULL);
|
||||
return GraphicsJNI::createBitmap(env, bitmap,
|
||||
GraphicsJNI::kBitmapCreateFlag_Premultiplied, NULL);
|
||||
}
|
||||
|
||||
static void nativeScreenshot(JNIEnv* env, jclass clazz,
|
||||
|
||||
@@ -57,10 +57,20 @@ public final class Bitmap implements Parcelable {
|
||||
private final BitmapFinalizer mFinalizer;
|
||||
|
||||
private final boolean mIsMutable;
|
||||
|
||||
/**
|
||||
* Represents whether the Bitmap's content is expected to be pre-multiplied.
|
||||
* Note that isPremultiplied() does not directly return this value, because
|
||||
* isPremultiplied() may never return true for a 565 Bitmap.
|
||||
*
|
||||
* setPremultiplied() does directly set the value so that setConfig() and
|
||||
* setPremultiplied() aren't order dependent, despite being setters.
|
||||
*/
|
||||
private boolean mIsPremultiplied;
|
||||
private byte[] mNinePatchChunk; // may be null
|
||||
private int[] mLayoutBounds; // may be null
|
||||
private int mWidth = -1;
|
||||
private int mHeight = -1;
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
private boolean mRecycled;
|
||||
|
||||
// Package-scoped for fast access.
|
||||
@@ -88,33 +98,27 @@ public final class Bitmap implements Parcelable {
|
||||
return sDefaultDensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor that must received an already allocated native
|
||||
* bitmap int (pointer).
|
||||
*/
|
||||
@SuppressWarnings({"UnusedDeclaration"}) // called from JNI
|
||||
Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
|
||||
int density) {
|
||||
this(nativeBitmap, buffer, isMutable, ninePatchChunk, null, density);
|
||||
}
|
||||
|
||||
/**
|
||||
* Private constructor that must received an already allocated native bitmap
|
||||
* int (pointer).
|
||||
*/
|
||||
@SuppressWarnings({"UnusedDeclaration"}) // called from JNI
|
||||
Bitmap(int nativeBitmap, byte[] buffer, boolean isMutable, byte[] ninePatchChunk,
|
||||
int[] layoutBounds, int density) {
|
||||
Bitmap(int nativeBitmap, byte[] buffer, int width, int height, int density,
|
||||
boolean isMutable, boolean isPremultiplied,
|
||||
byte[] ninePatchChunk, int[] layoutBounds) {
|
||||
if (nativeBitmap == 0) {
|
||||
throw new RuntimeException("internal error: native bitmap is 0");
|
||||
}
|
||||
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mIsMutable = isMutable;
|
||||
mIsPremultiplied = isPremultiplied;
|
||||
mBuffer = buffer;
|
||||
// we delete this in our finalizer
|
||||
mNativeBitmap = nativeBitmap;
|
||||
mFinalizer = new BitmapFinalizer(nativeBitmap);
|
||||
|
||||
mIsMutable = isMutable;
|
||||
mNinePatchChunk = ninePatchChunk;
|
||||
mLayoutBounds = layoutBounds;
|
||||
if (density >= 0) {
|
||||
@@ -123,11 +127,14 @@ public final class Bitmap implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Native bitmap has been reconfigured, so discard cached width/height
|
||||
* Native bitmap has been reconfigured, so set premult and cached
|
||||
* width/height values
|
||||
*/
|
||||
@SuppressWarnings({"UnusedDeclaration"}) // called from JNI
|
||||
void reinit() {
|
||||
mWidth = mHeight = -1;
|
||||
void reinit(int width, int height, boolean isPremultiplied) {
|
||||
mWidth = width;
|
||||
mHeight = height;
|
||||
mIsPremultiplied = isPremultiplied;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -206,7 +213,7 @@ public final class Bitmap implements Parcelable {
|
||||
throw new IllegalStateException("only mutable bitmaps may be reconfigured");
|
||||
}
|
||||
if (mBuffer == null) {
|
||||
throw new IllegalStateException("only non-purgeable bitmaps may be reconfigured");
|
||||
throw new IllegalStateException("native-backed bitmaps may not be reconfigured");
|
||||
}
|
||||
|
||||
nativeReconfigure(mNativeBitmap, width, height, config.nativeInt, mBuffer.length);
|
||||
@@ -543,6 +550,7 @@ public final class Bitmap implements Parcelable {
|
||||
checkRecycled("Can't copy a recycled bitmap");
|
||||
Bitmap b = nativeCopy(mNativeBitmap, config.nativeInt, isMutable);
|
||||
if (b != null) {
|
||||
b.mIsPremultiplied = mIsPremultiplied;
|
||||
b.mDensity = mDensity;
|
||||
}
|
||||
return b;
|
||||
@@ -719,6 +727,7 @@ public final class Bitmap implements Parcelable {
|
||||
// The new bitmap was created from a known bitmap source so assume that
|
||||
// they use the same density
|
||||
bitmap.mDensity = source.mDensity;
|
||||
bitmap.mIsPremultiplied = source.mIsPremultiplied;
|
||||
|
||||
canvas.setBitmap(bitmap);
|
||||
canvas.drawBitmap(source, srcR, dstR, paint);
|
||||
@@ -1001,22 +1010,48 @@ public final class Bitmap implements Parcelable {
|
||||
* <p>This method only returns true if {@link #hasAlpha()} returns true.
|
||||
* A bitmap with no alpha channel can be used both as a pre-multiplied and
|
||||
* as a non pre-multiplied bitmap.</p>
|
||||
*
|
||||
*
|
||||
* <p>Only pre-multiplied bitmaps may be drawn by the view system or
|
||||
* {@link Canvas}. If a non-pre-multiplied bitmap with an alpha channel is
|
||||
* drawn to a Canvas, a RuntimeException will be thrown.</p>
|
||||
*
|
||||
* @return true if the underlying pixels have been pre-multiplied, false
|
||||
* otherwise
|
||||
*
|
||||
* @see Bitmap#setPremultiplied(boolean)
|
||||
* @see BitmapFactory.Options#inPremultiplied
|
||||
*/
|
||||
public final boolean isPremultiplied() {
|
||||
return getConfig() != Config.RGB_565 && hasAlpha();
|
||||
return mIsPremultiplied && getConfig() != Config.RGB_565 && hasAlpha();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the bitmap should treat its data as pre-multiplied.
|
||||
*
|
||||
* <p>Bitmaps are always treated as pre-multiplied by the view system and
|
||||
* {@link Canvas} for performance reasons. Storing un-pre-multiplied data in
|
||||
* a Bitmap (through {@link #setPixel}, {@link #setPixels}, or {@link
|
||||
* BitmapFactory.Options#inPremultiplied BitmapFactory.Options.inPremultiplied})
|
||||
* can lead to incorrect blending if drawn by the framework.</p>
|
||||
*
|
||||
* <p>This method will not affect the behavior of a bitmap without an alpha
|
||||
* channel, or if {@link #hasAlpha()} returns false.</p>
|
||||
*
|
||||
* @see Bitmap#isPremultiplied()
|
||||
* @see BitmapFactory.Options#inPremultiplied
|
||||
*/
|
||||
public final void setPremultiplied(boolean premultiplied) {
|
||||
mIsPremultiplied = premultiplied;
|
||||
}
|
||||
|
||||
/** Returns the bitmap's width */
|
||||
public final int getWidth() {
|
||||
return mWidth == -1 ? mWidth = nativeWidth(mNativeBitmap) : mWidth;
|
||||
return mWidth;
|
||||
}
|
||||
|
||||
/** Returns the bitmap's height */
|
||||
public final int getHeight() {
|
||||
return mHeight == -1 ? mHeight = nativeHeight(mNativeBitmap) : mHeight;
|
||||
return mHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1236,7 +1271,7 @@ public final class Bitmap implements Parcelable {
|
||||
public int getPixel(int x, int y) {
|
||||
checkRecycled("Can't call getPixel() on a recycled bitmap");
|
||||
checkPixelAccess(x, y);
|
||||
return nativeGetPixel(mNativeBitmap, x, y);
|
||||
return nativeGetPixel(mNativeBitmap, x, y, mIsPremultiplied);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1270,7 +1305,7 @@ public final class Bitmap implements Parcelable {
|
||||
}
|
||||
checkPixelsAccess(x, y, width, height, offset, stride, pixels);
|
||||
nativeGetPixels(mNativeBitmap, pixels, offset, stride,
|
||||
x, y, width, height);
|
||||
x, y, width, height, mIsPremultiplied);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1350,7 +1385,7 @@ public final class Bitmap implements Parcelable {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
checkPixelAccess(x, y);
|
||||
nativeSetPixel(mNativeBitmap, x, y, color);
|
||||
nativeSetPixel(mNativeBitmap, x, y, color, mIsPremultiplied);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1387,7 +1422,7 @@ public final class Bitmap implements Parcelable {
|
||||
}
|
||||
checkPixelsAccess(x, y, width, height, offset, stride, pixels);
|
||||
nativeSetPixels(mNativeBitmap, pixels, offset, stride,
|
||||
x, y, width, height);
|
||||
x, y, width, height, mIsPremultiplied);
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<Bitmap> CREATOR
|
||||
@@ -1535,21 +1570,20 @@ public final class Bitmap implements Parcelable {
|
||||
int quality, OutputStream stream,
|
||||
byte[] tempStorage);
|
||||
private static native void nativeErase(int nativeBitmap, int color);
|
||||
private static native int nativeWidth(int nativeBitmap);
|
||||
private static native int nativeHeight(int nativeBitmap);
|
||||
private static native int nativeRowBytes(int nativeBitmap);
|
||||
private static native int nativeConfig(int nativeBitmap);
|
||||
|
||||
private static native int nativeGetPixel(int nativeBitmap, int x, int y);
|
||||
private static native int nativeGetPixel(int nativeBitmap, int x, int y,
|
||||
boolean isPremultiplied);
|
||||
private static native void nativeGetPixels(int nativeBitmap, int[] pixels,
|
||||
int offset, int stride, int x,
|
||||
int y, int width, int height);
|
||||
int offset, int stride, int x, int y,
|
||||
int width, int height, boolean isPremultiplied);
|
||||
|
||||
private static native void nativeSetPixel(int nativeBitmap, int x, int y,
|
||||
int color);
|
||||
int color, boolean isPremultiplied);
|
||||
private static native void nativeSetPixels(int nativeBitmap, int[] colors,
|
||||
int offset, int stride, int x,
|
||||
int y, int width, int height);
|
||||
int offset, int stride, int x, int y,
|
||||
int width, int height, boolean isPremultiplied);
|
||||
private static native void nativeCopyPixelsToBuffer(int nativeBitmap,
|
||||
Buffer dst);
|
||||
private static native void nativeCopyPixelsFromBuffer(int nb, Buffer src);
|
||||
|
||||
@@ -44,6 +44,7 @@ public class BitmapFactory {
|
||||
public Options() {
|
||||
inDither = false;
|
||||
inScaled = true;
|
||||
inPremultiplied = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,6 +125,26 @@ public class BitmapFactory {
|
||||
*/
|
||||
public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;
|
||||
|
||||
/**
|
||||
* If true (which is the default), the resulting bitmap will have its
|
||||
* color channels pre-multipled by the alpha channel.
|
||||
*
|
||||
* <p>This should NOT be set to false for images to be directly drawn by
|
||||
* the view system or through a {@link Canvas}. The view system and
|
||||
* {@link Canvas} assume all drawn images are pre-multiplied to simplify
|
||||
* draw-time blending, and will throw a RuntimeException when
|
||||
* un-premultiplied are drawn.</p>
|
||||
*
|
||||
* <p>This is likely only useful if you want to manipulate raw encoded
|
||||
* image data, e.g. with RenderScript or custom OpenGL.</p>
|
||||
*
|
||||
* <p>This does not affect bitmaps without an alpha channel.</p>
|
||||
*
|
||||
* @see Bitmap#hasAlpha()
|
||||
* @see Bitmap#isPremultiplied()
|
||||
*/
|
||||
public boolean inPremultiplied;
|
||||
|
||||
/**
|
||||
* If dither is true, the decoder will attempt to dither the decoded
|
||||
* image.
|
||||
|
||||
@@ -136,7 +136,7 @@ public class Canvas {
|
||||
if (!bitmap.isMutable()) {
|
||||
throw new IllegalStateException("Immutable bitmap passed to Canvas constructor");
|
||||
}
|
||||
throwIfRecycled(bitmap);
|
||||
throwIfCannotDraw(bitmap);
|
||||
mNativeCanvas = initRaster(bitmap.ni());
|
||||
mFinalizer = new CanvasFinalizer(mNativeCanvas);
|
||||
mBitmap = bitmap;
|
||||
@@ -225,7 +225,7 @@ public class Canvas {
|
||||
if (!bitmap.isMutable()) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
throwIfRecycled(bitmap);
|
||||
throwIfCannotDraw(bitmap);
|
||||
|
||||
safeCanvasSwap(initRaster(bitmap.ni()), true);
|
||||
mDensity = bitmap.mDensity;
|
||||
@@ -1075,11 +1075,19 @@ public class Canvas {
|
||||
public void drawPath(Path path, Paint paint) {
|
||||
native_drawPath(mNativeCanvas, path.ni(), paint.mNativePaint);
|
||||
}
|
||||
|
||||
private static void throwIfRecycled(Bitmap bitmap) {
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
protected static void throwIfCannotDraw(Bitmap bitmap) {
|
||||
if (bitmap.isRecycled()) {
|
||||
throw new RuntimeException("Canvas: trying to use a recycled bitmap " + bitmap);
|
||||
}
|
||||
if (!bitmap.isPremultiplied() && bitmap.getConfig() == Bitmap.Config.ARGB_8888 &&
|
||||
bitmap.hasAlpha()) {
|
||||
throw new RuntimeException("Canvas: trying to use a non-premultiplied bitmap "
|
||||
+ bitmap);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1128,7 +1136,7 @@ public class Canvas {
|
||||
* @param paint The paint used to draw the bitmap (may be null)
|
||||
*/
|
||||
public void drawBitmap(Bitmap bitmap, float left, float top, Paint paint) {
|
||||
throwIfRecycled(bitmap);
|
||||
throwIfCannotDraw(bitmap);
|
||||
native_drawBitmap(mNativeCanvas, bitmap.ni(), left, top,
|
||||
paint != null ? paint.mNativePaint : 0, mDensity, mScreenDensity, bitmap.mDensity);
|
||||
}
|
||||
@@ -1159,7 +1167,7 @@ public class Canvas {
|
||||
if (dst == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
throwIfRecycled(bitmap);
|
||||
throwIfCannotDraw(bitmap);
|
||||
native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst,
|
||||
paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
|
||||
}
|
||||
@@ -1190,7 +1198,7 @@ public class Canvas {
|
||||
if (dst == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
throwIfRecycled(bitmap);
|
||||
throwIfCannotDraw(bitmap);
|
||||
native_drawBitmap(mNativeCanvas, bitmap.ni(), src, dst,
|
||||
paint != null ? paint.mNativePaint : 0, mScreenDensity, bitmap.mDensity);
|
||||
}
|
||||
|
||||
@@ -551,8 +551,10 @@ public final class Bitmap_Delegate {
|
||||
int nativeInt = sManager.addNewDelegate(delegate);
|
||||
|
||||
// and create/return a new Bitmap with it
|
||||
return new Bitmap(nativeInt, null /* buffer */, isMutable, null /*ninePatchChunk*/,
|
||||
density);
|
||||
// TODO: pass correct width, height, isPremultiplied
|
||||
return new Bitmap(nativeInt, null /* buffer */, -1 /* width */, -1 /* height */, density,
|
||||
isMutable, true /* isPremultiplied */,
|
||||
null /*ninePatchChunk*/, null /* layoutBounds */);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user