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