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 "SkColorData.h"
14
ComputeCGAlphaInfo_RGBA(SkAlphaType at)15 static 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
ComputeCGAlphaInfo_BGRA(SkAlphaType at)33 static 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
SkBitmap_ReleaseInfo(void * info,const void * pixelData,size_t size)51 static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
52 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
53 delete bitmap;
54 }
55
getBitmapInfo(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info,bool * upscaleTo32)56 static 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
prepareForImageRef(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info)102 static 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
SkCreateCGImageRefWithColorspace(const SkBitmap & bm,CGColorSpaceRef colorSpace)123 CGImageRef 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->computeByteSize();
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
SkCGDrawBitmap(CGContextRef cg,const SkBitmap & bm,float x,float y)159 void 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
SkCreateCGContext(const SkPixmap & pmap)179 CGContextRef 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
SkCopyPixelsFromCGImage(const SkImageInfo & info,size_t rowBytes,void * pixels,CGImageRef image)203 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
SkCreateBitmapFromCGImage(SkBitmap * dst,CGImageRef image)237 bool 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
SkMakeImageFromCGImage(CGImageRef src)270 sk_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