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 kUnknown_SkAlphaType:
16             break;
17         case kOpaque_SkAlphaType:
18             info |= kCGImageAlphaNoneSkipLast;
19             break;
20         case kPremul_SkAlphaType:
21             info |= kCGImageAlphaPremultipliedLast;
22             break;
23         case kUnpremul_SkAlphaType:
24             info |= kCGImageAlphaLast;
25             break;
26     }
27     return info;
28 }
29 
ComputeCGAlphaInfo_BGRA(SkAlphaType at)30 static CGBitmapInfo ComputeCGAlphaInfo_BGRA(SkAlphaType at) {
31     CGBitmapInfo info = kCGBitmapByteOrder32Little;
32     switch (at) {
33         case kUnknown_SkAlphaType:
34             break;
35         case kOpaque_SkAlphaType:
36             info |= kCGImageAlphaNoneSkipFirst;
37             break;
38         case kPremul_SkAlphaType:
39             info |= kCGImageAlphaPremultipliedFirst;
40             break;
41         case kUnpremul_SkAlphaType:
42             info |= kCGImageAlphaFirst;
43             break;
44     }
45     return info;
46 }
47 
SkBitmap_ReleaseInfo(void * info,const void * pixelData,size_t size)48 static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
49     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
50     delete bitmap;
51 }
52 
getBitmapInfo(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info,bool * upscaleTo32)53 static bool getBitmapInfo(const SkBitmap& bm,
54                           size_t* bitsPerComponent,
55                           CGBitmapInfo* info,
56                           bool* upscaleTo32) {
57     if (upscaleTo32) {
58         *upscaleTo32 = false;
59     }
60 
61     switch (bm.colorType()) {
62         case kRGB_565_SkColorType:
63 #if 0
64             // doesn't see quite right. Are they thinking 1555?
65             *bitsPerComponent = 5;
66             *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
67 #else
68             if (upscaleTo32) {
69                 *upscaleTo32 = true;
70             }
71             // now treat like RGBA
72             *bitsPerComponent = 8;
73             *info = ComputeCGAlphaInfo_RGBA(kOpaque_SkAlphaType);
74 #endif
75             break;
76         case kRGBA_8888_SkColorType:
77             *bitsPerComponent = 8;
78             *info = ComputeCGAlphaInfo_RGBA(bm.alphaType());
79             break;
80         case kBGRA_8888_SkColorType:
81             *bitsPerComponent = 8;
82             *info = ComputeCGAlphaInfo_BGRA(bm.alphaType());
83             break;
84         case kARGB_4444_SkColorType:
85             *bitsPerComponent = 4;
86             *info = kCGBitmapByteOrder16Little;
87             if (bm.isOpaque()) {
88                 *info |= kCGImageAlphaNoneSkipLast;
89             } else {
90                 *info |= kCGImageAlphaPremultipliedLast;
91             }
92             break;
93         default:
94             return false;
95     }
96     return true;
97 }
98 
prepareForImageRef(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info)99 static SkBitmap* prepareForImageRef(const SkBitmap& bm,
100                                     size_t* bitsPerComponent,
101                                     CGBitmapInfo* info) {
102     bool upscaleTo32;
103     if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
104         return NULL;
105     }
106 
107     SkBitmap* copy;
108     if (upscaleTo32) {
109         copy = new SkBitmap;
110         // here we make a ceep copy of the pixels, since CG won't take our
111         // 565 directly
112         bm.copyTo(copy, kN32_SkColorType);
113     } else {
114         copy = new SkBitmap(bm);
115     }
116     return copy;
117 }
118 
SkCreateCGImageRefWithColorspace(const SkBitmap & bm,CGColorSpaceRef colorSpace)119 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
120                                             CGColorSpaceRef colorSpace) {
121     size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
122     CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
123 
124     SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
125     if (NULL == bitmap) {
126         return NULL;
127     }
128 
129     const int w = bitmap->width();
130     const int h = bitmap->height();
131     const size_t s = bitmap->getSize();
132 
133     // our provider "owns" the bitmap*, and will take care of deleting it
134     // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
135     // proc, which will in turn unlock the pixels
136     bitmap->lockPixels();
137     CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
138                                                              SkBitmap_ReleaseInfo);
139 
140     bool releaseColorSpace = false;
141     if (NULL == colorSpace) {
142         colorSpace = CGColorSpaceCreateDeviceRGB();
143         releaseColorSpace = true;
144     }
145 
146     CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
147                                    bitmap->bytesPerPixel() * 8,
148                                    bitmap->rowBytes(), colorSpace, info, dataRef,
149                                    NULL, false, kCGRenderingIntentDefault);
150 
151     if (releaseColorSpace) {
152         CGColorSpaceRelease(colorSpace);
153     }
154     CGDataProviderRelease(dataRef);
155     return ref;
156 }
157 
SkCGDrawBitmap(CGContextRef cg,const SkBitmap & bm,float x,float y)158 void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
159     CGImageRef img = SkCreateCGImageRef(bm);
160 
161     if (img) {
162         CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
163 
164         CGContextSaveGState(cg);
165         CGContextTranslateCTM(cg, x, r.size.height + y);
166         CGContextScaleCTM(cg, 1, -1);
167 
168         CGContextDrawImage(cg, r, img);
169 
170         CGContextRestoreGState(cg);
171 
172         CGImageRelease(img);
173     }
174 }
175 
176 ///////////////////////////////////////////////////////////////////////////////
177 
178 #include "SkStream.h"
179 
180 class SkAutoPDFRelease {
181 public:
SkAutoPDFRelease(CGPDFDocumentRef doc)182     SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {}
~SkAutoPDFRelease()183     ~SkAutoPDFRelease() {
184         if (fDoc) {
185             CGPDFDocumentRelease(fDoc);
186         }
187     }
188 private:
189     CGPDFDocumentRef fDoc;
190 };
191 #define SkAutoPDFRelease(...) SK_REQUIRE_LOCAL_VAR(SkAutoPDFRelease)
192 
SkPDFDocumentToBitmap(SkStream * stream,SkBitmap * output)193 bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) {
194     CGDataProviderRef data = SkCreateDataProviderFromStream(stream);
195     if (NULL == data) {
196         return false;
197     }
198 
199     CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data);
200     CGDataProviderRelease(data);
201     if (NULL == pdf) {
202         return false;
203     }
204     SkAutoPDFRelease releaseMe(pdf);
205 
206     CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
207     if (NULL == page) {
208         return false;
209     }
210 
211     CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
212 
213     int w = (int)CGRectGetWidth(bounds);
214     int h = (int)CGRectGetHeight(bounds);
215 
216     SkBitmap bitmap;
217     if (!bitmap.tryAllocN32Pixels(w, h)) {
218         return false;
219     }
220     bitmap.eraseColor(SK_ColorWHITE);
221 
222     size_t bitsPerComponent;
223     CGBitmapInfo info;
224     getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL);
225 
226     CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
227     CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h,
228                                              bitsPerComponent, bitmap.rowBytes(),
229                                              cs, info);
230     CGColorSpaceRelease(cs);
231 
232     if (ctx) {
233         CGContextDrawPDFPage(ctx, page);
234         CGContextRelease(ctx);
235     }
236 
237     output->swap(bitmap);
238     return true;
239 }
240 
241 ///////////////////////////////////////////////////////////////////////////////////////////////////
242 
SkCopyPixelsFromCGImage(const SkImageInfo & info,size_t rowBytes,void * pixels,CGImageRef image)243 SK_API bool SkCopyPixelsFromCGImage(const SkImageInfo& info, size_t rowBytes, void* pixels,
244                                     CGImageRef image) {
245     CGBitmapInfo cg_bitmap_info = 0;
246     size_t bitsPerComponent = 0;
247     switch (info.colorType()) {
248         case kRGBA_8888_SkColorType:
249             bitsPerComponent = 8;
250             cg_bitmap_info = ComputeCGAlphaInfo_RGBA(info.alphaType());
251             break;
252         case kBGRA_8888_SkColorType:
253             bitsPerComponent = 8;
254             cg_bitmap_info = ComputeCGAlphaInfo_BGRA(info.alphaType());
255             break;
256         default:
257             return false;   // no other colortypes are supported (for now)
258     }
259 
260     CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
261     CGContextRef cg = CGBitmapContextCreate(pixels, info.width(), info.height(), bitsPerComponent,
262                                             rowBytes, cs, cg_bitmap_info);
263     CFRelease(cs);
264     if (NULL == cg) {
265         return false;
266     }
267 
268     // use this blend mode, to avoid having to erase the pixels first, and to avoid CG performing
269     // any blending (which could introduce errors and be slower).
270     CGContextSetBlendMode(cg, kCGBlendModeCopy);
271 
272     CGContextDrawImage(cg, CGRectMake(0, 0, info.width(), info.height()), image);
273     CGContextRelease(cg);
274     return true;
275 }
276 
SkCreateBitmapFromCGImage(SkBitmap * dst,CGImageRef image,SkISize * scaleToFit)277 bool SkCreateBitmapFromCGImage(SkBitmap* dst, CGImageRef image, SkISize* scaleToFit) {
278     const int width = scaleToFit ? scaleToFit->width() : SkToInt(CGImageGetWidth(image));
279     const int height = scaleToFit ? scaleToFit->height() : SkToInt(CGImageGetHeight(image));
280     SkImageInfo info = SkImageInfo::MakeN32Premul(width, height);
281 
282     SkBitmap tmp;
283     if (!tmp.tryAllocPixels(info)) {
284         return false;
285     }
286 
287     if (!SkCopyPixelsFromCGImage(tmp.info(), tmp.rowBytes(), tmp.getPixels(), image)) {
288         return false;
289     }
290 
291     CGImageAlphaInfo cgInfo = CGImageGetAlphaInfo(image);
292     switch (cgInfo) {
293         case kCGImageAlphaNone:
294         case kCGImageAlphaNoneSkipLast:
295         case kCGImageAlphaNoneSkipFirst:
296             SkASSERT(SkBitmap::ComputeIsOpaque(tmp));
297             tmp.setAlphaType(kOpaque_SkAlphaType);
298             break;
299         default:
300             // we don't know if we're opaque or not, so compute it.
301             if (SkBitmap::ComputeIsOpaque(tmp)) {
302                 tmp.setAlphaType(kOpaque_SkAlphaType);
303             }
304     }
305 
306     *dst = tmp;
307     return true;
308 }
309