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