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