blob: 880c566c73a2900d5654ed4b51ad4ab105c183ae [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.com0d55f1e2008-12-18 19:26:11 +000011#include "SkCGUtils.h"
reed@android.com758b1292008-12-18 17:54:12 +000012#include "SkBitmap.h"
reed@android.coma545a552009-06-29 17:07:19 +000013#include "SkColorPriv.h"
reed@android.com758b1292008-12-18 17:54:12 +000014
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000015static CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) {
16 CGBitmapInfo info = kCGBitmapByteOrder32Big;
17 switch (at) {
reed44977482015-02-27 10:23:00 -080018 case kUnknown_SkAlphaType:
19 break;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000020 case kOpaque_SkAlphaType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000021 info |= kCGImageAlphaNoneSkipLast;
22 break;
23 case kPremul_SkAlphaType:
24 info |= kCGImageAlphaPremultipliedLast;
25 break;
26 case kUnpremul_SkAlphaType:
27 info |= kCGImageAlphaLast;
28 break;
29 }
30 return info;
31}
32
33static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
34 CGBitmapInfo info = kCGBitmapByteOrder32Little;
35 switch (at) {
reed44977482015-02-27 10:23:00 -080036 case kUnknown_SkAlphaType:
37 break;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000038 case kOpaque_SkAlphaType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000039 info |= kCGImageAlphaNoneSkipFirst;
40 break;
41 case kPremul_SkAlphaType:
42 info |= kCGImageAlphaPremultipliedFirst;
43 break;
44 case kUnpremul_SkAlphaType:
45 info |= kCGImageAlphaFirst;
46 break;
47 }
48 return info;
49}
50
reed@android.com2b26cac2008-12-22 02:33:11 +000051static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
reed@android.com758b1292008-12-18 17:54:12 +000052 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
53 delete bitmap;
54}
55
rmistry@google.comd6176b02012-08-23 18:14:13 +000056static bool getBitmapInfo(const SkBitmap& bm,
reed@google.com9c16bc02011-06-28 21:52:34 +000057 size_t* bitsPerComponent,
58 CGBitmapInfo* info,
59 bool* upscaleTo32) {
60 if (upscaleTo32) {
61 *upscaleTo32 = false;
62 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000063
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +000064 switch (bm.colorType()) {
65 case kRGB_565_SkColorType:
66#if 0
67 // doesn't see quite right. Are they thinking 1555?
68 *bitsPerComponent = 5;
69 *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000070#else
reed@google.com9c16bc02011-06-28 21:52:34 +000071 if (upscaleTo32) {
72 *upscaleTo32 = true;
73 }
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000074 // now treat like RGBA
reed@android.com758b1292008-12-18 17:54:12 +000075 *bitsPerComponent = 8;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000076 *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
reed@android.com8ede4922009-06-22 20:04:33 +000077#endif
reed@android.com758b1292008-12-18 17:54:12 +000078 break;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000079 case kRGBA_8888_SkColorType:
80 *bitsPerComponent = 8;
81 *info = ComputeCGAlphaInfo_RGBA(bm.alphaType());
82 break;
83 case kBGRA_8888_SkColorType:
84 *bitsPerComponent = 8;
85 *info = ComputeCGAlphaInfo_BGRA(bm.alphaType());
86 break;
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +000087 case kARGB_4444_SkColorType:
reed@android.com0680d6c2008-12-19 19:46:15 +000088 *bitsPerComponent = 4;
scroggo@google.com426648e2012-11-12 16:18:43 +000089 *info = kCGBitmapByteOrder16Little;
90 if (bm.isOpaque()) {
91 *info |= kCGImageAlphaNoneSkipLast;
92 } else {
93 *info |= kCGImageAlphaPremultipliedLast;
94 }
reed@android.com0680d6c2008-12-19 19:46:15 +000095 break;
reed@android.com758b1292008-12-18 17:54:12 +000096 default:
reed@google.com9c16bc02011-06-28 21:52:34 +000097 return false;
98 }
99 return true;
100}
101
102static SkBitmap* prepareForImageRef(const SkBitmap& bm,
103 size_t* bitsPerComponent,
104 CGBitmapInfo* info) {
105 bool upscaleTo32;
106 if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
halcanary96fcdcc2015-08-27 07:41:13 -0700107 return nullptr;
reed@android.com758b1292008-12-18 17:54:12 +0000108 }
109
reed@android.com32a42492009-07-10 03:33:52 +0000110 SkBitmap* copy;
111 if (upscaleTo32) {
112 copy = new SkBitmap;
Matt Sarett0122af02017-04-27 20:08:39 +0000113 // here we make a ceep copy of the pixels, since CG won't take our
reed@android.com32a42492009-07-10 03:33:52 +0000114 // 565 directly
Matt Sarett0122af02017-04-27 20:08:39 +0000115 bm.copyTo(copy, kN32_SkColorType);
reed@android.com32a42492009-07-10 03:33:52 +0000116 } else {
117 copy = new SkBitmap(bm);
118 }
119 return copy;
reed@android.com758b1292008-12-18 17:54:12 +0000120}
121
reed@android.com38669c12011-01-03 13:48:50 +0000122CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
123 CGColorSpaceRef colorSpace) {
124 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
125 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
reed@android.com758b1292008-12-18 17:54:12 +0000126
127 SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
halcanary96fcdcc2015-08-27 07:41:13 -0700128 if (nullptr == bitmap) {
129 return nullptr;
reed@android.com758b1292008-12-18 17:54:12 +0000130 }
131
132 const int w = bitmap->width();
133 const int h = bitmap->height();
134 const size_t s = bitmap->getSize();
135
reed@android.com758b1292008-12-18 17:54:12 +0000136 // our provider "owns" the bitmap*, and will take care of deleting it
reed@android.com2b26cac2008-12-22 02:33:11 +0000137 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000138 SkBitmap_ReleaseInfo);
reed@android.com2b26cac2008-12-22 02:33:11 +0000139
reed@android.com38669c12011-01-03 13:48:50 +0000140 bool releaseColorSpace = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700141 if (nullptr == colorSpace) {
reed@google.comc280d112011-01-05 16:07:35 +0000142 colorSpace = CGColorSpaceCreateDeviceRGB();
reed@android.com38669c12011-01-03 13:48:50 +0000143 releaseColorSpace = true;
144 }
145
reed@android.com758b1292008-12-18 17:54:12 +0000146 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
147 bitmap->bytesPerPixel() * 8,
reed@android.com38669c12011-01-03 13:48:50 +0000148 bitmap->rowBytes(), colorSpace, info, dataRef,
halcanary96fcdcc2015-08-27 07:41:13 -0700149 nullptr, false, kCGRenderingIntentDefault);
reed@android.com38669c12011-01-03 13:48:50 +0000150
151 if (releaseColorSpace) {
152 CGColorSpaceRelease(colorSpace);
153 }
reed@android.com758b1292008-12-18 17:54:12 +0000154 CGDataProviderRelease(dataRef);
155 return ref;
156}
157
reed@android.comf2b98d62010-12-20 18:26:13 +0000158void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
159 CGImageRef img = SkCreateCGImageRef(bm);
160
161 if (img) {
162 CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
reed@google.com62f46592011-01-05 15:49:43 +0000163
reed@android.comf2b98d62010-12-20 18:26:13 +0000164 CGContextSaveGState(cg);
165 CGContextTranslateCTM(cg, x, r.size.height + y);
166 CGContextScaleCTM(cg, 1, -1);
reed@google.com62f46592011-01-05 15:49:43 +0000167
reed@android.comf2b98d62010-12-20 18:26:13 +0000168 CGContextDrawImage(cg, r, img);
reed@google.com62f46592011-01-05 15:49:43 +0000169
reed@android.comf2b98d62010-12-20 18:26:13 +0000170 CGContextRestoreGState(cg);
reed@google.com62f46592011-01-05 15:49:43 +0000171
reed@android.comf2b98d62010-12-20 18:26:13 +0000172 CGImageRelease(img);
173 }
174}
175
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000176///////////////////////////////////////////////////////////////////////////////////////////////////
177
Mike Reed356f7c22017-01-10 11:58:39 -0500178CGContextRef SkCreateCGContext(const SkPixmap& pmap) {
179 CGBitmapInfo cg_bitmap_info = 0;
180 size_t bitsPerComponent = 0;
181 switch (pmap.colorType()) {
182 case kRGBA_8888_SkColorType:
183 bitsPerComponent = 8;
184 cg_bitmap_info = ComputeCGAlphaInfo_RGBA(pmap.alphaType());
185 break;
186 case kBGRA_8888_SkColorType:
187 bitsPerComponent = 8;
188 cg_bitmap_info = ComputeCGAlphaInfo_BGRA(pmap.alphaType());
189 break;
190 default:
191 return nullptr; // no other colortypes are supported (for now)
192 }
193
194 size_t rb = pmap.addr() ? pmap.rowBytes() : 0;
195 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
196 CGContextRef cg = CGBitmapContextCreate(pmap.writable_addr(), pmap.width(), pmap.height(),
197 bitsPerComponent, rb, cs, cg_bitmap_info);
198 CFRelease(cs);
199 return cg;
200}
201
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000202SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
203 CGImageRef image) {
204 CGBitmapInfo cg_bitmap_info = 0;
205 size_t bitsPerComponent = 0;
206 switch (info.colorType()) {
207 case kRGBA_8888_SkColorType:
208 bitsPerComponent = 8;
209 cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
210 break;
211 case kBGRA_8888_SkColorType:
212 bitsPerComponent = 8;
213 cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
214 break;
215 default:
216 return false; // no other colortypes are supported (for now)
217 }
218
219 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
220 CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
221 rowBytes, cs, cg_bitmap_info);
222 CFRelease(cs);
halcanary96fcdcc2015-08-27 07:41:13 -0700223 if (nullptr == cg) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000224 return false;
225 }
226
227 // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
228 // any blending (which could introduce errors and be slower).
229 CGContextSetBlendMode(cg, kCGBlendModeCopy);
230
231 CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
232 CGContextRelease(cg);
233 return true;
234}
235
Mike Reed463c8482016-12-21 12:01:12 -0500236bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image) {
237 const int width = SkToInt(CGImageGetWidth(image));
238 const int height = SkToInt(CGImageGetHeight(image));
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000239 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
240
241 SkBitmap tmp;
reed84825042014-09-02 12:50:45 -0700242 if (!tmp.tryAllocPixels(info)) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000243 return false;
244 }
245
246 if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
247 return false;
248 }
249
250 CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
251 switch (cgInfo) {
252 case kCGImageAlphaNone:
253 case kCGImageAlphaNoneSkipLast:
254 case kCGImageAlphaNoneSkipFirst:
255 SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
256 tmp.setAlphaType(kOpaque_SkAlphaType);
257 break;
258 default:
259 // we don't know if we're opaque or not, so compute it.
260 if (SkBitmap::ComputeIsOpaque(tmp)) {
261 tmp.setAlphaType(kOpaque_SkAlphaType);
262 }
263 }
264
265 *dst = tmp;
266 return true;
267}
mtklein1ee76512015-11-02 10:20:27 -0800268
Mike Reed463c8482016-12-21 12:01:12 -0500269sk_sp<SkImage> SkMakeImageFromCGImage(CGImageRef src) {
270 SkBitmap bm;
271 if (!SkCreateBitmapFromCGImage(&bm, src)) {
272 return nullptr;
273 }
274
275 bm.setImmutable();
276 return SkImage::MakeFromBitmap(bm);
277}
278
mtklein1ee76512015-11-02 10:20:27 -0800279#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)