OSDN Git Service

Add inPremutiplied option to BitmapFactory.Options, functionality in Bitmap
authorChris Craik <ccraik@google.com>
Fri, 16 Aug 2013 19:47:03 +0000 (12:47 -0700)
committerChris Craik <ccraik@google.com>
Wed, 21 Aug 2013 01:29:33 +0000 (18:29 -0700)
bug:2248948

Change-Id: I8fdd649332667598504a1076d5a447572bd53086

16 files changed:
api/current.txt
core/java/android/view/GLES20Canvas.java
core/jni/android/graphics/Bitmap.cpp
core/jni/android/graphics/BitmapFactory.cpp
core/jni/android/graphics/BitmapFactory.h
core/jni/android/graphics/BitmapRegionDecoder.cpp
core/jni/android/graphics/Canvas.cpp
core/jni/android/graphics/Graphics.cpp
core/jni/android/graphics/GraphicsJNI.h
core/jni/android_emoji_EmojiFactory.cpp
core/jni/android_view_GLES20Canvas.cpp
core/jni/android_view_SurfaceControl.cpp
graphics/java/android/graphics/Bitmap.java
graphics/java/android/graphics/BitmapFactory.java
graphics/java/android/graphics/Canvas.java
tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java

index 0d4dccf..eaf28f3 100644 (file)
@@ -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;
index d2d1f1b..beefc21 100644 (file)
@@ -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();
         }
index 0ea3bf7..fd9fbae 100644 (file)
@@ -38,6 +38,23 @@ static void FromColor_D32(void* dst, const SkColor src[], int width,
     }\r
 }\r
 \r
+static void FromColor_D32_Raw(void* dst, const SkColor src[], int width,\r
+                          int, int) {\r
+    // SkColor's ordering may be different from SkPMColor\r
+    if (SK_COLOR_MATCHES_PMCOLOR_BYTE_ORDER) {\r
+        memcpy(dst, src, width * sizeof(SkColor));\r
+        return;\r
+    }\r
+\r
+    // order isn't same, repack each pixel manually\r
+    SkPMColor* d = (SkPMColor*)dst;\r
+    for (int i = 0; i < width; i++) {\r
+        SkColor c = *src++;\r
+        *d++ = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),\r
+                                   SkColorGetG(c), SkColorGetB(c));\r
+    }\r
+}\r
+\r
 static void FromColor_D565(void* dst, const SkColor src[], int width,\r
                            int x, int y) {\r
     uint16_t* d = (uint16_t*)dst;\r
@@ -56,19 +73,35 @@ static void FromColor_D4444(void* dst, const SkColor src[], int width,
 \r
     DITHER_4444_SCAN(y);\r
     for (int stop = x + width; x < stop; x++) {\r
-        SkPMColor c = SkPreMultiplyColor(*src++);\r
-        *d++ = SkDitherARGB32To4444(c, DITHER_VALUE(x));\r
-//        *d++ = SkPixel32ToPixel4444(c);\r
+        SkPMColor pmc = SkPreMultiplyColor(*src++);\r
+        *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));\r
+//        *d++ = SkPixel32ToPixel4444(pmc);\r
+    }\r
+}\r
+\r
+static void FromColor_D4444_Raw(void* dst, const SkColor src[], int width,\r
+                            int x, int y) {\r
+    SkPMColor16* d = (SkPMColor16*)dst;\r
+\r
+    DITHER_4444_SCAN(y);\r
+    for (int stop = x + width; x < stop; x++) {\r
+        SkColor c = *src++;\r
+\r
+        // SkPMColor is used because the ordering is ARGB32, even though the target actually premultiplied\r
+        SkPMColor pmc = SkPackARGB32NoCheck(SkColorGetA(c), SkColorGetR(c),\r
+                                            SkColorGetG(c), SkColorGetB(c));\r
+        *d++ = SkDitherARGB32To4444(pmc, DITHER_VALUE(x));\r
+//        *d++ = SkPixel32ToPixel4444(pmc);\r
     }\r
 }\r
 \r
 // can return NULL\r
