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 "include/core/SkTypes.h"
9#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
10
11#include "include/core/SkBitmap.h"
12#include "include/private/SkColorData.h"
13#include "include/private/SkMacros.h"
14#include "include/private/SkTo.h"
15#include "include/utils/mac/SkCGUtils.h"
16#include "src/utils/mac/SkUniqueCFRef.h"
17
18#include <climits>
19#include <memory>
20
21static CGBitmapInfo compute_cgalpha_info_rgba(SkAlphaType at) {
22    CGBitmapInfo info = kCGBitmapByteOrder32Big;
23    switch (at) {
24        case kUnknown_SkAlphaType:                                          break;
25        case kOpaque_SkAlphaType:   info |= kCGImageAlphaNoneSkipLast;      break;
26        case kPremul_SkAlphaType:   info |= kCGImageAlphaPremultipliedLast; break;
27        case kUnpremul_SkAlphaType: info |= kCGImageAlphaLast;              break;
28    }
29    return info;
30}
31
32static CGBitmapInfo compute_cgalpha_info_bgra(SkAlphaType at) {
33    CGBitmapInfo info = kCGBitmapByteOrder32Little;
34    switch (at) {
35        case kUnknown_SkAlphaType:                                           break;
36        case kOpaque_SkAlphaType:   info |= kCGImageAlphaNoneSkipFirst;      break;
37        case kPremul_SkAlphaType:   info |= kCGImageAlphaPremultipliedFirst; break;
38        case kUnpremul_SkAlphaType: info |= kCGImageAlphaFirst;              break;
39    }
40    return info;
41}
42static CGBitmapInfo compute_cgalpha_info_4444(SkAlphaType at) {
43    CGBitmapInfo info = kCGBitmapByteOrder16Little;
44    switch (at) {
45        case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipLast;      break;
46        default:                  info |= kCGImageAlphaPremultipliedLast; break;
47    }
48    return info;
49}
50
51static bool get_bitmap_info(SkColorType skColorType,
52                            SkAlphaType skAlphaType,
53                            size_t* bitsPerComponent,
54                            CGBitmapInfo* info,
55                            bool* upscaleTo32) {
56    if (upscaleTo32) {
57        *upscaleTo32 = false;
58    }
59    switch (skColorType) {
60        case kRGB_565_SkColorType:
61            if (upscaleTo32) {
62                *upscaleTo32 = true;
63            }
64            // now treat like RGBA
65            *bitsPerComponent = 8;
66            *info = compute_cgalpha_info_rgba(kOpaque_SkAlphaType);
67            break;
68        case kRGBA_8888_SkColorType:
69            *bitsPerComponent = 8;
70            *info = compute_cgalpha_info_rgba(skAlphaType);
71            break;
72        case kBGRA_8888_SkColorType:
73            *bitsPerComponent = 8;
74            *info = compute_cgalpha_info_bgra(skAlphaType);
75            break;
76        case kARGB_4444_SkColorType:
77            *bitsPerComponent = 4;
78            *info = compute_cgalpha_info_4444(skAlphaType);
79            break;
80        default:
81            return false;
82    }
83    return true;
84}
85
86static std::unique_ptr<SkBitmap> prepare_for_image_ref(const SkBitmap& bm,
87                                                       size_t* bitsPerComponent,
88                                                       CGBitmapInfo* info) {
89    bool upscaleTo32;
90    if (!get_bitmap_info(bm.colorType(), bm.alphaType(), bitsPerComponent, info, &upscaleTo32)) {
91        return nullptr;
92    }
93    if (upscaleTo32) {
94        std::unique_ptr<SkBitmap> copy(new SkBitmap);
95        // here we make a deep copy of the pixels, since CG won't take our
96        // 565 directly, so we always go to RGBA
97        copy->allocPixels(bm.info().makeColorType(kRGBA_8888_SkColorType));
98        bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0);
99        return copy;
100    }
101    return std::make_unique<SkBitmap>(bm);
102}
103
104CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
105                                            CGColorSpaceRef colorSpace) {
106    if (bm.drawsNothing()) {
107        return nullptr;
108    }
109    size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
110    CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
111
112    std::unique_ptr<SkBitmap> bitmap = prepare_for_image_ref(bm, &bitsPerComponent, &info);
113    if (nullptr == bitmap) {
114        return nullptr;
115    }
116
117    SkPixmap pm = bitmap->pixmap();  // Copy bitmap info before releasing it.
118    const size_t s = bitmap->computeByteSize();
119    void* pixels = bitmap->getPixels();
120
121    // our provider "owns" the bitmap*, and will take care of deleting it
122    SkUniqueCFRef<CGDataProviderRef> dataRef(CGDataProviderCreateWithData(
123            bitmap.release(), pixels, s,
124            [](void* p, const void*, size_t) { delete reinterpret_cast<SkBitmap*>(p); }));
125
126    SkUniqueCFRef<CGColorSpaceRef> rgb;
127    if (nullptr == colorSpace) {
128        rgb.reset(CGColorSpaceCreateDeviceRGB());
129        colorSpace = rgb.get();
130    }
131    return CGImageCreate(pm.width(), pm.height(), bitsPerComponent,
132                         pm.info().bytesPerPixel() * CHAR_BIT, pm.rowBytes(), colorSpace,
133                         info, dataRef.get(), nullptr, false, kCGRenderingIntentDefault);
134}
135
136void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
137    SkUniqueCFRef<CGImageRef> img(SkCreateCGImageRef(bm));
138
139    if (img) {
140        CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
141
142        CGContextSaveGState(cg);
143        CGContextTranslateCTM(cg, x, r.size.height + y);
144        CGContextScaleCTM(cg, 1, -1);
145
146        CGContextDrawImage(cg, r, img.get());
147
148        CGContextRestoreGState(cg);
149    }
150}
151
152///////////////////////////////////////////////////////////////////////////////////////////////////
153
154CGContextRef SkCreateCGContext(const SkPixmap& pmap) {
155    CGBitmapInfo cg_bitmap_info = 0;
156    size_t bitsPerComponent = 0;
157    switch (pmap.colorType()) {
158        case kRGBA_8888_SkColorType:
159            bitsPerComponent = 8;
160            cg_bitmap_info = compute_cgalpha_info_rgba(pmap.alphaType());
161            break;
162        case kBGRA_8888_SkColorType:
163            bitsPerComponent = 8;
164            cg_bitmap_info = compute_cgalpha_info_bgra(pmap.alphaType());
165            break;
166        default:
167            return nullptr;   // no other colortypes are supported (for now)
168    }
169
170    size_t rb = pmap.addr() ? pmap.rowBytes() : 0;
171    SkUniqueCFRef<CGColorSpaceRef> cs(CGColorSpaceCreateDeviceRGB());
172    CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(),
173                                            bitsPerComponent, rb, cs.get(), cg_bitmap_info);
174    return cg;
175}
176
177bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
178                             CGImageRef image) {
179    CGBitmapInfo cg_bitmap_info = 0;
180    size_t bitsPerComponent = 0;
181    switch (info.colorType()) {
182        case kRGBA_8888_SkColorType:
183            bitsPerComponent = 8;
184            cg_bitmap_info = compute_cgalpha_info_rgba(info.alphaType());
185            break;
186        case kBGRA_8888_SkColorType:
187            bitsPerComponent = 8;
188            cg_bitmap_info = compute_cgalpha_info_bgra(info.alphaType());
189            break;
190        default:
191            return false;   // no other colortypes are supported (for now)
192    }
193
194    SkUniqueCFRef<CGColorSpaceRef> cs(CGColorSpaceCreateDeviceRGB());
195    SkUniqueCFRef<CGContextRef> cg(CGBitmapContextCreate(
196                pixels, info.width(), info.height(), bitsPerComponent,
197                rowBytes, cs.get(), cg_bitmap_info));
198    if (!cg) {
199        return false;
200    }
201
202    // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
203    // any blending (which could introduce errors and be slower).
204    CGContextSetBlendMode(cg.get(), kCGBlendModeCopy);
205
206    CGContextDrawImage(cg.get(), CGRectMake(0, 0, info.width(), info.height()), image);
207    return true;
208}
209
210bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) {
211    const int width = SkToInt(CGImageGetWidth(image));
212    const int height = SkToInt(CGImageGetHeight(image));
213    SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
214
215    SkBitmap tmp;
216    if (!tmp.tryAllocPixels(info)) {
217        return false;
218    }
219
220    if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
221        return false;
222    }
223
224    CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
225    switch (cgInfo) {
226        case kCGImageAlphaNone:
227        case kCGImageAlphaNoneSkipLast:
228        case kCGImageAlphaNoneSkipFirst:
229            SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
230            tmp.setAlphaType(kOpaque_SkAlphaType);
231            break;
232        default:
233            // we don't know if we're opaque or not, so compute it.
234            if (SkBitmap::ComputeIsOpaque(tmp)) {
235                tmp.setAlphaType(kOpaque_SkAlphaType);
236            }
237    }
238
239    *dst = tmp;
240    return true;
241}
242
243sk_sp<SkImage> SkMakeImageFromCGImage(CGImageRef src) {
244    SkBitmap bm;
245    if (!SkCreateBitmapFromCGImage(&bm, src)) {
246        return nullptr;
247    }
248
249    bm.setImmutable();
250    return bm.asImage();
251}
252
253#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
254