expose CGImage -> SkBitmap

BUG=skia:
R=scroggo@google.com, halcanary@google.com

Author: reed@google.com

Review URL: https://codereview.chromium.org/243463005

git-svn-id: http://skia.googlecode.com/svn/trunk@14315 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/src/core/SkBitmap.cpp b/src/core/SkBitmap.cpp
index 2a8c8e4..d19e9ca 100644
--- a/src/core/SkBitmap.cpp
+++ b/src/core/SkBitmap.cpp
@@ -997,7 +997,8 @@
     switch (dstColorType) {
         case kAlpha_8_SkColorType:
         case kRGB_565_SkColorType:
-        case kN32_SkColorType:
+        case kRGBA_8888_SkColorType:
+        case kBGRA_8888_SkColorType:
             break;
         case kIndex_8_SkColorType:
             if (!sameConfigs) {
diff --git a/src/ports/SkImageDecoder_CG.cpp b/src/ports/SkImageDecoder_CG.cpp
index c683752..b82b677 100644
--- a/src/ports/SkImageDecoder_CG.cpp
+++ b/src/ports/SkImageDecoder_CG.cpp
@@ -68,7 +68,9 @@
 
     const int width = SkToInt(CGImageGetWidth(image));
     const int height = SkToInt(CGImageGetHeight(image));
-    bm->setConfig(SkBitmap::kARGB_8888_Config, width, height);
+    SkImageInfo skinfo = SkImageInfo::MakeN32Premul(width, height);
+
+    bm->setConfig(skinfo);
     if (SkImageDecoder::kDecodeBounds_Mode == mode) {
         return true;
     }
@@ -76,16 +78,12 @@
     if (!this->allocPixelRef(bm, NULL)) {
         return false;
     }
+    
+    SkAutoLockPixels alp(*bm);
 
-    bm->lockPixels();
-    bm->eraseColor(SK_ColorTRANSPARENT);
-
-    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
-    CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height, 8, bm->rowBytes(), cs, BITMAP_INFO);
-    CFRelease(cs);
-
-    CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image);
-    CGContextRelease(cg);
+    if (!SkCopyPixelsFromCGImage(bm->info(), bm->rowBytes(), bm->getPixels(), image)) {
+        return false;
+    }
 
     CGImageAlphaInfo info = CGImageGetAlphaInfo(image);
     switch (info) {
@@ -112,7 +110,6 @@
         }
         bm->setAlphaType(kUnpremul_SkAlphaType);
     }
-    bm->unlockPixels();
     return true;
 }
 
diff --git a/src/utils/mac/SkCreateCGImageRef.cpp b/src/utils/mac/SkCreateCGImageRef.cpp
index 00166e9..f4374a4 100644
--- a/src/utils/mac/SkCreateCGImageRef.cpp
+++ b/src/utils/mac/SkCreateCGImageRef.cpp
@@ -9,6 +9,40 @@
 #include "SkBitmap.h"
 #include "SkColorPriv.h"
 
+static CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) {
+    CGBitmapInfo info = kCGBitmapByteOrder32Big;
+    switch (at) {
+        case kOpaque_SkAlphaType:
+        case kIgnore_SkAlphaType:
+            info |= kCGImageAlphaNoneSkipLast;
+            break;
+        case kPremul_SkAlphaType:
+            info |= kCGImageAlphaPremultipliedLast;
+            break;
+        case kUnpremul_SkAlphaType:
+            info |= kCGImageAlphaLast;
+            break;
+    }
+    return info;
+}
+
+static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
+    CGBitmapInfo info = kCGBitmapByteOrder32Little;
+    switch (at) {
+        case kOpaque_SkAlphaType:
+        case kIgnore_SkAlphaType:
+            info |= kCGImageAlphaNoneSkipFirst;
+            break;
+        case kPremul_SkAlphaType:
+            info |= kCGImageAlphaPremultipliedFirst;
+            break;
+        case kUnpremul_SkAlphaType:
+            info |= kCGImageAlphaFirst;
+            break;
+    }
+    return info;
+}
+
 static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
     delete bitmap;
@@ -28,44 +62,23 @@
             // doesn't see quite right. Are they thinking 1555?
             *bitsPerComponent = 5;
             *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
