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
181CGContextRef SkCreateCGContext(const SkPixmap& pmap) {
182    CGBitmapInfo cg_bitmap_info = 0;
183    size_t bitsPerComponent = 0;
184    switch (pmap.colorType()) {
185        case kRGBA_8888_SkColorType:
186            bitsPerComponent = 8;
187            cg_bitmap_info = ComputeCGAlphaInfo_RGBA(pmap.alphaType());
188            break;
189        case kBGRA_8888_SkColorType:
190            bitsPerComponent = 8;
191            cg_bitmap_info = ComputeCGAlphaInfo_BGRA(pmap.alphaType());
192            break;
193        default:
194            return nullptr;   // no other colortypes are supported (for now)
195    }
196
197    size_t rb = pmap.addr() ? pmap.rowBytes() : 0;
198    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
199    CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(),
200                                            bitsPerComponent, rb, cs, cg_bitmap_info);
201    CFRelease(cs);
202    return cg;
203}
204
205SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
206                                    CGImageRef image) {
207    CGBitmapInfo cg_bitmap_info = 0;
208    size_t bitsPerComponent = 0;
209    switch (info.colorType()) {
210        case kRGBA_8888_SkColorType:
211            bitsPerComponent = 8;
212            cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
213            break;
214        case kBGRA_8888_SkColorType:
215            bitsPerComponent = 8;
216            cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
217            break;
218        default:
219            return false;   // no other colortypes are supported (for now)
220    }
221
222    CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
223    CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
224                                            rowBytes, cs, cg_bitmap_info);
225    CFRelease(cs);
226    if (nullptr == cg) {
227        return false;
228    }
229
230    // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
231    // any blending (which could introduce errors and be slower).
232    CGContextSetBlendMode(cg, kCGBlendModeCopy);
233
234    CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
235    CGContextRelease(cg);
236    return true;
237}
238
239bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) {
240    const int width = SkToInt(CGImageGetWidth(image));
241    const int height = SkToInt(CGImageGetHeight(image));
242    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
243
244    SkBitmap tmp;
245    if (!tmp.tryAllocPixels(info)) {
246        return false;
247    }
248
249    if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
250        return false;
251    }
252
253    CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
254    switch (cgInfo) {
255        case kCGImageAlphaNone:
256        case kCGImageAlphaNoneSkipLast:
257        case kCGImageAlphaNoneSkipFirst:
258            SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
259            tmp.setAlphaType(kOpaque_SkAlphaType);
260            break;
261        default:
262            // we don't know if we're opaque or not, so compute it.
263            if (SkBitmap::ComputeIsOpaque(tmp)) {
264                tmp.setAlphaType(kOpaque_SkAlphaType);
265            }
266    }
267
268    *dst = tmp;
269    return true;
270}
271
272sk_sp<SkImage> SkMakeImageFromCGImage(CGImageRef src) {
273    SkBitmap bm;
274    if (!SkCreateBitmapFromCGImage(&bm, src)) {
275        return nullptr;
276    }
277
278    bm.setImmutable();
279    return SkImage::MakeFromBitmap(bm);
280}
281
282#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
283