blob: e7a78b0fa66b1b6c2c7337544890149a2ab98d6d [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"
reed@android.com758b1292008-12-18 17:54:12 +000015
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000016static CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) {
17 CGBitmapInfo info = kCGBitmapByteOrder32Big;
18 switch (at) {
reed44977482015-02-27 10:23:00 -080019 case kUnknown_SkAlphaType:
20 break;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000021 case kOpaque_SkAlphaType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000022 info |= kCGImageAlphaNoneSkipLast;
23 break;
24 case kPremul_SkAlphaType:
25 info |= kCGImageAlphaPremultipliedLast;
26 break;
27 case kUnpremul_SkAlphaType:
28 info |= kCGImageAlphaLast;
29 break;
30 }
31 return info;
32}
33
34static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
35 CGBitmapInfo info = kCGBitmapByteOrder32Little;
36 switch (at) {
reed44977482015-02-27 10:23:00 -080037 case kUnknown_SkAlphaType:
38 break;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000039 case kOpaque_SkAlphaType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000040 info |= kCGImageAlphaNoneSkipFirst;
41 break;
42 case kPremul_SkAlphaType:
43 info |= kCGImageAlphaPremultipliedFirst;
44 break;
45 case kUnpremul_SkAlphaType:
46 info |= kCGImageAlphaFirst;
47 break;
48 }
49 return info;
50}
51
reed@android.com2b26cac2008-12-22 02:33:11 +000052static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
reed@android.com758b1292008-12-18 17:54:12 +000053 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
54 delete bitmap;
55}
56
rmistry@google.comd6176b02012-08-23 18:14:13 +000057static bool getBitmapInfo(const SkBitmap& bm,
reed@google.com9c16bc02011-06-28 21:52:34 +000058 size_t* bitsPerComponent,
59 CGBitmapInfo* info,
60 bool* upscaleTo32) {
61 if (upscaleTo32) {
62 *upscaleTo32 = false;
63 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000064
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +000065 switch (bm.colorType()) {
66 case kRGB_565_SkColorType:
67#if 0
68 // doesn't see quite right. Are they thinking 1555?
69 *bitsPerComponent = 5;
70 *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000071#else
reed@google.com9c16bc02011-06-28 21:52:34 +000072 if (upscaleTo32) {
73 *upscaleTo32 = true;
74 }
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000075 // now treat like RGBA
reed@android.com758b1292008-12-18 17:54:12 +000076 *bitsPerComponent = 8;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000077 *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
reed@android.com8ede4922009-06-22 20:04:33 +000078#endif
reed@android.com758b1292008-12-18 17:54:12 +000079 break;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000080 case kRGBA_8888_SkColorType:
81 *bitsPerComponent = 8;
82 *info = ComputeCGAlphaInfo_RGBA(bm.alphaType());
83 break;
84 case kBGRA_8888_SkColorType:
85 *bitsPerComponent = 8;
86 *info = ComputeCGAlphaInfo_BGRA(bm.alphaType());
87 break;
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +000088 case kARGB_4444_SkColorType:
reed@android.com0680d6c2008-12-19 19:46:15 +000089 *bitsPerComponent = 4;
scroggo@google.com426648e2012-11-12 16:18:43 +000090 *info = kCGBitmapByteOrder16Little;
91 if (bm.isOpaque()) {
92 *info |= kCGImageAlphaNoneSkipLast;
93 } else {
94 *info |= kCGImageAlphaPremultipliedLast;
95 }
reed@android.com0680d6c2008-12-19 19:46:15 +000096 break;
reed@android.com758b1292008-12-18 17:54:12 +000097 default:
reed@google.com9c16bc02011-06-28 21:52:34 +000098 return false;
99 }
100 return true;
101}
102
103static SkBitmap* prepareForImageRef(const SkBitmap& bm,
104 size_t* bitsPerComponent,
105 CGBitmapInfo* info) {
106 bool upscaleTo32;
107 if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700108 return nullptr;
reed@android.com758b1292008-12-18 17:54:12 +0000109 }
110
reed@android.com32a42492009-07-10 03:33:52 +0000111 SkBitmap* copy;
112 if (upscaleTo32) {
113 copy = new SkBitmap;
Matt Sarett68b8e3d2017-04-28 11:15:22 -0400114 // here we make a deep copy of the pixels, since CG won't take our
reed@android.com32a42492009-07-10 03:33:52 +0000115 // 565 directly
Matt Sarett68b8e3d2017-04-28 11:15:22 -0400116 copy->allocPixels(bm.info().makeColorType(kN32_SkColorType));
117 bm.readPixels(copy->info(), copy->getPixels(), copy->rowBytes(), 0, 0);
reed@android.com32a42492009-07-10 03:33:52 +0000118 } else {
119 copy = new SkBitmap(bm);
120 }
121 return copy;
reed@android.com758b1292008-12-18 17:54:12 +0000122}
123
reed@android.com38669c12011-01-03 13:48:50 +0000124CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
125 CGColorSpaceRef colorSpace) {
126 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
127 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
reed@android.com758b1292008-12-18 17:54:12 +0000128
129 SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
halcanary96fcdcc2015-08-27 07:41:13 -0700130 if (nullptr == bitmap) {
131 return nullptr;
reed@android.com758b1292008-12-18 17:54:12 +0000132 }
133
134 const int w = bitmap->width();
135 const int h = bitmap->height();
Mike Reedf0ffb892017-10-03 14:47:21 -0400136 const size_t s = bitmap->computeByteSize();
reed@android.com758b1292008-12-18 17:54:12 +0000137
reed@android.com758b1292008-12-18 17:54:12 +0000138 // our provider "owns" the bitmap*, and will take care of deleting it
reed@android.com2b26cac2008-12-22 02:33:11 +0000139 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000140 SkBitmap_ReleaseInfo);
reed@android.com2b26cac2008-12-22 02:33:11 +0000141
reed@android.com38669c12011-01-03 13:48:50 +0000142 bool releaseColorSpace = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700143 if (nullptr == colorSpace) {
reed@google.comc280d112011-01-05 16:07:35 +0000144 colorSpace = CGColorSpaceCreateDeviceRGB();
reed@android.com38669c12011-01-03 13:48:50 +0000145 releaseColorSpace = true;
146 }
147
reed@android.com758b1292008-12-18 17:54:12 +0000148 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
149 bitmap->bytesPerPixel() * 8,
reed@android.com38669c12011-01-03 13:48:50 +0000150 bitmap->rowBytes(), colorSpace, info, dataRef,
halcanary96fcdcc2015-08-27 07:41:13 -0700151 nullptr, false, kCGRenderingIntentDefault);
reed@android.com38669c12011-01-03 13:48:50 +0000152
153 if (releaseColorSpace) {
154 CGColorSpaceRelease(colorSpace);
155 }
reed@android.com758b1292008-12-18 17:54:12 +0000156 CGDataProviderRelease(dataRef);
157 return ref;
158}
159
reed@android.comf2b98d62010-12-20 18:26:13 +0000160void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
161 CGImageRef img = SkCreateCGImageRef(bm);
162
163 if (img) {
164 CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
reed@google.com62f46592011-01-05 15:49:43 +0000165
reed@android.comf2b98d62010-12-20 18:26:13 +0000166 CGContextSaveGState(cg);
167 CGContextTranslateCTM(cg, x, r.size.height + y);
168 CGContextScaleCTM(cg, 1, -1);
reed@google.com62f46592011-01-05 15:49:43 +0000169
reed@android.comf2b98d62010-12-20 18:26:13 +0000170 CGContextDrawImage(cg, r, img);
reed@google.com62f46592011-01-05 15:49:43 +0000171
reed@android.comf2b98d62010-12-20 18:26:13 +0000172 CGContextRestoreGState(cg);
reed@google.com62f46592011-01-05 15:49:43 +0000173
reed@android.comf2b98d62010-12-20 18:26:13 +0000174 CGImageRelease(img);
175 }
176}
177
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000178///////////////////////////////////////////////////////////////////////////////////////////////////
179
Mike Reed356f7c22017-01-10 11:58:39 -0500180CGContextRef SkCreateCGContext(const SkPixmap& pmap) {
181 CGBitmapInfo cg_bitmap_info = 0;
182 size_t bitsPerComponent = 0;
183 switch (pmap.colorType()) {
184 case kRGBA_8888_SkColorType:
185 bitsPerComponent = 8;
186 cg_bitmap_info = ComputeCGAlphaInfo_RGBA(pmap.alphaType());
187 break;
188 case kBGRA_8888_SkColorType:
189 bitsPerComponent = 8;
190 cg_bitmap_info = ComputeCGAlphaInfo_BGRA(pmap.alphaType());
191 break;
192 default:
193 return nullptr; // no other colortypes are supported (for now)
194 }
195
196 size_t rb = pmap.addr() ? pmap.rowBytes() : 0;
197 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
198 CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(),
199 bitsPerComponent, rb, cs, cg_bitmap_info);
200 CFRelease(cs);
201 return cg;
202}
203
Mike Reed57c2b8b2017-12-31 15:23:54 -0500204bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
205 CGImageRef image) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000206 CGBitmapInfo cg_bitmap_info = 0;
207 size_t bitsPerComponent = 0;
208 switch (info.colorType()) {
209 case kRGBA_8888_SkColorType:
210 bitsPerComponent = 8;
211 cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
212 break;
213 case kBGRA_8888_SkColorType:
214 bitsPerComponent = 8;
215 cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
216 break;
217 default:
218 return false; // no other colortypes are supported (for now)
219 }
220
221 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
222 CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
223 rowBytes, cs, cg_bitmap_info);
224 CFRelease(cs);
halcanary96fcdcc2015-08-27 07:41:13 -0700225 if (nullptr == cg) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000226 return false;
227 }
228
229 // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
230 // any blending (which could introduce errors and be slower).
231 CGContextSetBlendMode(cg, kCGBlendModeCopy);
232
233 CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
234 CGContextRelease(cg);
235 return true;
236}
237
Mike Reed463c8482016-12-21 12:01:12 -0500238bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) {
239 const int width = SkToInt(CGImageGetWidth(image));
240 const int height = SkToInt(CGImageGetHeight(image));
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000241 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
242
243 SkBitmap tmp;
reed84825042014-09-02 12:50:45 -0700244 if (!tmp.tryAllocPixels(info)) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000245 return false;
246 }
247
248 if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
249 return false;
250 }
251
252 CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
253 switch (cgInfo) {
254 case kCGImageAlphaNone:
255 case kCGImageAlphaNoneSkipLast:
256 case kCGImageAlphaNoneSkipFirst:
257 SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
258 tmp.setAlphaType(kOpaque_SkAlphaType);
259 break;
260 default:
261 // we don't know if we're opaque or not, so compute it.
262 if (SkBitmap::ComputeIsOpaque(tmp)) {
263 tmp.setAlphaType(kOpaque_SkAlphaType);
264 }
265 }
266
267 *dst = tmp;
268 return true;
269}
mtklein1ee76512015-11-02 10:20:27 -0800270
Mike Reed463c8482016-12-21 12:01:12 -0500271sk_sp<SkImage> SkMakeImageFromCGImage(CGImageRef src) {
272 SkBitmap bm;
273 if (!SkCreateBitmapFromCGImage(&bm, src)) {
274 return nullptr;
275 }
276
277 bm.setImmutable();
278 return SkImage::MakeFromBitmap(bm);
279}
280
mtklein1ee76512015-11-02 10:20:27 -0800281#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)