SkCreateCGImageRef.cpp revision 5dc14c171a8c5c2608801754bdfc30aac08063a6
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 void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) { 13 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info); 14 delete bitmap; 15} 16 17static bool getBitmapInfo(const SkBitmap& bm, 18 size_t* bitsPerComponent, 19 CGBitmapInfo* info, 20 bool* upscaleTo32) { 21 if (upscaleTo32) { 22 *upscaleTo32 = false; 23 } 24 25 switch (bm.config()) { 26 case SkBitmap::kRGB_565_Config: 27 if (upscaleTo32) { 28 *upscaleTo32 = true; 29 } 30 // fall through 31 case SkBitmap::kARGB_8888_Config: 32 *bitsPerComponent = 8; 33#if SK_PMCOLOR_BYTE_ORDER(R,G,B,A) 34 *info = kCGBitmapByteOrder32Big; 35 if (bm.isOpaque()) { 36 *info |= kCGImageAlphaNoneSkipLast; 37 } else { 38 *info |= kCGImageAlphaPremultipliedLast; 39 } 40#elif SK_PMCOLOR_BYTE_ORDER(B,G,R,A) 41 // Matches the CGBitmapInfo that Apple recommends for best 42 // performance, used by google chrome. 43 *info = kCGBitmapByteOrder32Little; 44 if (bm.isOpaque()) { 45 *info |= kCGImageAlphaNoneSkipFirst; 46 } else { 47 *info |= kCGImageAlphaPremultipliedFirst; 48 } 49#else 50 // ...add more formats as required... 51#warning Cannot convert SkBitmap to CGImageRef with these shiftmasks. \ 52This will probably not work. 53 // Legacy behavior. Perhaps turn this into an error at some 54 // point. 55 *info = kCGBitmapByteOrder32Big; 56 if (bm.isOpaque()) { 57 *info |= kCGImageAlphaNoneSkipLast; 58 } else { 59 *info |= kCGImageAlphaPremultipliedLast; 60 } 61#endif 62 break; 63#if 0 64 case SkBitmap::kRGB_565_Config: 65 // doesn't see quite right. Are they thinking 1555? 66 *bitsPerComponent = 5; 67 *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone; 68 break; 69#endif 70 case SkBitmap::kARGB_4444_Config: 71 *bitsPerComponent = 4; 72 *info = kCGBitmapByteOrder16Little; 73 if (bm.isOpaque()) { 74 *info |= kCGImageAlphaNoneSkipLast; 75 } else { 76 *info |= kCGImageAlphaPremultipliedLast; 77 } 78 break; 79 default: 80 return false; 81 } 82 return true; 83} 84 85static SkBitmap* prepareForImageRef(const SkBitmap& bm, 86 size_t* bitsPerComponent, 87 CGBitmapInfo* info) { 88 bool upscaleTo32; 89 if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) { 90 return NULL; 91 } 92 93 SkBitmap* copy; 94 if (upscaleTo32) { 95 copy = new SkBitmap; 96 // here we make a ceep copy of the pixels, since CG won't take our 97 // 565 directly 98 bm.copyTo(copy, SkBitmap::kARGB_8888_Config); 99 } else { 100 copy = new SkBitmap(bm); 101 } 102 return copy; 103} 104 105CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm, 106 CGColorSpaceRef colorSpace) { 107 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING; 108 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING; 109 110 SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info); 111 if (NULL == bitmap) { 112 return NULL; 113 } 114 115 const int w = bitmap->width(); 116 const int h = bitmap->height(); 117 const size_t s = bitmap->getSize(); 118 119 // our provider "owns" the bitmap*, and will take care of deleting it 120 // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release 121 // proc, which will in turn unlock the pixels 122 bitmap->lockPixels(); 123 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s, 124 SkBitmap_ReleaseInfo); 125 126 bool releaseColorSpace = false; 127 if (NULL == colorSpace) { 128 colorSpace = CGColorSpaceCreateDeviceRGB(); 129 releaseColorSpace = true; 130 } 131 132 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent, 133 bitmap->bytesPerPixel() * 8, 134 bitmap->rowBytes(), colorSpace, info, dataRef, 135 NULL, false, kCGRenderingIntentDefault); 136 137 if (releaseColorSpace) { 138 CGColorSpaceRelease(colorSpace); 139 } 140 CGDataProviderRelease(dataRef); 141 return ref; 142} 143 144void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) { 145 CGImageRef img = SkCreateCGImageRef(bm); 146 147 if (img) { 148 CGRect r = CGRectMake(0, 0, bm.width(), bm.height()); 149 150 CGContextSaveGState(cg); 151 CGContextTranslateCTM(cg, x, r.size.height + y); 152 CGContextScaleCTM(cg, 1, -1); 153 154 CGContextDrawImage(cg, r, img); 155 156 CGContextRestoreGState(cg); 157 158 CGImageRelease(img); 159 } 160} 161 162/////////////////////////////////////////////////////////////////////////////// 163 164#include "SkStream.h" 165 166class SkAutoPDFRelease { 167public: 168 SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {} 169 ~SkAutoPDFRelease() { 170 if (fDoc) { 171 CGPDFDocumentRelease(fDoc); 172 } 173 } 174private: 175 CGPDFDocumentRef fDoc; 176}; 177 178static void CGDataProviderReleaseData_FromMalloc(void*, const void* data, 179 size_t size) { 180 sk_free((void*)data); 181} 182 183bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) { 184 size_t size = stream->getLength(); 185 void* ptr = sk_malloc_throw(size); 186 stream->read(ptr, size); 187 CGDataProviderRef data = CGDataProviderCreateWithData(NULL, ptr, size, 188 CGDataProviderReleaseData_FromMalloc); 189 if (NULL == data) { 190 return false; 191 } 192 193 CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data); 194 CGDataProviderRelease(data); 195 if (NULL == pdf) { 196 return false; 197 } 198 SkAutoPDFRelease releaseMe(pdf); 199 200 CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1); 201 if (NULL == page) { 202 return false; 203 } 204 205 CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox); 206 207 int w = (int)CGRectGetWidth(bounds); 208 int h = (int)CGRectGetHeight(bounds); 209 210 SkBitmap bitmap; 211 bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h); 212 bitmap.allocPixels(); 213 bitmap.eraseColor(SK_ColorWHITE); 214 215 size_t bitsPerComponent; 216 CGBitmapInfo info; 217 getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL); 218 219 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); 220 CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h, 221 bitsPerComponent, bitmap.rowBytes(), 222 cs, info); 223 CGColorSpaceRelease(cs); 224 225 if (ctx) { 226 CGContextDrawPDFPage(ctx, page); 227 CGContextRelease(ctx); 228 } 229 230 output->swap(bitmap); 231 return true; 232} 233