-static FromColorProc ChooseFromColorProc(SkBitmap::Config config) {\r
+static FromColorProc ChooseFromColorProc(SkBitmap::Config config, bool isPremultiplied) {\r
     switch (config) {\r
         case SkBitmap::kARGB_8888_Config:\r
-            return FromColor_D32;\r
+            return isPremultiplied ? FromColor_D32 : FromColor_D32_Raw;\r
         case SkBitmap::kARGB_4444_Config:\r
-            return FromColor_D4444;\r
+            return isPremultiplied ? FromColor_D4444 : FromColor_D4444_Raw;\r
         case SkBitmap::kRGB_565_Config:\r
             return FromColor_D565;\r
         default:\r
@@ -77,13 +110,12 @@ static FromColorProc ChooseFromColorProc(SkBitmap::Config config) {
     return NULL;\r
 }\r
 \r
-bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors,\r
-                            int srcOffset, int srcStride,\r
-                            int x, int y, int width, int height,\r
-                            const SkBitmap& dstBitmap) {\r
+bool GraphicsJNI::SetPixels(JNIEnv* env, jintArray srcColors, int srcOffset, int srcStride,\r
+        int x, int y, int width, int height,\r
+        const SkBitmap& dstBitmap, bool isPremultiplied) {\r
     SkAutoLockPixels alp(dstBitmap);\r
     void* dst = dstBitmap.getPixels();\r
-    FromColorProc proc = ChooseFromColorProc(dstBitmap.config());\r
+    FromColorProc proc = ChooseFromColorProc(dstBitmap.config(), isPremultiplied);\r
 \r
     if (NULL == dst || NULL == proc) {\r
         return false;\r
@@ -122,6 +154,17 @@ static void ToColor_S32_Alpha(SkColor dst[], const void* src, int width,
     } while (--width != 0);\r
 }\r
 \r
+static void ToColor_S32_Raw(SkColor dst[], const void* src, int width,\r
+                              SkColorTable*) {\r
+    SkASSERT(width > 0);\r
+    const SkPMColor* s = (const SkPMColor*)src;\r
+    do {\r
+        SkPMColor c = *s++;\r
+        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),\r
+                                SkGetPackedG32(c), SkGetPackedB32(c));\r
+    } while (--width != 0);\r
+}\r
+\r
 static void ToColor_S32_Opaque(SkColor dst[], const void* src, int width,\r
                                SkColorTable*) {\r
     SkASSERT(width > 0);\r
@@ -142,6 +185,17 @@ static void ToColor_S4444_Alpha(SkColor dst[], const void* src, int width,
     } while (--width != 0);\r
 }\r
 \r
+static void ToColor_S4444_Raw(SkColor dst[], const void* src, int width,\r
+                                SkColorTable*) {\r
+    SkASSERT(width > 0);\r
+    const SkPMColor16* s = (const SkPMColor16*)src;\r
+    do {\r
+        SkPMColor c = SkPixel4444ToPixel32(*s++);\r
+        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),\r
+                                SkGetPackedG32(c), SkGetPackedB32(c));\r
+    } while (--width != 0);\r
+}\r
+\r
 static void ToColor_S4444_Opaque(SkColor dst[], const void* src, int width,\r
                                  SkColorTable*) {\r
     SkASSERT(width > 0);\r
@@ -175,6 +229,19 @@ static void ToColor_SI8_Alpha(SkColor dst[], const void* src, int width,
     ctable->unlockColors(false);\r
 }\r
 \r
+static void ToColor_SI8_Raw(SkColor dst[], const void* src, int width,\r
+                              SkColorTable* ctable) {\r
+    SkASSERT(width > 0);\r
+    const uint8_t* s = (const uint8_t*)src;\r
+    const SkPMColor* colors = ctable->lockColors();\r
+    do {\r
+        SkPMColor c = colors[*s++];\r
+        *dst++ = SkColorSetARGB(SkGetPackedA32(c), SkGetPackedR32(c),\r
+                                SkGetPackedG32(c), SkGetPackedB32(c));\r
+    } while (--width != 0);\r
+    ctable->unlockColors(false);\r
+}\r
+\r
 static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,\r
                                SkColorTable* ctable) {\r
     SkASSERT(width > 0);\r
@@ -189,19 +256,22 @@ static void ToColor_SI8_Opaque(SkColor dst[], const void* src, int width,
 }\r
 \r
 // can return NULL\r
-static ToColorProc ChooseToColorProc(const SkBitmap& src) {\r
+static ToColorProc ChooseToColorProc(const SkBitmap& src, bool isPremultiplied) {\r
     switch (src.config()) {\r
         case SkBitmap::kARGB_8888_Config:\r
-            return src.isOpaque() ? ToColor_S32_Opaque : ToColor_S32_Alpha;\r
+            if (src.isOpaque()) return ToColor_S32_Opaque;\r
+            return isPremultiplied ? ToColor_S32_Alpha : ToColor_S32_Raw;\r
         case SkBitmap::kARGB_4444_Config:\r
-            return src.isOpaque() ? ToColor_S4444_Opaque : ToColor_S4444_Alpha;\r
+            if (src.isOpaque()) return ToColor_S4444_Opaque;\r
+            return isPremultiplied ? ToColor_S4444_Alpha : ToColor_S4444_Raw;\r
         case SkBitmap::kRGB_565_Config:\r
             return ToColor_S565;\r
         case SkBitmap::kIndex8_Config:\r
             if (src.getColorTable() == NULL) {\r
                 return NULL;\r
             }\r
-            return src.isOpaque() ? ToColor_SI8_Opaque : ToColor_SI8_Alpha;\r
+            if (src.isOpaque()) return ToColor_SI8_Opaque;\r
+            return isPremultiplied ? ToColor_SI8_Raw : ToColor_SI8_Alpha;\r
         default:\r
             break;\r
     }\r
@@ -211,6 +281,12 @@ static ToColorProc ChooseToColorProc(const SkBitmap& src) {
 ///////////////////////////////////////////////////////////////////////////////\r
 ///////////////////////////////////////////////////////////////////////////////\r
 \r
+static int getPremulBitmapCreateFlags(bool isMutable) {\r
+    int flags = GraphicsJNI::kBitmapCreateFlag_Premultiplied;\r
+    if (isMutable) flags |= GraphicsJNI::kBitmapCreateFlag_Mutable;\r
+    return flags;\r
+}\r
+\r
 static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,\r
                               int offset, int stride, int width, int height,\r
                               SkBitmap::Config config, jboolean isMutable) {\r
@@ -236,10 +312,12 @@ static jobject Bitmap_creator(JNIEnv* env, jobject, jintArray jColors,
     }\r
 \r
     if (jColors != NULL) {\r
-        GraphicsJNI::SetPixels(env, jColors, offset, stride, 0, 0, width, height, bitmap);\r
+        GraphicsJNI::SetPixels(env, jColors, offset, stride,\r
+                0, 0, width, height, bitmap, true);\r
     }\r
 \r
-    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff, isMutable, NULL, NULL);\r
+    return GraphicsJNI::createBitmap(env, new SkBitmap(bitmap), buff,\r
+            getPremulBitmapCreateFlags(isMutable), NULL, NULL);\r
 }\r
 \r
 static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,\r
@@ -250,8 +328,8 @@ static jobject Bitmap_copy(JNIEnv* env, jobject, const SkBitmap* src,
     if (!src->copyTo(&result, dstConfig, &allocator)) {\r
         return NULL;\r
     }\r
-\r
-    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(), isMutable, NULL, NULL);\r
+    return GraphicsJNI::createBitmap(env, new SkBitmap(result), allocator.getStorageObj(),\r
+            getPremulBitmapCreateFlags(isMutable), NULL, NULL);\r
 }\r
 \r
 static void Bitmap_destructor(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
@@ -347,14 +425,6 @@ static void Bitmap_erase(JNIEnv* env, jobject, SkBitmap* bitmap, jint color) {
     bitmap->eraseColor(color);\r
 }\r
 \r
-static int Bitmap_width(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
-    return bitmap->width();\r
-}\r
-\r
-static int Bitmap_height(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
-    return bitmap->height();\r
-}\r
-\r
 static int Bitmap_rowBytes(JNIEnv* env, jobject, SkBitmap* bitmap) {\r
     return bitmap->rowBytes();\r
 }\r
@@ -449,7 +519,9 @@ static jobject Bitmap_createFromParcel(JNIEnv* env, jobject, jobject parcel) {
     bitmap->unlockPixels();\r
 \r
     blob.release();\r
-    return GraphicsJNI::createBitmap(env, bitmap, buffer, isMutable, NULL, NULL, density);\r
+\r
+    return GraphicsJNI::createBitmap(env, bitmap, buffer, getPremulBitmapCreateFlags(isMutable),\r
+            NULL, NULL, density);\r
 }\r
 \r
 static jboolean Bitmap_writeToParcel(JNIEnv* env, jobject,\r
@@ -527,16 +599,17 @@ static jobject Bitmap_extractAlpha(JNIEnv* env, jobject clazz,
         env->ReleaseIntArrayElements(offsetXY, array, 0);\r
     }\r
 \r
-    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(), true, NULL, NULL);\r
+    return GraphicsJNI::createBitmap(env, dst, allocator.getStorageObj(),\r
+            GraphicsJNI::kBitmapCreateFlag_Mutable, NULL, NULL);\r
 }\r
 \r
 ///////////////////////////////////////////////////////////////////////////////\r
 \r
 static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,\r
-                           int x, int y) {\r
+        int x, int y, bool isPremultiplied) {\r
     SkAutoLockPixels alp(*bitmap);\r
 \r
-    ToColorProc proc = ChooseToColorProc(*bitmap);\r
+    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);\r
     if (NULL == proc) {\r
         return 0;\r
     }\r
