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