1 2/* 3 * Copyright 2011 Google Inc. 4 * 5 * Use of this source code is governed by a BSD-style license that can be 6 * found in the LICENSE file. 7 */ 8#include "SkCGUtils.h" 9#include "SkBitmap.h" 10#include "SkColorPriv.h" 11 12static CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) { 13 CGBitmapInfo info = kCGBitmapByteOrder32Big; 14 switch (at) { 15 case kUnknown_SkAlphaType: 16 break; 17 case kOpaque_SkAlphaType: 18 info |= kCGImageAlphaNoneSkipLast; 19 break; 20 case kPremul_SkAlphaType: 21 info |= kCGImageAlphaPremultipliedLast; 22 break; 23 case kUnpremul_SkAlphaType: 24 info |= kCGImageAlphaLast; 25 break; 26 } 27 return info; 28} 29 30static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) { 31 CGBitmapInfo info = kCGBitmapByteOrder32Little; 32 switch (at) { 33 case kUnknown_SkAlphaType: 34 break; 35 case kOpaque_SkAlphaType: 36 info |= kCGImageAlphaNoneSkipFirst; 37 break; 38 case kPremul_SkAlphaType: 39 info |= kCGImageAlphaPremultipliedFirst; 40 break; 41 case kUnpremul_SkAlphaType: 42 info |= kCGImageAlphaFirst; 43 break; 44 } 45 return info; 46} 47 48static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) { 49 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info); 50 delete bitmap; 51} 52 53static bool getBitmapInfo(const SkBitmap& bm, 54 size_t* bitsPerComponent, 55 CGBitmapInfo* info, 56 bool* upscaleTo32) { 57 if (upscaleTo32) { 58 *upscaleTo32 = false; 59 } 60 61 switch (bm.colorType()) { 62 case kRGB_565_SkColorType: 63#if 0 64 // doesn't see quite right. Are they thinking 1555? 65 *bitsPerComponent = 5; 66 *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone; 67#else 68 if (upscaleTo32) { 69 *upscaleTo32 = true; 70 } 71 // now treat like RGBA 72 *bitsPerComponent = 8; 73 *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType); 74#endif 75 break; 76 case kRGBA_8888_SkColorType: 77 *bitsPerComponent = 8; 78 *info = ComputeCGAlphaInfo_RGBA(bm.alphaType()); 79 break; 80 case kBGRA_8888_SkColorType: 81 *bitsPerComponent = 8; 82 *info = ComputeCGAlphaInfo_BGRA(bm.alphaType()); 83 break; 84 case kARGB_4444_SkColorType: 85 *bitsPerComponent = 4; 86 *info = kCGBitmapByteOrder16Little; 87 if (bm.isOpaque()) { 88 *info |= kCGImageAlphaNoneSkipLast; 89 } else { 90 *info |= kCGImageAlphaPremultipliedLast; 91 } 92 break; 93 default: 94 return false; 95 } 96 return true; 97} 98 99static SkBitmap* prepareForImageRef(const SkBitmap& bm, 100 size_t* bitsPerComponent, 101 CGBitmapInfo* info) { 102 bool upscaleTo32; 103 if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) { 104 return NULL; 105 } 106 107 SkBitmap* copy; 108 if (upscaleTo32) { 109 copy = new SkBitmap; 110 // here we make a ceep copy of the pixels, since CG won't take our 111 // 565 directly 112 bm.copyTo(copy, kN32_SkColorType); 113 } else { 114 copy = new SkBitmap(bm); 115 } 116 return copy; 117} 118 119CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm, 120 CGColorSpaceRef colorSpace) { 121 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING; 122 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING; 123 124 SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info); 125 if (NULL == bitmap) { 126 return NULL; 127 } 128 129 const int w = bitmap->width(); 130 const int h = bitmap->height(); 131 const size_t s = bitmap->getSize(); 132 133 // our provider "owns" the bitmap*, and will take care of deleting it 134 // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release 135 // proc, which will in turn unlock the pixels 136 bitmap->lockPixels(); 137 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s, 138 SkBitmap_ReleaseInfo); 139 140 bool releaseColorSpace = false; 141 if (NULL == colorSpace) { 142 colorSpace = CGColorSpaceCreateDeviceRGB(); 143 releaseColorSpace = true; 144 } 145 146 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent, 147 bitmap->bytesPerPixel() * 8, 148 bitmap->rowBytes(), colorSpace, info, dataRef, 149 NULL, false, kCGRenderingIntentDefault); 150 151 if (releaseColorSpace) { 152 CGColorSpaceRelease(colorSpace); 153 } 154 CGDataProviderRelease(dataRef); 155 return ref; 156} 157 158void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) { 159 CGImageRef img = SkCreateCGImageRef(bm); 160 161 if (img) { 162 CGRect r = CGRectMake(0, 0, bm.width(), bm.height()); 163 164 CGContextSaveGState(cg); 165 CGContextTranslateCTM(cg, x, r.size.height + y); 166 CGContextScaleCTM(cg, 1, -1); 167 168 CGContextDrawImage(cg, r, img); 169 170 CGContextRestoreGState(cg); 171 172 CGImageRelease(img); 173 } 174} 175 176/////////////////////////////////////////////////////////////////////////////// 177 178#include "SkStream.h" 179 180class SkAutoPDFRelease { 181public: 182 SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {} 183 ~SkAutoPDFRelease() { 184 if (fDoc) { 185 CGPDFDocumentRelease(fDoc); 186 } 187 } 188private: 189 CGPDFDocumentRef fDoc; 190}; 191#define SkAutoPDFRelease(...) SK_REQUIRE_LOCAL_VAR(SkAutoPDFRelease) 192 193bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) { 194 CGDataProviderRef data = SkCreateDataProviderFromStream(stream); 195 if (NULL == data) { 196 return false; 197 } 198 199 CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data); 200 CGDataProviderRelease(data); 201 if (NULL == pdf) { 202 return false; 203 } 204 SkAutoPDFRelease releaseMe(pdf); 205 206 CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1); 207 if (NULL == page) { 208 return false; 209 } 210 211 CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox); 212 213 int w = (int)CGRectGetWidth(bounds); 214 int h = (int)CGRectGetHeight(bounds); 215 216 SkBitmap bitmap; 217 if (!bitmap.tryAllocN32Pixels(w, h)) { 218 return false; 219 } 220 bitmap.eraseColor(SK_ColorWHITE); 221 222 size_t bitsPerComponent; 223 CGBitmapInfo info; 224 getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL); 225 226 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); 227 CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h, 228 bitsPerComponent, bitmap.rowBytes(), 229 cs, info); 230 CGColorSpaceRelease(cs); 231 232 if (ctx) { 233 CGContextDrawPDFPage(ctx, page); 234 CGContextRelease(ctx); 235 } 236 237 output->swap(bitmap); 238 return true; 239} 240 241/////////////////////////////////////////////////////////////////////////////////////////////////// 242 243SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels, 244 CGImageRef image) { 245 CGBitmapInfo cg_bitmap_info = 0; 246 size_t bitsPerComponent = 0; 247 switch (info.colorType()) { 248 case kRGBA_8888_SkColorType: 249 bitsPerComponent = 8; 250 cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType()); 251 break; 252 case kBGRA_8888_SkColorType: 253 bitsPerComponent = 8; 254 cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType()); 255 break; 256 default: 257 return false; // no other colortypes are supported (for now) 258 } 259 260 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); 261 CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent, 262 rowBytes, cs, cg_bitmap_info); 263 CFRelease(cs); 264 if (NULL == cg) { 265 return false; 266 } 267 268 // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing 269 // any blending (which could introduce errors and be slower). 270 CGContextSetBlendMode(cg, kCGBlendModeCopy); 271 272 CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image); 273 CGContextRelease(cg); 274 return true; 275} 276 277bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image, SkISize* scaleToFit) { 278 const int width = scaleToFit ? scaleToFit->width() : SkToInt(CGImageGetWidth(image)); 279 const int height = scaleToFit ? scaleToFit->height() : SkToInt(CGImageGetHeight(image)); 280 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height); 281 282 SkBitmap tmp; 283 if (!tmp.tryAllocPixels(info)) { 284 return false; 285 } 286 287 if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) { 288 return false; 289 } 290 291 CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image); 292 switch (cgInfo) { 293 case kCGImageAlphaNone: 294 case kCGImageAlphaNoneSkipLast: 295 case kCGImageAlphaNoneSkipFirst: 296 SkASSERT(SkBitmap::ComputeIsOpaque(tmp)); 297 tmp.setAlphaType(kOpaque_SkAlphaType); 298 break; 299 default: 300 // we don't know if we're opaque or not, so compute it. 301 if (SkBitmap::ComputeIsOpaque(tmp)) { 302 tmp.setAlphaType(kOpaque_SkAlphaType); 303 } 304 } 305 306 *dst = tmp; 307 return true; 308} 309