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 SkImage::MakeFromBitmap(bm);
251}
252
253#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
254