1
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 */
8 #include "SkCGUtils.h"
9 #include "SkBitmap.h"
10 #include "SkColorPriv.h"
11
ComputeCGAlphaInfo_RGBA(SkAlphaType at)12 static CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) {
13 CGBitmapInfo info = kCGBitmapByteOrder32Big;
14 switch (at) {
15 case kOpaque_SkAlphaType:
16 case kIgnore_SkAlphaType:
17 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
ComputeCGAlphaInfo_BGRA(SkAlphaType at)29 static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
30 CGBitmapInfo info = kCGBitmapByteOrder32Little;
31 switch (at) {
32 case kOpaque_SkAlphaType:
33 case kIgnore_SkAlphaType:
34 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
SkBitmap_ReleaseInfo(void * info,const void * pixelData,size_t size)46 static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
47 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
48 delete bitmap;
49 }
50
getBitmapInfo(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info,bool * upscaleTo32)51 static bool getBitmapInfo(const SkBitmap& bm,
52 size_t* bitsPerComponent,
53 CGBitmapInfo* info,
54 bool* upscaleTo32) {
55 if (upscaleTo32) {
56 *upscaleTo32 = false;
57 }
58
59 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;
65 #else
66 if (upscaleTo32) {
67 *upscaleTo32 = true;
68 }
69 // now treat like RGBA
70 *bitsPerComponent = 8;
71 *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
72 #endif
73 break;
74 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;
82 case kARGB_4444_SkColorType:
83 *bitsPerComponent = 4;
84 *info = kCGBitmapByteOrder16Little;
85 if (bm.isOpaque()) {
86 *info |= kCGImageAlphaNoneSkipLast;
87 } else {
88 *info |= kCGImageAlphaPremultipliedLast;
89 }
90 break;
91 default:
92 return false;
93 }
94 return true;
95 }
96
prepareForImageRef(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info)97 static 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;
103 }
104
105 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
110 bm.copyTo(copy, kN32_SkColorType);
111 } else {
112 copy = new SkBitmap(bm);
113 }
114 return copy;
115 }
116
SkCreateCGImageRefWithColorspace(const SkBitmap & bm,CGColorSpaceRef colorSpace)117 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
118 CGColorSpaceRef colorSpace) {
119 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
120 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
121
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
131 // our provider "owns" the bitmap*, and will take care of deleting it
132 // 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();
135 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
136 SkBitmap_ReleaseInfo);
137
138 bool releaseColorSpace = false;
139 if (NULL == colorSpace) {
140 colorSpace = CGColorSpaceCreateDeviceRGB();
141 releaseColorSpace = true;
142 }
143
144 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
145 bitmap->bytesPerPixel() * 8,
146 bitmap->rowBytes(), colorSpace, info, dataRef,
147 NULL, false, kCGRenderingIntentDefault);
148
149 if (releaseColorSpace) {
150 CGColorSpaceRelease(colorSpace);
151 }
152 CGDataProviderRelease(dataRef);
153 return ref;
154 }
155
SkCGDrawBitmap(CGContextRef cg,const SkBitmap & bm,float x,float y)156 void 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());
161
162 CGContextSaveGState(cg);
163 CGContextTranslateCTM(cg, x, r.size.height + y);
164 CGContextScaleCTM(cg, 1, -1);
165
166 CGContextDrawImage(cg, r, img);
167
168 CGContextRestoreGState(cg);
169
170 CGImageRelease(img);
171 }
172 }
173
174 ///////////////////////////////////////////////////////////////////////////////
175
176 #include "SkStream.h"
177
178 class SkAutoPDFRelease {
179 public:
SkAutoPDFRelease(CGPDFDocumentRef doc)180 SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {}
~SkAutoPDFRelease()181 ~SkAutoPDFRelease() {
182 if (fDoc) {
183 CGPDFDocumentRelease(fDoc);
184 }
185 }
186 private:
187 CGPDFDocumentRef fDoc;
188 };
189 #define SkAutoPDFRelease(...) SK_REQUIRE_LOCAL_VAR(SkAutoPDFRelease)
190
CGDataProviderReleaseData_FromMalloc(void *,const void * data,size_t size)191 static void CGDataProviderReleaseData_FromMalloc(void*, const void* data,
192 size_t size) {
193 sk_free((void*)data);
194 }
195
SkPDFDocumentToBitmap(SkStream * stream,SkBitmap * output)196 bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) {
197 size_t size = stream->getLength();
198 void* ptr = sk_malloc_throw(size);
199 stream->read(ptr, size);
200 CGDataProviderRef data = CGDataProviderCreateWithData(NULL, ptr, size,
201 CGDataProviderReleaseData_FromMalloc);
202 if (NULL == data) {
203 return false;
204 }
205
206 CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data);
207 CGDataProviderRelease(data);
208 if (NULL == pdf) {
209 return false;
210 }
211 SkAutoPDFRelease releaseMe(pdf);
212
213 CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
214 if (NULL == page) {
215 return false;
216 }
217
218 CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
219
220 int w = (int)CGRectGetWidth(bounds);
221 int h = (int)CGRectGetHeight(bounds);
222
223 SkBitmap bitmap;
224 if (!bitmap.allocPixels(SkImageInfo::MakeN32Premul(w, h))) {
225 return false;
226 }
227 bitmap.eraseColor(SK_ColorWHITE);
228
229 size_t bitsPerComponent;
230 CGBitmapInfo info;
231 getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL);
232
233 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
234 CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h,
235 bitsPerComponent, bitmap.rowBytes(),
236 cs, info);
237 CGColorSpaceRelease(cs);
238
239 if (ctx) {
240 CGContextDrawPDFPage(ctx, page);
241 CGContextRelease(ctx);
242 }
243
244 output->swap(bitmap);
245 return true;
246 }
247
248 ///////////////////////////////////////////////////////////////////////////////////////////////////
249
SkCopyPixelsFromCGImage(const SkImageInfo & info,size_t rowBytes,void * pixels,CGImageRef image)250 SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
251 CGImageRef image) {
252 CGBitmapInfo cg_bitmap_info = 0;
253 size_t bitsPerComponent = 0;
254 switch (info.colorType()) {
255 case kRGBA_8888_SkColorType:
256 bitsPerComponent = 8;
257 cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
258 break;
259 case kBGRA_8888_SkColorType:
260 bitsPerComponent = 8;
261 cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
262 break;
263 default:
264 return false; // no other colortypes are supported (for now)
265 }
266
267 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
268 CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
269 rowBytes, cs, cg_bitmap_info);
270 CFRelease(cs);
271 if (NULL == cg) {
272 return false;
273 }
274
275 // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
276 // any blending (which could introduce errors and be slower).
277 CGContextSetBlendMode(cg, kCGBlendModeCopy);
278
279 CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
280 CGContextRelease(cg);
281 return true;
282 }
283
SkCreateBitmapFromCGImage(SkBitmap * dst,CGImageRef image,SkISize * scaleToFit)284 bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image, SkISize* scaleToFit) {
285 const int width = scaleToFit ? scaleToFit->width() : SkToInt(CGImageGetWidth(image));
286 const int height = scaleToFit ? scaleToFit->height() : SkToInt(CGImageGetHeight(image));
287 SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
288
289 SkBitmap tmp;
290 if (!tmp.allocPixels(info)) {
291 return false;
292 }
293
294 if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
295 return false;
296 }
297
298 CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
299 switch (cgInfo) {
300 case kCGImageAlphaNone:
301 case kCGImageAlphaNoneSkipLast:
302 case kCGImageAlphaNoneSkipFirst:
303 SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
304 tmp.setAlphaType(kOpaque_SkAlphaType);
305 break;
306 default:
307 // we don't know if we're opaque or not, so compute it.
308 if (SkBitmap::ComputeIsOpaque(tmp)) {
309 tmp.setAlphaType(kOpaque_SkAlphaType);
310 }
311 }
312
313 *dst = tmp;
314 return true;
315 }
316