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