@@ -551,11 +624,11 @@ static int Bitmap_getPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
 }\r
 \r
 static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,\r
-                             jintArray pixelArray, int offset, int stride,\r
-                             int x, int y, int width, int height) {\r
+        jintArray pixelArray, int offset, int stride,\r
+        int x, int y, int width, int height, bool isPremultiplied) {\r
     SkAutoLockPixels alp(*bitmap);\r
 \r
-    ToColorProc proc = ChooseToColorProc(*bitmap);\r
+    ToColorProc proc = ChooseToColorProc(*bitmap, isPremultiplied);\r
     if (NULL == proc) {\r
         return;\r
     }\r
@@ -578,13 +651,13 @@ static void Bitmap_getPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,
 ///////////////////////////////////////////////////////////////////////////////\r
 \r
 static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,\r
-                            int x, int y, SkColor color) {\r
+        int x, int y, SkColor color, bool isPremultiplied) {\r
     SkAutoLockPixels alp(*bitmap);\r
     if (NULL == bitmap->getPixels()) {\r
         return;\r
     }\r
 \r
-    FromColorProc proc = ChooseFromColorProc(bitmap->config());\r
+    FromColorProc proc = ChooseFromColorProc(bitmap->config(), isPremultiplied);\r
     if (NULL == proc) {\r
         return;\r
     }\r
@@ -594,10 +667,10 @@ static void Bitmap_setPixel(JNIEnv* env, jobject, const SkBitmap* bitmap,
 }\r
 \r
 static void Bitmap_setPixels(JNIEnv* env, jobject, const SkBitmap* bitmap,\r
-                             jintArray pixelArray, int offset, int stride,\r
-                             int x, int y, int width, int height) {\r
+        jintArray pixelArray, int offset, int stride,\r
+        int x, int y, int width, int height, bool isPremultiplied) {\r
     GraphicsJNI::SetPixels(env, pixelArray, offset, stride,\r
-                           x, y, width, height, *bitmap);\r
+            x, y, width, height, *bitmap, isPremultiplied);\r
 }\r
 \r
 static void Bitmap_copyPixelsToBuffer(JNIEnv* env, jobject,\r
@@ -693,8 +766,6 @@ static JNINativeMethod gBitmapMethods[] = {
     {   "nativeCompress",           "(IIILjava/io/OutputStream;[B)Z",\r
         (void*)Bitmap_compress },\r
     {   "nativeErase",              "(II)V", (void*)Bitmap_erase },\r
-    {   "nativeWidth",              "(I)I", (void*)Bitmap_width },\r
-    {   "nativeHeight",             "(I)I", (void*)Bitmap_height },\r
     {   "nativeRowBytes",           "(I)I", (void*)Bitmap_rowBytes },\r
     {   "nativeConfig",             "(I)I", (void*)Bitmap_config },\r
     {   "nativeHasAlpha",           "(I)Z", (void*)Bitmap_hasAlpha },\r
@@ -709,10 +780,10 @@ static JNINativeMethod gBitmapMethods[] = {
     {   "nativeExtractAlpha",       "(II[I)Landroid/graphics/Bitmap;",\r
         (void*)Bitmap_extractAlpha },\r
     {   "nativeGenerationId",       "(I)I", (void*)Bitmap_getGenerationId },\r
-    {   "nativeGetPixel",           "(III)I", (void*)Bitmap_getPixel },\r
-    {   "nativeGetPixels",          "(I[IIIIIII)V", (void*)Bitmap_getPixels },\r
-    {   "nativeSetPixel",           "(IIII)V", (void*)Bitmap_setPixel },\r
-    {   "nativeSetPixels",          "(I[IIIIIII)V", (void*)Bitmap_setPixels },\r
+    {   "nativeGetPixel",           "(IIIZ)I", (void*)Bitmap_getPixel },\r
+    {   "nativeGetPixels",          "(I[IIIIIIIZ)V", (void*)Bitmap_getPixels },\r
+    {   "nativeSetPixel",           "(IIIIZ)V", (void*)Bitmap_setPixel },\r
+    {   "nativeSetPixels",          "(I[IIIIIIIZ)V", (void*)Bitmap_setPixels },\r
     {   "nativeCopyPixelsToBuffer", "(ILjava/nio/Buffer;)V",\r
                                             (void*)Bitmap_copyPixelsToBuffer },\r
     {   "nativeCopyPixelsFromBuffer", "(ILjava/nio/Buffer;)V",\r
index 49e3aa4..c433874 100644 (file)
@@ -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");
index f2aaab7..97dcc6d 100644 (file)
@@ -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;
index 10ef60a..0c0ebbb 100644 (file)
@@ -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) {
index 6c4526e..813dd5a 100644 (file)
@@ -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);
     }
index 7c420ad..ef5b7c9 100644 (file)
@@ -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");
index b676527..5a2a6f8 100644 (file)
@@ -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);
 };
index 4383997..5276934 100644 (file)
@@ -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");
index 6d46cf9..dc90da7 100644 (file)
@@ -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;
     }
index 19f56cd..67eade8 100644 (file)
@@ -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,
index 47cf875..c971da5 100644 (file)
@@ -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.
@@ -89,32 +99,26 @@ public final class Bitmap implements Parcelable {
     }
 
     /**
-     * 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);
index a4124bf..1c426fd 100644 (file)
@@ -44,6 +44,7 @@ public class BitmapFactory {
         public Options() {
             inDither = false;
             inScaled = true;
+            inPremultiplied = true;
         }
 
         /**
@@ -125,6 +126,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.
          */
index 0ea4074..d46238f 100644 (file)
@@ -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);
     }
index 96616aa..4121f79 100644 (file)
@@ -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 */);
     }
 
     /**