• 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 "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 
compute_cgalpha_info_rgba(SkAlphaType at)20 static CGBitmapInfo compute_cgalpha_info_rgba(SkAlphaType at) {
21     CGBitmapInfo info = kCGBitmapByteOrder32Big;
22     switch (at) {
23         case kUnknown_SkAlphaType:                                          break;
24         case kOpaque_SkAlphaType:   info |= kCGImageAlphaNoneSkipLast;      break;
25         case kPremul_SkAlphaType:   info |= kCGImageAlphaPremultipliedLast; break;
26         case kUnpremul_SkAlphaType: info |= kCGImageAlphaLast;              break;
27     }
28     return info;
29 }
30 
compute_cgalpha_info_bgra(SkAlphaType at)31 static CGBitmapInfo compute_cgalpha_info_bgra(SkAlphaType at) {
32     CGBitmapInfo info = kCGBitmapByteOrder32Little;
33     switch (at) {
34         case kUnknown_SkAlphaType:                                           break;
35         case kOpaque_SkAlphaType:   info |= kCGImageAlphaNoneSkipFirst;      break;
36         case kPremul_SkAlphaType:   info |= kCGImageAlphaPremultipliedFirst; break;
37         case kUnpremul_SkAlphaType: info |= kCGImageAlphaFirst;              break;
38     }
39     return info;
40 }
compute_cgalpha_info_4444(SkAlphaType at)41 static CGBitmapInfo compute_cgalpha_info_4444(SkAlphaType at) {
42     CGBitmapInfo info = kCGBitmapByteOrder16Little;
43     switch (at) {
44         case kOpaque_SkAlphaType: info |= kCGImageAlphaNoneSkipLast;      break;
45         default:                  info |= kCGImageAlphaPremultipliedLast; break;
46     }
47     return info;
48 }
49 
get_bitmap_info(SkColorType skColorType,SkAlphaType skAlphaType,size_t * bitsPerComponent,CGBitmapInfo * info,bool * upscaleTo32)50 static bool get_bitmap_info(SkColorType skColorType,
51                             SkAlphaType skAlphaType,
52                             size_t* bitsPerComponent,
53                             CGBitmapInfo* info,
54                             bool* upscaleTo32) {
55     if (upscaleTo32) {
56         *upscaleTo32 = false;
57     }
58     switch (skColorType) {
59         case kRGB_565_SkColorType:
60             if (upscaleTo32) {
61                 *upscaleTo32 = true;
62             }
63             // now treat like RGBA
64             *bitsPerComponent = 8;
65             *info = compute_cgalpha_info_rgba(kOpaque_SkAlphaType);
66             break;
67         case kRGBA_8888_SkColorType:
68             *bitsPerComponent = 8;
69             *info = compute_cgalpha_info_rgba(skAlphaType);
70             break;
71         case kBGRA_8888_SkColorType:
72             *bitsPerComponent = 8;
73             *info = compute_cgalpha_info_bgra(skAlphaType);
74             break;
75         case kARGB_4444_SkColorType:
76             *bitsPerComponent = 4;
77             *info = compute_cgalpha_info_4444(skAlphaType);
78             break;
79         default:
80             return false;
81     }
82     return true;
83 }
84 
prepare_for_image_ref(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info)85 static std::unique_ptr<SkBitmap> prepare_for_image_ref(const SkBitmap& bm,
86                                                        size_t* bitsPerComponent,
87                                                        CGBitmapInfo* info) {
88     bool upscaleTo32;
89     if (!get_bitmap_info(bm.colorType(), bm.alphaType(), bitsPerComponent, info, &upscaleTo32)) {
90         return nullptr;
91     }
92     if (upscaleTo32) {
93         std::unique_ptr<SkBitmap> copy(new SkBitmap);
94         // here we make a deep copy of the pixels, since CG won't take our
95         // 565 directly, so we always go to RGBA
96         copy->allocPixels(bm.info().makeColorType(kRGBA_8888_SkColorType));
97         bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0);
98         return copy;
99     }
100     return std::unique_ptr<SkBitmap>(new SkBitmap(bm));
101 }
102 
SkCreateCGImageRefWithColorspace(const SkBitmap & bm,CGColorSpaceRef colorSpace)103 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
104                                             CGColorSpaceRef colorSpace) {
105     if (bm.drawsNothing()) {
106         return nullptr;
107     }
108     size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
109     CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
110 
111     std::unique_ptr<SkBitmap> bitmap = prepare_for_image_ref(bm, &bitsPerComponent, &info);
112     if (nullptr == bitmap) {
113         return nullptr;
114     }
115 
116     SkPixmap pm = bitmap->pixmap();  // Copy bitmap info before releasing it.
117     const size_t s = bitmap->computeByteSize();
118     void* pixels = bitmap->getPixels();
119 
120     // our provider "owns" the bitmap*, and will take care of deleting it
121     SkUniqueCFRef<CGDataProviderRef> dataRef(CGDataProviderCreateWithData(
122             bitmap.release(), pixels, s,
123             [](void* p, const void*, size_t) { delete reinterpret_cast<SkBitmap*>(p); }));
124 
125     SkUniqueCFRef<CGColorSpaceRef> rgb;
126     if (nullptr == colorSpace) {
127         rgb.reset(CGColorSpaceCreateDeviceRGB());
128         colorSpace = rgb.get();
129     }
130     return CGImageCreate(pm.width(), pm.height(), bitsPerComponent,
131                          pm.info().bytesPerPixel() * CHAR_BIT, pm.rowBytes(), colorSpace,
132                          info, dataRef.get(), nullptr, false, kCGRenderingIntentDefault);
133 }
134 
SkCGDrawBitmap(CGContextRef cg,const SkBitmap & bm,float x,float y)135 void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
136     SkUniqueCFRef<CGImageRef> img(SkCreateCGImageRef(bm));
137 
138     if (img) {
139         CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
140 
141         CGContextSaveGState(cg);
142         CGContextTranslateCTM(cg, x, r.size.height + y);
143         CGContextScaleCTM(cg, 1, -1);
144 
145         CGContextDrawImage(cg, r, img.get());
146 
147         CGContextRestoreGState(cg);
148     }
149 }
150 
151 ///////////////////////////////////////////////////////////////////////////////////////////////////
152 
SkCreateCGContext(const SkPixmap & pmap)153 CGContextRef SkCreateCGContext(const SkPixmap& pmap) {
154     CGBitmapInfo cg_bitmap_info = 0;
155     size_t bitsPerComponent = 0;
156     switch (pmap.colorType()) {
157         case kRGBA_8888_SkColorType:
158             bitsPerComponent = 8;
159             cg_bitmap_info = compute_cgalpha_info_rgba(pmap.alphaType());
160             break;
161         case kBGRA_8888_SkColorType:
162             bitsPerComponent = 8;
163             cg_bitmap_info = compute_cgalpha_info_bgra(pmap.alphaType());
164             break;
165         default:
166             return nullptr;   // no other colortypes are supported (for now)
167     }
168 
169     size_t rb = pmap.addr() ? pmap.rowBytes() : 0;
170     SkUniqueCFRef<CGColorSpaceRef> cs(CGColorSpaceCreateDeviceRGB());
171     CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(),
172                                             bitsPerComponent, rb, cs.get(), cg_bitmap_info);
173     return cg;
174 }
175 
SkCopyPixelsFromCGImage(const SkImageInfo & info,size_t rowBytes,void * pixels,CGImageRef image)176 bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
177                              CGImageRef image) {
178     CGBitmapInfo cg_bitmap_info = 0;
179     size_t bitsPerComponent = 0;
180     switch (info.colorType()) {
181         case kRGBA_8888_SkColorType:
182             bitsPerComponent = 8;
183             cg_bitmap_info = compute_cgalpha_info_rgba(info.alphaType());
184             break;
185         case kBGRA_8888_SkColorType:
186             bitsPerComponent = 8;
187             cg_bitmap_info = compute_cgalpha_info_bgra(info.alphaType());
188             break;
189         default:
190             return false;   // no other colortypes are supported (for now)
191     }
192 
193     SkUniqueCFRef<CGColorSpaceRef> cs(CGColorSpaceCreateDeviceRGB());
194     SkUniqueCFRef<CGContextRef> cg(CGBitmapContextCreate(
195                 pixels, info.width(), info.height(), bitsPerComponent,
196                 rowBytes, cs.get(), cg_bitmap_info));
197     if (!cg) {
198         return false;
199     }
200 
201     // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
202     // any blending (which could introduce errors and be slower).
203     CGContextSetBlendMode(cg.get(), kCGBlendModeCopy);
204 
205     CGContextDrawImage(cg.get(), CGRectMake(0, 0, info.width(), info.height()), image);
206     return true;
207 }
208 
SkCreateBitmapFromCGImage(SkBitmap * dst,CGImageRef image)209 bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) {
210     const int width = SkToInt(CGImageGetWidth(image));
211     const int height = SkToInt(CGImageGetHeight(image));
212     SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
213 
214     SkBitmap tmp;
215     if (!tmp.tryAllocPixels(info)) {
216         return false;
217     }
218 
219     if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
220         return false;
221     }
222 
223     CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
224     switch (cgInfo) {
225         case kCGImageAlphaNone:
226         case kCGImageAlphaNoneSkipLast:
227         case kCGImageAlphaNoneSkipFirst:
228             SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
229             tmp.setAlphaType(kOpaque_SkAlphaType);
230             break;
231         default:
232             // we don't know if we're opaque or not, so compute it.
233             if (SkBitmap::ComputeIsOpaque(tmp)) {
234                 tmp.setAlphaType(kOpaque_SkAlphaType);
235             }
236     }
237 
238     *dst = tmp;
239     return true;
240 }
241 
SkMakeImageFromCGImage(CGImageRef src)242 sk_sp<SkImage> SkMakeImageFromCGImage(CGImageRef src) {
243     SkBitmap bm;
244     if (!SkCreateBitmapFromCGImage(&bm, src)) {
245         return nullptr;
246     }
247 
248     bm.setImmutable();
249     return SkImage::MakeFromBitmap(bm);
250 }
251 
252 #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
253