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