• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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