SkCreateCGImageRef.cpp revision d8c2476a8b1e1e1a1771b17e8dd4db8645914f8c
1/*
2 * Copyright 2011 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "SkTypes.h"
9#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
10
11#include "SkCGUtils.h"
12#include "SkBitmap.h"
13#include "SkColorPriv.h"
14
15static CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) {
16    CGBitmapInfo info = kCGBitmapByteOrder32Big;
17    switch (at) {
18        case kUnknown_SkAlphaType:
19            break;
20        case kOpaque_SkAlphaType:
21            info |= kCGImageAlphaNoneSkipLast;
22            break;
23        case kPremul_SkAlphaType:
24            info |= kCGImageAlphaPremultipliedLast;
25            break;
26        case kUnpremul_SkAlphaType:
27            info |= kCGImageAlphaLast;
28            break;
29    }
30    return info;
31}
32
33static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
34    CGBitmapInfo info = kCGBitmapByteOrder32Little;
35    switch (at) {
36        case kUnknown_SkAlphaType:
37            break;
38        case kOpaque_SkAlphaType:
39            info |= kCGImageAlphaNoneSkipFirst;
40            break;
41        case kPremul_SkAlphaType:
42            info |= kCGImageAlphaPremultipliedFirst;
43            break;
44        case kUnpremul_SkAlphaType:
45            info |= kCGImageAlphaFirst;
46            break;
47    }
48    return info;
49}
50
51static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
52    SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
53    delete bitmap;
54}
55
56static bool getBitmapInfo(const SkBitmap& bm,
57                          size_t* bitsPerComponent,
58                          CGBitmapInfo* info,
59                          bool* upscaleTo32) {
60    if (upscaleTo32) {
61        *upscaleTo32 = false;
62    }
63
64    switch (bm.colorType()) {
65        case kRGB_565_SkColorType:
66#if 0
67            // doesn't see quite right. Are they thinking 1555?
68            *bitsPerComponent = 5;
69            *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
70#else
71            if (upscaleTo32) {
72                *upscaleTo32 = true;
73            }
74            // now treat like RGBA
75            *bitsPerComponent = 8;
76            *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
77#endif
78            break;
79        case kRGBA_8888_SkColorType:
80            *bitsPerComponent = 8;
81            *info = ComputeCGAlphaInfo_RGBA(bm.alphaType());
82            break;
83        case kBGRA_8888_SkColorType:
84            *bitsPerComponent = 8;
85            *info = ComputeCGAlphaInfo_BGRA(bm.alphaType());
86            break;
87        case kARGB_4444_SkColorType:
88            *bitsPerComponent = 4;
89            *info = kCGBitmapByteOrder16Little;
90            if (bm.isOpaque()) {
91                *info |= kCGImageAlphaNoneSkipLast;
92            } else {
93                *info |= kCGImageAlphaPremultipliedLast;
94            }
95            break;
96        default:
97            return false;
98    }
99    return true;
100}
101
102static SkBitmap* prepareForImageRef(const SkBitmap& bm,
103                                    size_t* bitsPerComponent,
104                                    CGBitmapInfo* info) {
105    bool upscaleTo32;
106    if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
107        return nullptr;
108    }
109
110    SkBitmap* copy;
111    if (upscaleTo32) {
112        copy = new SkBitmap;
113        // here we make a ceep copy of the pixels, since CG won't take our
114        // 565 directly
115        bm.copyTo(copy, kN32_SkColorType);
116    } else {
117        copy = new SkBitmap(bm);
118    }
119    return copy;
120}
121
122CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
123                                            CGColorSpaceRef colorSpace) {
124    size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
125    CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
126
127    SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
128    if (nullptr == bitmap) {
129        return nullptr;
130    }
131
132    const int w = bitmap->width();
133    const int h = bitmap->height();
134    const size_t s = bitmap->getSize();
135
136    // our provider "owns" the bitmap*, and will take care of deleting it
137    // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
138    // proc, which will in turn unlock the pixels
139    bitmap->lockPixels();
140    CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
141                                                             SkBitmap_ReleaseInfo);
142
143    bool releaseColorSpace = false;
144    if (nullptr == colorSpace) {
145        colorSpace = CGColorSpaceCreateDeviceRGB();
146        releaseColorSpace = true;
147    }
148
149    CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
150                                   bitmap->bytesPerPixel() * 8,
151                                   bitmap->rowBytes(), colorSpace, info, dataRef,
152                                   nullptr, false, kCGRenderingIntentDefault);
153
154    if (releaseColorSpace) {
155        CGColorSpaceRelease(colorSpace);
156    }
157    CGDataProviderRelease(dataRef);
158    return ref;
159}
160
161void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
162    CGImageRef img = SkCreateCGImageRef(bm);
163
164    if (img) {
165        CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
166
167        CGContextSaveGState(cg);
168        CGContextTranslateCTM(cg, x, r.size.height + y);
169        CGContextScaleCTM(cg, 1, -1);
170
171        CGContextDrawImage(cg, r, img);
172
173        CGContextRestoreGState(cg);
174
175        CGImageRelease(img);
176    }
177}
178
179///////////////////////////////////////////////////////////////////////////////////////////////////
180
181SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
182                                    CGImageRef image) {
183    CGBitmapInfo cg_bitmap_info = 0;
184    size_t bitsPerComponent = 0;
185    switch (info.colorType()) {
186        case kRGBA_8888_SkColorType:
187            bitsPerComponent = 8;
188            cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
189            break;
190        case kBGRA_8888_SkColorType:
191            bitsPerComponent = 8;
192            cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
193            break;
194        default:
195            return false;   // no other colortypes are supported (for now)
196    }
197
198    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
199    CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
200                                            rowBytes, cs, cg_bitmap_info);
201    CFRelease(cs);
202    if (nullptr == cg) {
203        return false;
204    }
205
206    // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
207    // any blending (which could introduce errors and be slower).
208    CGContextSetBlendMode(cg, kCGBlendModeCopy);
209
210    CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
211    CGContextRelease(cg);
212    return true;
213}
214
215bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image, SkISize* scaleToFit) {
216    const int width = scaleToFit ? scaleToFit->width() : SkToInt(CGImageGetWidth(image));
217    const int height = scaleToFit ? scaleToFit->height() : SkToInt(CGImageGetHeight(image));
218    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
219
220    SkBitmap tmp;
221    if (!tmp.tryAllocPixels(info)) {
222        return false;
223    }
224
225    if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
226        return false;
227    }
228
229    CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
230    switch (cgInfo) {
231        case kCGImageAlphaNone:
232        case kCGImageAlphaNoneSkipLast:
233        case kCGImageAlphaNoneSkipFirst:
234            SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
235            tmp.setAlphaType(kOpaque_SkAlphaType);
236            break;
237        default:
238            // we don't know if we're opaque or not, so compute it.
239            if (SkBitmap::ComputeIsOpaque(tmp)) {
240                tmp.setAlphaType(kOpaque_SkAlphaType);
241            }
242    }
243
244    *dst = tmp;
245    return true;
246}
247
248#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
249