• 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 "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