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