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
9 #include "SkTypes.h"
10 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
11
12 #include "SkCGUtils.h"
13 #include "SkBitmap.h"
14 #include "SkColorPriv.h"
15
ComputeCGAlphaInfo_RGBA(SkAlphaType at)16 static CGBitmapInfo ComputeCGAlphaInfo_RGBA(SkAlphaType at) {
17 CGBitmapInfo info = kCGBitmapByteOrder32Big;
18 switch (at) {
19 case kUnknown_SkAlphaType:
20 break;
21 case kOpaque_SkAlphaType:
22 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
ComputeCGAlphaInfo_BGRA(SkAlphaType at)34 static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
35 CGBitmapInfo info = kCGBitmapByteOrder32Little;
36 switch (at) {
37 case kUnknown_SkAlphaType:
38 break;
39 case kOpaque_SkAlphaType:
40 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
SkBitmap_ReleaseInfo(void * info,const void * pixelData,size_t size)52 static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
53 SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
54 delete bitmap;
55 }
56
getBitmapInfo(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info,bool * upscaleTo32)57 static bool getBitmapInfo(const SkBitmap& bm,
58 size_t* bitsPerComponent,
59 CGBitmapInfo* info,
60 bool* upscaleTo32) {
61 if (upscaleTo32) {
62 *upscaleTo32 = false;
63 }
64
65 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;
71 #else
72 if (upscaleTo32) {
73 *upscaleTo32 = true;
74 }
75 // now treat like RGBA
76 *bitsPerComponent = 8;
77 *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
78 #endif
79 break;
80 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;
88 case kARGB_4444_SkColorType:
89 *bitsPerComponent = 4;
90 *info = kCGBitmapByteOrder16Little;
91 if (bm.isOpaque()) {
92 *info |= kCGImageAlphaNoneSkipLast;
93 } else {
94 *info |= kCGImageAlphaPremultipliedLast;
95 }
96 break;
97 default:
98 return false;
99 }
100 return true;
101 }
102
prepareForImageRef(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info)103 static SkBitmap* prepareForImageRef(const SkBitmap& bm,
104 size_t* bitsPerComponent,
105 CGBitmapInfo* info) {
106 bool upscaleTo32;
107 if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
108 return nullptr;
109 }
110
111 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
116 bm.copyTo(copy, kN32_SkColorType);
117 } else {
118 copy = new SkBitmap(bm);
119 }
120 return copy;
121 }
122
SkCreateCGImageRefWithColorspace(const SkBitmap & bm,CGColorSpaceRef colorSpace)123 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
124 CGColorSpaceRef colorSpace) {
125 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
126 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING;
127
128 SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
129 if (nullptr == bitmap) {
130 return nullptr;
131 }
132
133 const int w = bitmap->width();
134 const int h = bitmap->height();
135 const size_t s = bitmap->getSize();
136
137 // our provider "owns" the bitmap*, and will take care of deleting it
138 // 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();
141 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
142 SkBitmap_ReleaseInfo);
143
144 bool releaseColorSpace = false;
145 if (nullptr == colorSpace) {
146 colorSpace = CGColorSpaceCreateDeviceRGB();
147 releaseColorSpace = true;
148 }
149
150 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
151 bitmap->bytesPerPixel() * 8,
152 bitmap->rowBytes(), colorSpace, info, dataRef,
153 nullptr, false, kCGRenderingIntentDefault);
154
155 if (releaseColorSpace) {
156 CGColorSpaceRelease(colorSpace);
157 }
158 CGDataProviderRelease(dataRef);
159 return ref;
160 }
161
SkCGDrawBitmap(CGContextRef cg,const SkBitmap & bm,float x,float y)162 void 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());
167
168 CGContextSaveGState(cg);
169 CGContextTranslateCTM(cg, x, r.size.height + y);
170 CGContextScaleCTM(cg, 1, -1);
171
172 CGContextDrawImage(cg, r, img);
173
174 CGContextRestoreGState(cg);
175
176 CGImageRelease(img);
177 }
178 }
179
180 ///////////////////////////////////////////////////////////////////////////////
181
182 #include "SkStream.h"
183
184 class SkAutoPDFRelease {
185 public:
SkAutoPDFRelease(CGPDFDocumentRef doc)186 SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {}
~SkAutoPDFRelease()187 ~SkAutoPDFRelease() {
188 if (fDoc) {
189 CGPDFDocumentRelease(fDoc);
190 }
191 }
192 private:
193 CGPDFDocumentRef fDoc;
194 };
195 #define SkAutoPDFRelease(...) SK_REQUIRE_LOCAL_VAR(SkAutoPDFRelease)
196
SkPDFDocumentToBitmap(SkStream * stream,SkBitmap * output)197 bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) {
198 CGDataProviderRef data = SkCreateDataProviderFromStream(stream);
199 if (nullptr == data) {
200 return false;
201 }
202
203 CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data);
204 CGDataProviderRelease(data);
205 if (nullptr == pdf) {
206 return false;
207 }
208 SkAutoPDFRelease releaseMe(pdf);
209
210 CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
211 if (nullptr == page) {
212 return false;
213 }
214
215 CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
216
217 int w = (int)CGRectGetWidth(bounds);
218 int h = (int)CGRectGetHeight(bounds);
219
220 SkBitmap bitmap;
221 if (!bitmap.tryAllocN32Pixels(w, h)) {
222 return false;
223 }
224 bitmap.eraseColor(SK_ColorWHITE);
225
226 size_t bitsPerComponent;
227 CGBitmapInfo info;
228 getBitmapInfo(bitmap, &bitsPerComponent, &info, nullptr);
229
230 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
231 CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h,
232 bitsPerComponent, bitmap.rowBytes(),
233 cs, info);
234 CGColorSpaceRelease(cs);
235
236 if (ctx) {
237 CGContextDrawPDFPage(ctx, page);
238 CGContextRelease(ctx);
239 }
240
241 output->swap(bitmap);
242 return true;
243 }
244
245 ///////////////////////////////////////////////////////////////////////////////////////////////////
246
SkCopyPixelsFromCGImage(const SkImageInfo & info,size_t rowBytes,void * pixels,CGImageRef image)247 SK_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);
268 if (nullptr == cg) {
269 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
SkCreateBitmapFromCGImage(SkBitmap * dst,CGImageRef image,SkISize * scaleToFit)281 bool 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;
287 if (!tmp.tryAllocPixels(info)) {
288 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 }
313
314 #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS)
315