blob: 51a5ed9c033d5c6002b8eb44131a4cf98994d114 [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 */
reed@android.com0d55f1e2008-12-18 19:26:11 +00008#include "SkCGUtils.h"
reed@android.com758b1292008-12-18 17:54:12 +00009#include "SkBitmap.h"
reed@android.coma545a552009-06-29 17:07:19 +000010#include "SkColorPriv.h"
reed@android.com758b1292008-12-18 17:54:12 +000011
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000012static CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) {
13 CGBitmapInfo info = kCGBitmapByteOrder32Big;
14 switch (at) {
15 case kOpaque_SkAlphaType:
scroggo574290f2015-02-27 06:21:58 -080016 case kIgnore_SkAlphaType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000017 info |= kCGImageAlphaNoneSkipLast;
18 break;
19 case kPremul_SkAlphaType:
20 info |= kCGImageAlphaPremultipliedLast;
21 break;
22 case kUnpremul_SkAlphaType:
23 info |= kCGImageAlphaLast;
24 break;
25 }
26 return info;
27}
28
29static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
30 CGBitmapInfo info = kCGBitmapByteOrder32Little;
31 switch (at) {
32 case kOpaque_SkAlphaType:
scroggo574290f2015-02-27 06:21:58 -080033 case kIgnore_SkAlphaType:
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000034 info |= kCGImageAlphaNoneSkipFirst;
35 break;
36 case kPremul_SkAlphaType:
37 info |= kCGImageAlphaPremultipliedFirst;
38 break;
39 case kUnpremul_SkAlphaType:
40 info |= kCGImageAlphaFirst;
41 break;
42 }
43 return info;
44}
45
reed@android.com2b26cac2008-12-22 02:33:11 +000046static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
reed@android.com758b1292008-12-18 17:54:12 +000047 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
48 delete bitmap;
49}
50
rmistry@google.comd6176b02012-08-23 18:14:13 +000051static bool getBitmapInfo(const SkBitmap& bm,
reed@google.com9c16bc02011-06-28 21:52:34 +000052 size_t* bitsPerComponent,
53 CGBitmapInfo* info,
54 bool* upscaleTo32) {
55 if (upscaleTo32) {
56 *upscaleTo32 = false;
57 }
rmistry@google.comd6176b02012-08-23 18:14:13 +000058
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +000059 switch (bm.colorType()) {
60 case kRGB_565_SkColorType:
61#if 0
62 // doesn't see quite right. Are they thinking 1555?
63 *bitsPerComponent = 5;
64 *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000065#else
reed@google.com9c16bc02011-06-28 21:52:34 +000066 if (upscaleTo32) {
67 *upscaleTo32 = true;
68 }
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000069 // now treat like RGBA
reed@android.com758b1292008-12-18 17:54:12 +000070 *bitsPerComponent = 8;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000071 *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
reed@android.com8ede4922009-06-22 20:04:33 +000072#endif
reed@android.com758b1292008-12-18 17:54:12 +000073 break;
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +000074 case kRGBA_8888_SkColorType:
75 *bitsPerComponent = 8;
76 *info = ComputeCGAlphaInfo_RGBA(bm.alphaType());
77 break;
78 case kBGRA_8888_SkColorType:
79 *bitsPerComponent = 8;
80 *info = ComputeCGAlphaInfo_BGRA(bm.alphaType());
81 break;
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +000082 case kARGB_4444_SkColorType:
reed@android.com0680d6c2008-12-19 19:46:15 +000083 *bitsPerComponent = 4;
scroggo@google.com426648e2012-11-12 16:18:43 +000084 *info = kCGBitmapByteOrder16Little;
85 if (bm.isOpaque()) {
86 *info |= kCGImageAlphaNoneSkipLast;
87 } else {
88 *info |= kCGImageAlphaPremultipliedLast;
89 }
reed@android.com0680d6c2008-12-19 19:46:15 +000090 break;
reed@android.com758b1292008-12-18 17:54:12 +000091 default:
reed@google.com9c16bc02011-06-28 21:52:34 +000092 return false;
93 }
94 return true;
95}
96
97static SkBitmap* prepareForImageRef(const SkBitmap& bm,
98 size_t* bitsPerComponent,
99 CGBitmapInfo* info) {
100 bool upscaleTo32;
101 if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
102 return NULL;
reed@android.com758b1292008-12-18 17:54:12 +0000103 }
104
reed@android.com32a42492009-07-10 03:33:52 +0000105 SkBitmap* copy;
106 if (upscaleTo32) {
107 copy = new SkBitmap;
108 // here we make a ceep copy of the pixels, since CG won't take our
109 // 565 directly
commit-bot@chromium.org28fcae22014-04-11 17:15:40 +0000110 bm.copyTo(copy, kN32_SkColorType);
reed@android.com32a42492009-07-10 03:33:52 +0000111 } else {
112 copy = new SkBitmap(bm);
113 }
114 return copy;
reed@android.com758b1292008-12-18 17:54:12 +0000115}
116
reed@android.com38669c12011-01-03 13:48:50 +0000117CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
118 CGColorSpaceRef colorSpace) {
119 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
120 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
reed@android.com758b1292008-12-18 17:54:12 +0000121
122 SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
123 if (NULL == bitmap) {
124 return NULL;
125 }
126
127 const int w = bitmap->width();
128 const int h = bitmap->height();
129 const size_t s = bitmap->getSize();
130
reed@android.com758b1292008-12-18 17:54:12 +0000131 // our provider "owns" the bitmap*, and will take care of deleting it
rmistry@google.comd6176b02012-08-23 18:14:13 +0000132 // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
133 // proc, which will in turn unlock the pixels
134 bitmap->lockPixels();
reed@android.com2b26cac2008-12-22 02:33:11 +0000135 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
rmistry@google.comd6176b02012-08-23 18:14:13 +0000136 SkBitmap_ReleaseInfo);
reed@android.com2b26cac2008-12-22 02:33:11 +0000137
reed@android.com38669c12011-01-03 13:48:50 +0000138 bool releaseColorSpace = false;
139 if (NULL == colorSpace) {
reed@google.comc280d112011-01-05 16:07:35 +0000140 colorSpace = CGColorSpaceCreateDeviceRGB();
reed@android.com38669c12011-01-03 13:48:50 +0000141 releaseColorSpace = true;
142 }
143
reed@android.com758b1292008-12-18 17:54:12 +0000144 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
145 bitmap->bytesPerPixel() * 8,
reed@android.com38669c12011-01-03 13:48:50 +0000146 bitmap->rowBytes(), colorSpace, info, dataRef,
reed@android.com758b1292008-12-18 17:54:12 +0000147 NULL, false, kCGRenderingIntentDefault);
reed@android.com38669c12011-01-03 13:48:50 +0000148
149 if (releaseColorSpace) {
150 CGColorSpaceRelease(colorSpace);
151 }
reed@android.com758b1292008-12-18 17:54:12 +0000152 CGDataProviderRelease(dataRef);
153 return ref;
154}
155
reed@android.comf2b98d62010-12-20 18:26:13 +0000156void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
157 CGImageRef img = SkCreateCGImageRef(bm);
158
159 if (img) {
160 CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
reed@google.com62f46592011-01-05 15:49:43 +0000161
reed@android.comf2b98d62010-12-20 18:26:13 +0000162 CGContextSaveGState(cg);
163 CGContextTranslateCTM(cg, x, r.size.height + y);
164 CGContextScaleCTM(cg, 1, -1);
reed@google.com62f46592011-01-05 15:49:43 +0000165
reed@android.comf2b98d62010-12-20 18:26:13 +0000166 CGContextDrawImage(cg, r, img);
reed@google.com62f46592011-01-05 15:49:43 +0000167
reed@android.comf2b98d62010-12-20 18:26:13 +0000168 CGContextRestoreGState(cg);
reed@google.com62f46592011-01-05 15:49:43 +0000169
reed@android.comf2b98d62010-12-20 18:26:13 +0000170 CGImageRelease(img);
171 }
172}
173
reed@google.com292ade62011-06-28 20:54:03 +0000174///////////////////////////////////////////////////////////////////////////////
reed@android.comf2b98d62010-12-20 18:26:13 +0000175
reed@google.com292ade62011-06-28 20:54:03 +0000176#include "SkStream.h"
177
178class SkAutoPDFRelease {
179public:
180 SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {}
181 ~SkAutoPDFRelease() {
182 if (fDoc) {
183 CGPDFDocumentRelease(fDoc);
184 }
185 }
186private:
187 CGPDFDocumentRef fDoc;
188};
commit-bot@chromium.orge61a86c2013-11-18 16:03:59 +0000189#define SkAutoPDFRelease(...) SK_REQUIRE_LOCAL_VAR(SkAutoPDFRelease)
reed@google.com292ade62011-06-28 20:54:03 +0000190
reed@google.com292ade62011-06-28 20:54:03 +0000191bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) {
halcanaryc8306772014-10-27 07:06:47 -0700192 CGDataProviderRef data = SkCreateDataProviderFromStream(stream);
reed@google.com292ade62011-06-28 20:54:03 +0000193 if (NULL == data) {
194 return false;
195 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000196
reed@google.com292ade62011-06-28 20:54:03 +0000197 CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data);
198 CGDataProviderRelease(data);
199 if (NULL == pdf) {
200 return false;
201 }
202 SkAutoPDFRelease releaseMe(pdf);
203
204 CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
205 if (NULL == page) {
206 return false;
207 }
rmistry@google.comd6176b02012-08-23 18:14:13 +0000208
reed@google.com292ade62011-06-28 20:54:03 +0000209 CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000210
reed@google.com292ade62011-06-28 20:54:03 +0000211 int w = (int)CGRectGetWidth(bounds);
212 int h = (int)CGRectGetHeight(bounds);
rmistry@google.comd6176b02012-08-23 18:14:13 +0000213
reed@google.com292ade62011-06-28 20:54:03 +0000214 SkBitmap bitmap;
reed84825042014-09-02 12:50:45 -0700215 if (!bitmap.tryAllocN32Pixels(w, h)) {
reed@google.com9ebcac52014-01-24 18:53:42 +0000216 return false;
217 }
reed@google.com292ade62011-06-28 20:54:03 +0000218 bitmap.eraseColor(SK_ColorWHITE);
reed@google.com9c16bc02011-06-28 21:52:34 +0000219
220 size_t bitsPerComponent;
221 CGBitmapInfo info;
rmistry@google.comd6176b02012-08-23 18:14:13 +0000222 getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL);
reed@google.comcd88d7c2011-06-29 20:12:38 +0000223
224 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
225 CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h,
226 bitsPerComponent, bitmap.rowBytes(),
227 cs, info);
228 CGColorSpaceRelease(cs);
229
reed@google.com292ade62011-06-28 20:54:03 +0000230 if (ctx) {
231 CGContextDrawPDFPage(ctx, page);
232 CGContextRelease(ctx);
233 }
234
235 output->swap(bitmap);
236 return true;
237}
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000238
239///////////////////////////////////////////////////////////////////////////////////////////////////
240
241SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
242 CGImageRef image) {
243 CGBitmapInfo cg_bitmap_info = 0;
244 size_t bitsPerComponent = 0;
245 switch (info.colorType()) {
246 case kRGBA_8888_SkColorType:
247 bitsPerComponent = 8;
248 cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
249 break;
250 case kBGRA_8888_SkColorType:
251 bitsPerComponent = 8;
252 cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
253 break;
254 default:
255 return false; // no other colortypes are supported (for now)
256 }
257
258 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
259 CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
260 rowBytes, cs, cg_bitmap_info);
261 CFRelease(cs);
262 if (NULL == cg) {
263 return false;
264 }
265
266 // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
267 // any blending (which could introduce errors and be slower).
268 CGContextSetBlendMode(cg, kCGBlendModeCopy);
269
270 CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
271 CGContextRelease(cg);
272 return true;
273}
274
275bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image, SkISize* scaleToFit) {
276 const int width = scaleToFit ? scaleToFit->width() : SkToInt(CGImageGetWidth(image));
277 const int height = scaleToFit ? scaleToFit->height() : SkToInt(CGImageGetHeight(image));
278 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
279
280 SkBitmap tmp;
reed84825042014-09-02 12:50:45 -0700281 if (!tmp.tryAllocPixels(info)) {
commit-bot@chromium.org60b5dce2014-04-22 20:24:33 +0000282 return false;
283 }
284
285 if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
286 return false;
287 }
288
289 CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
290 switch (cgInfo) {
291 case kCGImageAlphaNone:
292 case kCGImageAlphaNoneSkipLast:
293 case kCGImageAlphaNoneSkipFirst:
294 SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
295 tmp.setAlphaType(kOpaque_SkAlphaType);
296 break;
297 default:
298 // we don't know if we're opaque or not, so compute it.
299 if (SkBitmap::ComputeIsOpaque(tmp)) {
300 tmp.setAlphaType(kOpaque_SkAlphaType);
301 }
302 }
303
304 *dst = tmp;
305 return true;
306}