SkCreateCGImageRef.cpp revision d4a338f4d0a0cdc08d7d3668931c60997f0fa971
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 deep copy of the pixels, since CG won't take our
114        // 565 directly
115        copy->allocPixels(bm.info().makeColorType(kN32_SkColorType));
116        bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0);
117    } else {
118        copy = new SkBitmap(bm);
119    }
120    return copy;
121}
122
123CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
124                                            CGColorSpaceRef colorSpace) {
125    size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
126    CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
127
128    SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
129    if (nullptr == bitmap) {
130        return nullptr;
131    }
132
133    const int w = bitmap->width();
134    const int h = bitmap->height();
135    const size_t s = bitmap->getSize();
136
137    // our provider "owns" the bitmap*, and will take care of deleting it
138    CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
139                                                             SkBitmap_ReleaseInfo);
140
141    bool releaseColorSpace = false;
142    if (nullptr == colorSpace) {
143        colorSpace = CGColorSpaceCreateDeviceRGB();
144        releaseColorSpace = true;
145    }
146
147    CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
148                                   bitmap->bytesPerPixel() * 8,
149                                   bitmap->rowBytes(), colorSpace, info, dataRef,
150                                   nullptr, false, kCGRenderingIntentDefault);
151
152    if (releaseColorSpace) {
153        CGColorSpaceRelease(colorSpace);
154    }
155    CGDataProviderRelease(dataRef);
156    return ref;
157}
158
159void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
160    CGImageRef img = SkCreateCGImageRef(bm);
161
162    if (img) {
163        CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
164
165        CGContextSaveGState(cg);
166        CGContextTranslateCTM(cg, x, r.size.height + y);
167        CGContextScaleCTM(cg, 1, -1);
168
169        CGContextDrawImage(cg, r, img);
170
171        CGContextRestoreGState(cg);
172
173        CGImageRelease(img);
174    }
175}
176
177///////////////////////////////////////////////////////////////////////////////////////////////////
178
179CGContextRef SkCreateCGContext(const SkPixmap& pmap) {
180    CGBitmapInfo cg_bitmap_info = 0;
181    size_t bitsPerComponent = 0;
182    switch (pmap.colorType()) {
183        case kRGBA_8888_SkColorType:
184            bitsPerComponent = 8;
185            cg_bitmap_info = ComputeCGAlphaInfo_RGBA(pmap.alphaType());
186            break;
187        case kBGRA_8888_SkColorType:
188            bitsPerComponent = 8;
189            cg_bitmap_info = ComputeCGAlphaInfo_BGRA(pmap.alphaType());
190            break;
191        default:
192            return nullptr;   // no other colortypes are supported (for now)
193    }
194
195    size_t rb = pmap.addr() ? pmap.rowBytes() : 0;
196    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
197    CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(),
198                                            bitsPerComponent, rb, cs, cg_bitmap_info);
199    CFRelease(cs);
200    return cg;
201}
202
203SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
204                                    CGImageRef image) {
205    CGBitmapInfo cg_bitmap_info = 0;
206    size_t bitsPerComponent = 0;
207    switch (info.colorType()) {
208        case kRGBA_8888_SkColorType:
209            bitsPerComponent = 8;
210            cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
211            break;
212        case kBGRA_8888_SkColorType:
213            bitsPerComponent = 8;
214            cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
215            break;
216        default:
217            return false;   // no other colortypes are supported (for now)
218    }
219
220    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
221    CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
222                                            rowBytes, cs, cg_bitmap_info);
223    CFRelease(cs);
224    if (nullptr == cg) {
225        return false;
226    }
227
228    // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
229    // any blending (which could introduce errors and be slower).
230    CGContextSetBlendMode(cg, kCGBlendModeCopy);
231
232    CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
233    CGContextRelease(cg);
234    return true;
235}
236
237bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) {
238    const int width = SkToInt(CGImageGetWidth(image));
239    const int height = SkToInt(CGImageGetHeight(image));
240    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
241
242    SkBitmap tmp;
243    if (!tmp.tryAllocPixels(info)) {
244        return false;
245    }
246
247    if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
248        return false;
249    }
250
251    CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
252    switch (cgInfo) {
253        case kCGImageAlphaNone:
254        case kCGImageAlphaNoneSkipLast:
255        case kCGImageAlphaNoneSkipFirst:
256            SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
257            tmp.setAlphaType(kOpaque_SkAlphaType);
258            break;
259        default:
260            // we don't know if we're opaque or not, so compute it.
261            if (SkBitmap::ComputeIsOpaque(tmp)) {
262                tmp.setAlphaType(kOpaque_SkAlphaType);
263            }
264    }
265
266    *dst = tmp;
267    return true;
268}
269
270sk_sp<SkImage> SkMakeImageFromCGImage(CGImageRef src) {
271    SkBitmap bm;
272    if (!SkCreateBitmapFromCGImage(&bm, src)) {
273        return nullptr;
274    }
275
276    bm.setImmutable();
277    return SkImage::MakeFromBitmap(bm);
278}
279
280#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
281