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/core/SkColorSpace.h"
13 #include "include/core/SkData.h"
14 #include "include/encode/SkICC.h"
15 #include "include/private/SkColorData.h"
16 #include "include/private/base/SkMacros.h"
17 #include "include/private/base/SkTo.h"
18 #include "include/utils/mac/SkCGUtils.h"
19 #include "src/utils/mac/SkUniqueCFRef.h"
20
21 #include <climits>
22 #include <memory>
23
compute_cgalpha_info_rgba(SkAlphaType at)24 static CGBitmapInfo compute_cgalpha_info_rgba(SkAlphaType at) {
25 CGBitmapInfo info = kCGBitmapByteOrder32Big;
26 switch (at) {
27 case kUnknown_SkAlphaType: break;
28 case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipLast; break;
29 case kPremul_SkAlphaType: info |= kCGImageAlphaPremultipliedLast; break;
30 case kUnpremul_SkAlphaType: info |= kCGImageAlphaLast; break;
31 }
32 return info;
33 }
34
compute_cgalpha_info_bgra(SkAlphaType at)35 static CGBitmapInfo compute_cgalpha_info_bgra(SkAlphaType at) {
36 CGBitmapInfo info = kCGBitmapByteOrder32Little;
37 switch (at) {
38 case kUnknown_SkAlphaType: break;
39 case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipFirst; break;
40 case kPremul_SkAlphaType: info |= kCGImageAlphaPremultipliedFirst; break;
41 case kUnpremul_SkAlphaType: info |= kCGImageAlphaFirst; break;
42 }
43 return info;
44 }
compute_cgalpha_info_4444(SkAlphaType at)45 static CGBitmapInfo compute_cgalpha_info_4444(SkAlphaType at) {
46 CGBitmapInfo info = kCGBitmapByteOrder16Little;
47 switch (at) {
48 case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipLast; break;
49 default: info |= kCGImageAlphaPremultipliedLast; break;
50 }
51 return info;
52 }
53
get_bitmap_info(SkColorType skColorType,SkAlphaType skAlphaType,size_t * bitsPerComponent,CGBitmapInfo * info,bool * upscaleTo32)54 static bool get_bitmap_info(SkColorType skColorType,
55 SkAlphaType skAlphaType,
56 size_t* bitsPerComponent,
57 CGBitmapInfo* info,
58 bool* upscaleTo32) {
59 if (upscaleTo32) {
60 *upscaleTo32 = false;
61 }
62 switch (skColorType) {
63 case kRGB_565_SkColorType:
64 if (upscaleTo32) {
65 *upscaleTo32 = true;
66 }
67 // now treat like RGBA
68 *bitsPerComponent = 8;
69 *info = compute_cgalpha_info_rgba(kOpaque_SkAlphaType);
70 break;
71 case kRGBA_8888_SkColorType:
72 *bitsPerComponent = 8;
73 *info = compute_cgalpha_info_rgba(skAlphaType);
74 break;
75 case kBGRA_8888_SkColorType:
76 *bitsPerComponent = 8;
77 *info = compute_cgalpha_info_bgra(skAlphaType);
78 break;
79 case kARGB_4444_SkColorType:
80 *bitsPerComponent = 4;
81 *info = compute_cgalpha_info_4444(skAlphaType);
82 break;
83 default:
84 return false;
85 }
86 return true;
87 }
88
prepare_for_image_ref(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info)89 static std::unique_ptr<SkBitmap> prepare_for_image_ref(const SkBitmap& bm,
90 size_t* bitsPerComponent,
91 CGBitmapInfo* info) {
92 bool upscaleTo32;
93 if (!get_bitmap_info(bm.colorType(), bm.alphaType(), bitsPerComponent, info, &upscaleTo32)) {
94 return nullptr;
95 }
96 if (upscaleTo32) {
97 std::unique_ptr<SkBitmap> copy(new SkBitmap);
98 // here we make a deep copy of the pixels, since CG won't take our
99 // 565 directly, so we always go to RGBA
100 copy->allocPixels(bm.info().makeColorType(kRGBA_8888_SkColorType));
101 bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0);
102 return copy;
103 }
104 return std::make_unique<SkBitmap>(bm);
105 }
106
SkCreateCGImageRefWithColorspace(const SkBitmap & bm,CGColorSpaceRef colorSpace)107 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
108 CGColorSpaceRef colorSpace) {
109 return SkCreateCGImageRef(bm);
110 }
111
SkCreateCGImageRef(const SkBitmap & bm)112 CGImageRef SkCreateCGImageRef(const SkBitmap& bm) {
113 if (bm.drawsNothing()) {
114 return nullptr;
115 }
116 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
117 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
118
119 std::unique_ptr<SkBitmap> bitmap = prepare_for_image_ref(bm, &bitsPerComponent, &info);
120 if (nullptr == bitmap) {
121 return nullptr;
122 }
123
124 SkPixmap pm = bitmap->pixmap(); // Copy bitmap info before releasing it.
125 const size_t s = bitmap->computeByteSize();
126 void* pixels = bitmap->getPixels();
127
128 // our provider "owns" the bitmap*, and will take care of deleting it
129 SkUniqueCFRef<CGDataProviderRef> dataRef(CGDataProviderCreateWithData(
130 bitmap.release(), pixels, s,
131 [](void* p, const void*, size_t) { delete reinterpret_cast<SkBitmap*>(p); }));
132
133 SkUniqueCFRef<CGColorSpaceRef> colorSpace(SkCreateCGColorSpace(bm.colorSpace()));
134 return CGImageCreate(pm.width(),
135 pm.height(),
136 bitsPerComponent,
137 pm.info().bytesPerPixel() * CHAR_BIT,
138 pm.rowBytes(),
139 colorSpace.get(),
140 info,
141 dataRef.get(),
142 nullptr,
143 false,
144 kCGRenderingIntentDefault);
145 }
146
SkCGDrawBitmap(CGContextRef cg,const SkBitmap & bm,float x,float y)147 void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
148 SkUniqueCFRef<CGImageRef> img(SkCreateCGImageRef(bm));
149
150 if (img) {
151 CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
152
153 CGContextSaveGState(cg);
154 CGContextTranslateCTM(cg, x, r.size.height + y);
155 CGContextScaleCTM(cg, 1, -1);
156
157 CGContextDrawImage(cg, r, img.get());
158
159 CGContextRestoreGState(cg);
160 }
161 }
162
163 ///////////////////////////////////////////////////////////////////////////////////////////////////
164
SkCreateCGContext(const SkPixmap & pmap)165 CGContextRef SkCreateCGContext(const SkPixmap& pmap) {
166 CGBitmapInfo cg_bitmap_info = 0;
167 size_t bitsPerComponent = 0;
168 switch (pmap.colorType()) {
169 case kRGBA_8888_SkColorType:
170 bitsPerComponent = 8;
171 cg_bitmap_info = compute_cgalpha_info_rgba(pmap.alphaType());
172 break;
173 case kBGRA_8888_SkColorType:
174 bitsPerComponent = 8;
175 cg_bitmap_info = compute_cgalpha_info_bgra(pmap.alphaType());
176 break;
177 default:
178 return nullptr; // no other colortypes are supported (for now)
179 }
180
181 size_t rb = pmap.addr() ? pmap.rowBytes() : 0;
182 SkUniqueCFRef<CGColorSpaceRef> cs(SkCreateCGColorSpace(pmap.colorSpace()));
183 CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(),
184 bitsPerComponent, rb, cs.get(), cg_bitmap_info);
185 return cg;
186 }
187
SkCopyPixelsFromCGImage(const SkImageInfo & info,size_t rowBytes,void * pixels,CGImageRef image)188 bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
189 CGImageRef image) {
190 CGBitmapInfo cg_bitmap_info = 0;
191 size_t bitsPerComponent = 0;
192 switch (info.colorType()) {
193 case kRGBA_8888_SkColorType:
194 bitsPerComponent = 8;
195 cg_bitmap_info = compute_cgalpha_info_rgba(info.alphaType());
196 break;
197 case kBGRA_8888_SkColorType:
198 bitsPerComponent = 8;
199 cg_bitmap_info = compute_cgalpha_info_bgra(info.alphaType());
200 break;
201 default:
202 return false; // no other colortypes are supported (for now)
203 }
204
205 SkUniqueCFRef<CGColorSpaceRef> cs(SkCreateCGColorSpace(info.colorSpace()));
206 SkUniqueCFRef<CGContextRef> cg(CGBitmapContextCreate(
207 pixels, info.width(), info.height(), bitsPerComponent,
208 rowBytes, cs.get(), cg_bitmap_info));
209 if (!cg) {
210 return false;
211 }
212
213 // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
214 // any blending (which could introduce errors and be slower).
215 CGContextSetBlendMode(cg.get(), kCGBlendModeCopy);
216
217 CGContextDrawImage(cg.get(), CGRectMake(0, 0, info.width(), info.height()), image);
218 return true;
219 }
220
SkCreateBitmapFromCGImage(SkBitmap * dst,CGImageRef image)221 bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) {
222 const int width = SkToInt(CGImageGetWidth(image));
223 const int height = SkToInt(CGImageGetHeight(image));
224 sk_sp<SkColorSpace> colorSpace(SkMakeColorSpaceFromCGColorSpace(CGImageGetColorSpace(image)));
225 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height, colorSpace);
226
227 SkBitmap tmp;
228 if (!tmp.tryAllocPixels(info)) {
229 return false;
230 }
231
232 if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
233 return false;
234 }
235
236 CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
237 switch (cgInfo) {
238 case kCGImageAlphaNone:
239 case kCGImageAlphaNoneSkipLast:
240 case kCGImageAlphaNoneSkipFirst:
241 SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
242 tmp.setAlphaType(kOpaque_SkAlphaType);
243 break;
244 default:
245 // we don't know if we're opaque or not, so compute it.
246 if (SkBitmap::ComputeIsOpaque(tmp)) {
247 tmp.setAlphaType(kOpaque_SkAlphaType);
248 }
249 }
250
251 *dst = tmp;
252 return true;
253 }
254
SkMakeImageFromCGImage(CGImageRef src)255 sk_sp<SkImage> SkMakeImageFromCGImage(CGImageRef src) {
256 SkBitmap bm;
257 if (!SkCreateBitmapFromCGImage(&bm, src)) {
258 return nullptr;
259 }
260
261 bm.setImmutable();
262 return bm.asImage();
263 }
264
SkCreateCGDataProvider(sk_sp<SkData> data)265 CGDataProviderRef SkCreateCGDataProvider(sk_sp<SkData> data) {
266 if (!data) {
267 return nullptr;
268 }
269
270 CGDataProviderRef result = CGDataProviderCreateWithData(
271 data.get(), data->data(), data->size(), [](void* info, const void*, size_t) {
272 reinterpret_cast<SkData*>(info)->unref();
273 });
274 if (!result) {
275 return nullptr;
276 }
277
278 // Retain `data` for the release that will come when `result` is freed.
279 data->ref();
280 return result;
281 }
282
SkMakeColorSpaceFromCGColorSpace(CGColorSpaceRef cgColorSpace)283 sk_sp<SkColorSpace> SkMakeColorSpaceFromCGColorSpace(CGColorSpaceRef cgColorSpace) {
284 if (!cgColorSpace) {
285 return nullptr;
286 }
287
288 // Attempt to convert by name.
289 SkUniqueCFRef<CFStringRef> name(CGColorSpaceCopyName(cgColorSpace));
290 if (name && CFStringCompare(name.get(), kCGColorSpaceSRGB, 0) == kCFCompareEqualTo) {
291 return SkColorSpace::MakeSRGB();
292 }
293
294 // Attempt to convert by parsing the ICC profile.
295 SkUniqueCFRef<CFDataRef> iccData(CGColorSpaceCopyICCData(cgColorSpace));
296 if (!iccData) {
297 return nullptr;
298 }
299 skcms_ICCProfile iccProfile;
300 if (!skcms_Parse(
301 CFDataGetBytePtr(iccData.get()), CFDataGetLength(iccData.get()), &iccProfile)) {
302 return nullptr;
303 }
304 return SkColorSpace::Make(iccProfile);
305 }
306
SkCreateCGColorSpace(const SkColorSpace * space)307 CGColorSpaceRef SkCreateCGColorSpace(const SkColorSpace* space) {
308 // Initialize result to sRGB. We will use this as the fallback on failure.
309 CGColorSpaceRef cgSRGB = CGColorSpaceCreateWithName(kCGColorSpaceSRGB);
310
311 // Early-out of this is sRGB (or nullptr defaulting to sRGB).
312 if (!space || space->isSRGB()) {
313 return cgSRGB;
314 }
315
316 // Create an SkData with the ICC profile.
317 skcms_TransferFunction fn;
318 skcms_Matrix3x3 to_xyzd50;
319 space->transferFn(&fn);
320 space->toXYZD50(&to_xyzd50);
321 sk_sp<SkData> iccData = SkWriteICCProfile(fn, to_xyzd50);
322 if (!iccData) {
323 return cgSRGB;
324 }
325
326 // Create a CGColorSpaceRef from that ICC data.
327 const size_t kNumComponents = 3;
328 const CGFloat kComponentRanges[6] = {0, 1, 0, 1, 0, 1};
329 SkUniqueCFRef<CGDataProviderRef> iccDataProvider(SkCreateCGDataProvider(iccData));
330 CGColorSpaceRef result = CGColorSpaceCreateICCBased(
331 kNumComponents, kComponentRanges, iccDataProvider.get(), cgSRGB);
332 if (!result) {
333 return cgSRGB;
334 }
335
336 // We will not be returning |cgSRGB|, so free it now.
337 CFRelease(cgSRGB);
338 cgSRGB = nullptr;
339
340 return result;
341 }
342
343 #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
344