-            break;
-#endif
+#else
             if (upscaleTo32) {
                 *upscaleTo32 = true;
             }
-            // fall through
-        case kN32_SkColorType:
+            // now treat like RGBA
             *bitsPerComponent = 8;
-#if SK_PMCOLOR_BYTE_ORDER(R,G,B,A)
-            *info = kCGBitmapByteOrder32Big;
-            if (bm.isOpaque()) {
-                *info |= kCGImageAlphaNoneSkipLast;
-            } else {
-                *info |= kCGImageAlphaPremultipliedLast;
-            }
-#elif SK_PMCOLOR_BYTE_ORDER(B,G,R,A)
-            // Matches the CGBitmapInfo that Apple recommends for best
-            // performance, used by google chrome.
-            *info = kCGBitmapByteOrder32Little;
-            if (bm.isOpaque()) {
-                *info |= kCGImageAlphaNoneSkipFirst;
-            } else {
-                *info |= kCGImageAlphaPremultipliedFirst;
-            }
-#else
-            // ...add more formats as required...
-#warning Cannot convert SkBitmap to CGImageRef with these shiftmasks. \
-This will probably not work.
-            // Legacy behavior. Perhaps turn this into an error at some
-            // point.
-            *info = kCGBitmapByteOrder32Big;
-            if (bm.isOpaque()) {
-                *info |= kCGImageAlphaNoneSkipLast;
-            } else {
-                *info |= kCGImageAlphaPremultipliedLast;
-            }
+            *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
 #endif
             break;
+        case kRGBA_8888_SkColorType:
+            *bitsPerComponent = 8;
+            *info = ComputeCGAlphaInfo_RGBA(bm.alphaType());
+            break;
+        case kBGRA_8888_SkColorType:
+            *bitsPerComponent = 8;
+            *info = ComputeCGAlphaInfo_BGRA(bm.alphaType());
+            break;
         case kARGB_4444_SkColorType:
             *bitsPerComponent = 4;
             *info = kCGBitmapByteOrder16Little;
@@ -231,3 +244,73 @@
     output->swap(bitmap);
     return true;
 }
+
+///////////////////////////////////////////////////////////////////////////////////////////////////
+
+SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
+                                    CGImageRef image) {
+    CGBitmapInfo cg_bitmap_info = 0;
+    size_t bitsPerComponent = 0;
+    switch (info.colorType()) {
+        case kRGBA_8888_SkColorType:
+            bitsPerComponent = 8;
+            cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
+            break;
+        case kBGRA_8888_SkColorType:
+            bitsPerComponent = 8;
+            cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
+            break;
+        default:
+            return false;   // no other colortypes are supported (for now)
+    }
+
+    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
+    CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
+                                            rowBytes, cs, cg_bitmap_info);
+    CFRelease(cs);
+    if (NULL == cg) {
+        return false;
+    }
+
+    // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
+    // any blending (which could introduce errors and be slower).
+    CGContextSetBlendMode(cg, kCGBlendModeCopy);
+
+    CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
+    CGContextRelease(cg);
+    return true;
+}
+
+bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image, SkISize* scaleToFit) {
+    const int width = scaleToFit ? scaleToFit->width() : SkToInt(CGImageGetWidth(image));
+    const int height = scaleToFit ? scaleToFit->height() : SkToInt(CGImageGetHeight(image));
+    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
+
+    SkBitmap tmp;
+    if (!tmp.allocPixels(info)) {
+        return false;
+    }
+
+    if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
+        return false;
+    }
+
+    CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
+    switch (cgInfo) {
+        case kCGImageAlphaNone:
+        case kCGImageAlphaNoneSkipLast:
+        case kCGImageAlphaNoneSkipFirst:
+            SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
+            tmp.setAlphaType(kOpaque_SkAlphaType);
+            break;
+        default:
+            // we don't know if we're opaque or not, so compute it.
+            if (SkBitmap::ComputeIsOpaque(tmp)) {
+                tmp.setAlphaType(kOpaque_SkAlphaType);
+            }
+    }
+
+    *dst = tmp;
+    return true;
+}
+