blob: 3fd9057c35511c2e974ea57fc681bcdc376da4eb [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2011 Google Inc.
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
mtklein1ee76512015-11-02 10:20:27 -08008
9#include "SkTypes.h"
10#if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
11
reed@android.com0d55f1e2008-12-18 19:26:11 +000012#include "SkCGUtils.h"
reed@android.com758b1292008-12-18 17:54:12 +000013#include "SkBitmap.h"
reed@android.coma545a552009-06-29 17:07:19 +000014#include "SkColorPriv.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;
114 // here we make a ceep copy of the pixels, since CG won't take our
115 // 565 directly
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000116 bm.copyTo(copy, kN32_SkColorType);
reed@android.com32a42492009-07-10 03:33:52 +0000117 } else {
118 copy = new SkBitmap(bm);
119 }
120 return copy;
reed@android.com758b1292008-12-18 17:54:12 +0000121}
122
reed@android.com38669c12011-01-03 13:48:50 +0000123CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
124 CGColorSpaceRef colorSpace) {
125 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
126 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
reed@android.com758b1292008-12-18 17:54:12 +0000127
128 SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
halcanary96fcdcc2015-08-27 07:41:13 -0700129 if (nullptr == bitmap) {
130 return nullptr;
reed@android.com758b1292008-12-18 17:54:12 +0000131 }
132
133 const int w = bitmap->width();
134 const int h = bitmap->height();
135 const size_t s = bitmap->getSize();
136
reed@android.com758b1292008-12-18 17:54:12 +0000137 // our provider "owns" the bitmap*, and will take care of deleting it
rmistry@google.comd6176b02012-08-23 18:14:13 +0000138 // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
139 // proc, which will in turn unlock the pixels
140 bitmap->lockPixels();
reed@android.com2b26cac2008-12-22 02:33:11 +0000141 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000142 SkBitmap_ReleaseInfo);
reed@android.com2b26cac2008-12-22 02:33:11 +0000143
reed@android.com38669c12011-01-03 13:48:50 +0000144 bool releaseColorSpace = false;
halcanary96fcdcc2015-08-27 07:41:13 -0700145 if (nullptr == colorSpace) {
reed@google.comc280d112011-01-05 16:07:35 +0000146 colorSpace = CGColorSpaceCreateDeviceRGB();
reed@android.com38669c12011-01-03 13:48:50 +0000147 releaseColorSpace = true;
148 }
149
reed@android.com758b1292008-12-18 17:54:12 +0000150 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
151 bitmap->bytesPerPixel() * 8,
reed@android.com38669c12011-01-03 13:48:50 +0000152 bitmap->rowBytes(), colorSpace, info, dataRef,
halcanary96fcdcc2015-08-27 07:41:13 -0700153 nullptr, false, kCGRenderingIntentDefault);
reed@android.com38669c12011-01-03 13:48:50 +0000154
155 if (releaseColorSpace) {
156 CGColorSpaceRelease(colorSpace);
157 }
reed@android.com758b1292008-12-18 17:54:12 +0000158 CGDataProviderRelease(dataRef);
159 return ref;
160}
161
reed@android.comf2b98d62010-12-20 18:26:13 +0000162void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
163 CGImageRef img = SkCreateCGImageRef(bm);
164
165 if (img) {
166 CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
reed@google.com62f46592011-01-05 15:49:43 +0000167
reed@android.comf2b98d62010-12-20 18:26:13 +0000168 CGContextSaveGState(cg);
169 CGContextTranslateCTM(cg, x, r.size.height + y);
170 CGContextScaleCTM(cg, 1, -1);
reed@google.com62f46592011-01-05 15:49:43 +0000171
reed@android.comf2b98d62010-12-20 18:26:13 +0000172 CGContextDrawImage(cg, r, img);
reed@google.com62f46592011-01-05 15:49:43 +0000173
reed@android.comf2b98d62010-12-20 18:26:13 +0000174 CGContextRestoreGState(cg);
reed@google.com62f46592011-01-05 15:49:43 +0000175
reed@android.comf2b98d62010-12-20 18:26:13 +0000176 CGImageRelease(img);
177 }
178}
179
reed@google.com292ade62011-06-28 20:54:03 +0000180///////////////////////////////////////////////////////////////////////////////
reed@android.comf2b98d62010-12-20 18:26:13 +0000181
reed@google.com292ade62011-06-28 20:54:03 +0000182#include "SkStream.h"
183
184class SkAutoPDFRelease {
185public:
186 SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {}
187 ~SkAutoPDFRelease() {
188 if (fDoc) {
189 CGPDFDocumentRelease(fDoc);
190 }
191 }
192private:
193 CGPDFDocumentRef fDoc;
194};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000195#define SkAutoPDFRelease(...) SK_REQUIRE_LOCAL_VAR(SkAutoPDFRelease)
reed@google.com292ade62011-06-28 20:54:03 +0000196
reed@google.com292ade62011-06-28 20:54:03 +0000197bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) {
halcanaryc8306772014-10-27 07:06:47 -0700198 CGDataProviderRef data = SkCreateDataProviderFromStream(stream);
halcanary96fcdcc2015-08-27 07:41:13 -0700199 if (nullptr == data) {
reed@google.com292ade62011-06-28 20:54:03 +0000200 return false;
201 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000202
reed@google.com292ade62011-06-28 20:54:03 +0000203 CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data);
204 CGDataProviderRelease(data);
halcanary96fcdcc2015-08-27 07:41:13 -0700205 if (nullptr == pdf) {
reed@google.com292ade62011-06-28 20:54:03 +0000206 return false;
207 }
208 SkAutoPDFRelease releaseMe(pdf);
209
210 CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
halcanary96fcdcc2015-08-27 07:41:13 -0700211 if (nullptr == page) {
reed@google.com292ade62011-06-28 20:54:03 +0000212 return false;
213 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000214
reed@google.com292ade62011-06-28 20:54:03 +0000215 CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000216
reed@google.com292ade62011-06-28 20:54:03 +0000217 int w = (int)CGRectGetWidth(bounds);
218 int h = (int)CGRectGetHeight(bounds);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000219
reed@google.com292ade62011-06-28 20:54:03 +0000220 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -0700221 if (!bitmap.tryAllocN32Pixels(w, h)) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000222 return false;
223 }
reed@google.com292ade62011-06-28 20:54:03 +0000224 bitmap.eraseColor(SK_ColorWHITE);
reed@google.com9c16bc02011-06-28 21:52:34 +0000225
226 size_t bitsPerComponent;
227 CGBitmapInfo info;
halcanary96fcdcc2015-08-27 07:41:13 -0700228 getBitmapInfo(bitmap, &bitsPerComponent, &info, nullptr);
reed@google.comcd88d7c2011-06-29 20:12:38 +0000229
230 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
231 CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h,
232 bitsPerComponent, bitmap.rowBytes(),
233 cs, info);
234 CGColorSpaceRelease(cs);
235
reed@google.com292ade62011-06-28 20:54:03 +0000236 if (ctx) {
237 CGContextDrawPDFPage(ctx, page);
238 CGContextRelease(ctx);
239 }
240
241 output->swap(bitmap);
242 return true;
243}
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000244
245///////////////////////////////////////////////////////////////////////////////////////////////////
246
247SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
248 CGImageRef image) {
249 CGBitmapInfo cg_bitmap_info = 0;
250 size_t bitsPerComponent = 0;
251 switch (info.colorType()) {
252 case kRGBA_8888_SkColorType:
253 bitsPerComponent = 8;
254 cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
255 break;
256 case kBGRA_8888_SkColorType:
257 bitsPerComponent = 8;
258 cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
259 break;
260 default:
261 return false; // no other colortypes are supported (for now)
262 }
263
264 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
265 CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
266 rowBytes, cs, cg_bitmap_info);
267 CFRelease(cs);
halcanary96fcdcc2015-08-27 07:41:13 -0700268 if (nullptr == cg) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000269 return false;
270 }
271
272 // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
273 // any blending (which could introduce errors and be slower).
274 CGContextSetBlendMode(cg, kCGBlendModeCopy);
275
276 CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
277 CGContextRelease(cg);
278 return true;
279}
280
281bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image, SkISize* scaleToFit) {
282 const int width = scaleToFit ? scaleToFit->width() : SkToInt(CGImageGetWidth(image));
283 const int height = scaleToFit ? scaleToFit->height() : SkToInt(CGImageGetHeight(image));
284 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
285
286 SkBitmap tmp;
reed84825042014-09-02 12:50:45 -0700287 if (!tmp.tryAllocPixels(info)) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000288 return false;
289 }
290
291 if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
292 return false;
293 }
294
295 CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
296 switch (cgInfo) {
297 case kCGImageAlphaNone:
298 case kCGImageAlphaNoneSkipLast:
299 case kCGImageAlphaNoneSkipFirst:
300 SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
301 tmp.setAlphaType(kOpaque_SkAlphaType);
302 break;
303 default:
304 // we don't know if we're opaque or not, so compute it.
305 if (SkBitmap::ComputeIsOpaque(tmp)) {
306 tmp.setAlphaType(kOpaque_SkAlphaType);
307 }
308 }
309
310 *dst = tmp;
311 return true;
312}
mtklein1ee76512015-11-02 10:20:27 -0800313
314#endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)