SkCreateCGImageRef.cpp revision ede0c5c7784ff4bd86e268d33df89c0ac432ca5b
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