blob: 300e23b216ad7ced08cb98b63ee418561667611e [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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 */
mtklein1ee76512015-11-02 10:20:27 -08007
8#include "SkTypes.h"
9#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
10
reed@android.com758b1292008-12-18 17:54:12 +000011#include "SkBitmap.h"
Hal Canary2a2f6752018-06-11 21:44:01 -040012#include "SkCGUtils.h"
Cary Clarka4083c92017-09-15 11:59:23 -040013#include "SkColorData.h"
Hal Canary50dbc092018-06-12 14:50:37 -040014#include "SkMacros.h"
Hal Canary2a2f6752018-06-11 21:44:01 -040015#include "SkTo.h"
reed@android.com758b1292008-12-18 17:54:12 +000016
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000017static CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) {
18 CGBitmapInfo info = kCGBitmapByteOrder32Big;
19 switch (at) {
reed44977482015-02-27 10:23:00 -080020 case kUnknown_SkAlphaType:
21 break;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000022 case kOpaque_SkAlphaType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000023 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
35static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
36 CGBitmapInfo info = kCGBitmapByteOrder32Little;
37 switch (at) {
reed44977482015-02-27 10:23:00 -080038 case kUnknown_SkAlphaType:
39 break;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000040 case kOpaque_SkAlphaType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000041 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
reed@android.com2b26cac2008-12-22 02:33:11 +000053static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
reed@android.com758b1292008-12-18 17:54:12 +000054 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
55 delete bitmap;
56}
57
rmistry@google.comd6176b02012-08-23 18:14:13 +000058static bool getBitmapInfo(const SkBitmap& bm,
reed@google.com9c16bc02011-06-28 21:52:34 +000059 size_t* bitsPerComponent,
60 CGBitmapInfo* info,
61 bool* upscaleTo32) {
62 if (upscaleTo32) {
63 *upscaleTo32 = false;
64 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000065
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +000066 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;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000072#else
reed@google.com9c16bc02011-06-28 21:52:34 +000073 if (upscaleTo32) {
74 *upscaleTo32 = true;
75 }
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000076 // now treat like RGBA
reed@android.com758b1292008-12-18 17:54:12 +000077 *bitsPerComponent = 8;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000078 *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
reed@android.com8ede4922009-06-22 20:04:33 +000079#endif
reed@android.com758b1292008-12-18 17:54:12 +000080 break;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000081 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;
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +000089 case kARGB_4444_SkColorType:
reed@android.com0680d6c2008-12-19 19:46:15 +000090 *bitsPerComponent = 4;
scroggo@google.com426648e2012-11-12 16:18:43 +000091 *info = kCGBitmapByteOrder16Little;
92 if (bm.isOpaque()) {
93 *info |= kCGImageAlphaNoneSkipLast;
94 } else {
95 *info |= kCGImageAlphaPremultipliedLast;
96 }
reed@android.com0680d6c2008-12-19 19:46:15 +000097 break;
reed@android.com758b1292008-12-18 17:54:12 +000098 default:
reed@google.com9c16bc02011-06-28 21:52:34 +000099 return false;
100 }
101 return true;
102}
103
104static SkBitmap* prepareForImageRef(const SkBitmap& bm,
105 size_t* bitsPerComponent,
106 CGBitmapInfo* info) {
107 bool upscaleTo32;
108 if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700109 return nullptr;
reed@android.com758b1292008-12-18 17:54:12 +0000110 }
111
reed@android.com32a42492009-07-10 03:33:52 +0000112 SkBitmap* copy;
113 if (upscaleTo32) {
114 copy = new SkBitmap;
Matt Sarett68b8e3d2017-04-28 11:15:22 -0400115 // here we make a deep copy of the pixels, since CG won't take our
reed@android.com32a42492009-07-10 03:33:52 +0000116 // 565 directly
Matt Sarett68b8e3d2017-04-28 11:15:22 -0400117 copy->allocPixels(bm.info().makeColorType(kN32_SkColorType));
118 bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0);
reed@android.com32a42492009-07-10 03:33:52 +0000119 } else {
120 copy = new SkBitmap(bm);
121 }
122 return copy;
reed@android.com758b1292008-12-18 17:54:12 +0000123}
124
reed@android.com38669c12011-01-03 13:48:50 +0000125CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
126 CGColorSpaceRef colorSpace) {
127 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
128 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
reed@android.com758b1292008-12-18 17:54:12 +0000129
130 SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
halcanary96fcdcc2015-08-27 07:41:13 -0700131 if (nullptr == bitmap) {
132 return nullptr;
reed@android.com758b1292008-12-18 17:54:12 +0000133 }
134
135 const int w = bitmap->width();
136 const int h = bitmap->height();
Mike Reedf0ffb892017-10-03 14:47:21 -0400137 const size_t s = bitmap->computeByteSize();
reed@android.com758b1292008-12-18 17:54:12 +0000138
reed@android.com758b1292008-12-18 17:54:12 +0000139 // our provider "owns" the bitmap*, and will take care of deleting it
reed@android.com2b26cac2008-12-22 02:33:11 +0000140 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000141 SkBitmap_ReleaseInfo);
reed@android.com2b26cac2008-12-22 02:33:11 +0000142
reed@android.com38669c12011-01-03 13:48:50 +0000143 bool releaseColorSpace = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700144 if (nullptr == colorSpace) {
reed@google.comc280d112011-01-05 16:07:35 +0000145 colorSpace = CGColorSpaceCreateDeviceRGB();
reed@android.com38669c12011-01-03 13:48:50 +0000146 releaseColorSpace = true;
147 }
148
reed@android.com758b1292008-12-18 17:54:12 +0000149 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
150 bitmap->bytesPerPixel() * 8,
reed@android.com38669c12011-01-03 13:48:50 +0000151 bitmap->rowBytes(), colorSpace, info, dataRef,
halcanary96fcdcc2015-08-27 07:41:13 -0700152 nullptr, false, kCGRenderingIntentDefault);
reed@android.com38669c12011-01-03 13:48:50 +0000153
154 if (releaseColorSpace) {
155 CGColorSpaceRelease(colorSpace);
156 }
reed@android.com758b1292008-12-18 17:54:12 +0000157 CGDataProviderRelease(dataRef);
158 return ref;
159}
160
reed@android.comf2b98d62010-12-20 18:26:13 +0000161void 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());
reed@google.com62f46592011-01-05 15:49:43 +0000166
reed@android.comf2b98d62010-12-20 18:26:13 +0000167 CGContextSaveGState(cg);
168 CGContextTranslateCTM(cg, x, r.size.height + y);
169 CGContextScaleCTM(cg, 1, -1);
reed@google.com62f46592011-01-05 15:49:43 +0000170
reed@android.comf2b98d62010-12-20 18:26:13 +0000171 CGContextDrawImage(cg, r, img);
reed@google.com62f46592011-01-05 15:49:43 +0000172
reed@android.comf2b98d62010-12-20 18:26:13 +0000173 CGContextRestoreGState(cg);
reed@google.com62f46592011-01-05 15:49:43 +0000174
reed@android.comf2b98d62010-12-20 18:26:13 +0000175 CGImageRelease(img);
176 }
177}
178
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000179///////////////////////////////////////////////////////////////////////////////////////////////////
180
Mike Reed356f7c22017-01-10 11:58:39 -0500181CGContextRef 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
Mike Reed57c2b8b2017-12-31 15:23:54 -0500205bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
206 CGImageRef image) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000207 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);
halcanary96fcdcc2015-08-27 07:41:13 -0700226 if (nullptr == cg) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000227 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
Mike Reed463c8482016-12-21 12:01:12 -0500239bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) {
240 const int width = SkToInt(CGImageGetWidth(image));
241 const int height = SkToInt(CGImageGetHeight(image));
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000242 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
243
244 SkBitmap tmp;
reed84825042014-09-02 12:50:45 -0700245 if (!tmp.tryAllocPixels(info)) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000246 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}
mtklein1ee76512015-11-02 10:20:27 -0800271
Mike Reed463c8482016-12-21 12:01:12 -0500272sk_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
mtklein1ee76512015-11-02 10:20:27 -0800282#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)