• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "SkBitmap.h"
12 #include "SkCGUtils.h"
13 #include "SkColorData.h"
14 #include "SkMacros.h"
15 #include "SkTo.h"
16 
ComputeCGAlphaInfo_RGBA(SkAlphaType at)17 static CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) {
18     CGBitmapInfo info = kCGBitmapByteOrder32Big;
19     switch (at) {
20         case kUnknown_SkAlphaType:
21             break;
22         case kOpaque_SkAlphaType:
23             info |= kCGImageAlphaNoneSkipLast;
24             break;
25         case kPremul_SkAlphaType:
26             info |= kCGImageAlphaPremultipliedLast;
27             break;
28         case kUnpremul_SkAlphaType:
29             info |= kCGImageAlphaLast;
30             break;
31     }
32     return info;
33 }
34 
ComputeCGAlphaInfo_BGRA(SkAlphaType at)35 static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
36     CGBitmapInfo info = kCGBitmapByteOrder32Little;
37     switch (at) {
38         case kUnknown_SkAlphaType:
39             break;
40         case kOpaque_SkAlphaType:
41             info |= kCGImageAlphaNoneSkipFirst;
42             break;
43         case kPremul_SkAlphaType:
44             info |= kCGImageAlphaPremultipliedFirst;
45             break;
46         case kUnpremul_SkAlphaType:
47             info |= kCGImageAlphaFirst;
48             break;
49     }
50     return info;
51 }
52 
SkBitmap_ReleaseInfo(void * info,const void * pixelData,size_t size)53 static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
54     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
55     delete bitmap;
56 }
57 
getBitmapInfo(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info,bool * upscaleTo32)58 static bool getBitmapInfo(const SkBitmap& bm,
59                           size_t* bitsPerComponent,
60                           CGBitmapInfo* info,
61                           bool* upscaleTo32) {
62     if (upscaleTo32) {
63         *upscaleTo32 = false;
64     }
65 
66     switch (bm.colorType()) {
67         case kRGB_565_SkColorType:
68 #if 0
69             // doesn't see quite right. Are they thinking 1555?
70             *bitsPerComponent = 5;
71             *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
72 #else
73             if (upscaleTo32) {
74                 *upscaleTo32 = true;
75             }
76             // now treat like RGBA
77             *bitsPerComponent = 8;
78             *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
79 #endif
80             break;
81         case kRGBA_8888_SkColorType:
82             *bitsPerComponent = 8;
83             *info = ComputeCGAlphaInfo_RGBA(bm.alphaType());
84             break;
85         case kBGRA_8888_SkColorType:
86             *bitsPerComponent = 8;
87             *info = ComputeCGAlphaInfo_BGRA(bm.alphaType());
88             break;
89         case kARGB_4444_SkColorType:
90             *bitsPerComponent = 4;
91             *info = kCGBitmapByteOrder16Little;
92             if (bm.isOpaque()) {
93                 *info |= kCGImageAlphaNoneSkipLast;
94             } else {
95                 *info |= kCGImageAlphaPremultipliedLast;
96             }
97             break;
98         default:
99             return false;
100     }
101     return true;
102 }
103 
prepareForImageRef(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info)104 static SkBitmap* prepareForImageRef(const SkBitmap& bm,
105                                     size_t* bitsPerComponent,
106                                     CGBitmapInfo* info) {
107     bool upscaleTo32;
108     if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
109         return nullptr;
110     }
111 
112     SkBitmap* copy;
113     if (upscaleTo32) {
114         copy = new SkBitmap;
115         // here we make a deep copy of the pixels, since CG won't take our
116         // 565 directly
117         copy->allocPixels(bm.info().makeColorType(kN32_SkColorType));
118         bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0);
119     } else {
120         copy = new SkBitmap(bm);
121     }
122     return copy;
123 }
124 
SkCreateCGImageRefWithColorspace(const SkBitmap & bm,CGColorSpaceRef colorSpace)125 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
126                                             CGColorSpaceRef colorSpace) {
127     size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
128     CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
129 
130     SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
131     if (nullptr == bitmap) {
132         return nullptr;
133     }
134 
135     const int w = bitmap->width();
136     const int h = bitmap->height();
137     const size_t s = bitmap->computeByteSize();
138 
139     // our provider "owns" the bitmap*, and will take care of deleting it
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 
SkCGDrawBitmap(CGContextRef cg,const SkBitmap & bm,float x,float y)161 void 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 
SkCreateCGContext(const SkPixmap & pmap)181 CGContextRef 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 
SkCopyPixelsFromCGImage(const SkImageInfo & info,size_t rowBytes,void * pixels,CGImageRef image)205 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 
SkCreateBitmapFromCGImage(SkBitmap * dst,CGImageRef image)239 bool 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 
SkMakeImageFromCGImage(CGImageRef src)272 sk_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