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 | |
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 | |
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 | } |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |