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