• 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 
SkBitmap_ReleaseInfo(void * info,const void * pixelData,size_t size)12 static void SkBitmap_ReleaseInfo(void* info, const void* pixelData, size_t size) {
13     SkBitmap* bitmap = reinterpret_cast<SkBitmap*>(info);
14     delete bitmap;
15 }
16 
17 #define HAS_ARGB_SHIFTS(a, r, g, b) \
18     (SK_A32_SHIFT == (a) && SK_R32_SHIFT == (r) \
19     && SK_G32_SHIFT == (g) && SK_B32_SHIFT == (b))
20 
getBitmapInfo(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info,bool * upscaleTo32)21 static bool getBitmapInfo(const SkBitmap& bm,
22                           size_t* bitsPerComponent,
23                           CGBitmapInfo* info,
24                           bool* upscaleTo32) {
25     if (upscaleTo32) {
26         *upscaleTo32 = false;
27     }
28 
29     switch (bm.config()) {
30         case SkBitmap::kRGB_565_Config:
31             if (upscaleTo32) {
32                 *upscaleTo32 = true;
33             }
34             // fall through
35         case SkBitmap::kARGB_8888_Config:
36             *bitsPerComponent = 8;
37 #if defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 0, 8, 16) \
38 || defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(0, 24, 16, 8)
39             *info = kCGBitmapByteOrder32Big;
40             if (bm.isOpaque()) {
41                 *info |= kCGImageAlphaNoneSkipLast;
42             } else {
43                 *info |= kCGImageAlphaPremultipliedLast;
44             }
45 #elif defined(SK_CPU_LENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0) \
46 || defined(SK_CPU_BENDIAN) && HAS_ARGB_SHIFTS(24, 16, 8, 0)
47             // Matches the CGBitmapInfo that Apple recommends for best
48             // performance, used by google chrome.
49             *info = kCGBitmapByteOrder32Little;
50             if (bm.isOpaque()) {
51                 *info |= kCGImageAlphaNoneSkipFirst;
52             } else {
53                 *info |= kCGImageAlphaPremultipliedFirst;
54             }
55 #else
56             // ...add more formats as required...
57 #warning Cannot convert SkBitmap to CGImageRef with these shiftmasks. \
58 This will probably not work.
59             // Legacy behavior. Perhaps turn this into an error at some
60             // point.
61             *info = kCGBitmapByteOrder32Big;
62             if (bm.isOpaque()) {
63                 *info |= kCGImageAlphaNoneSkipLast;
64             } else {
65                 *info |= kCGImageAlphaPremultipliedLast;
66             }
67 #endif
68             break;
69 #if 0
70         case SkBitmap::kRGB_565_Config:
71             // doesn't see quite right. Are they thinking 1555?
72             *bitsPerComponent = 5;
73             *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone;
74             break;
75 #endif
76         case SkBitmap::kARGB_4444_Config:
77             *bitsPerComponent = 4;
78             *info = kCGBitmapByteOrder16Little;
79             if (bm.isOpaque()) {
80                 *info |= kCGImageAlphaNoneSkipLast;
81             } else {
82                 *info |= kCGImageAlphaPremultipliedLast;
83             }
84             break;
85         default:
86             return false;
87     }
88     return true;
89 }
90 
prepareForImageRef(const SkBitmap & bm,size_t * bitsPerComponent,CGBitmapInfo * info)91 static SkBitmap* prepareForImageRef(const SkBitmap& bm,
92                                     size_t* bitsPerComponent,
93                                     CGBitmapInfo* info) {
94     bool upscaleTo32;
95     if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) {
96         return NULL;
97     }
98 
99     SkBitmap* copy;
100     if (upscaleTo32) {
101         copy = new SkBitmap;
102         // here we make a ceep copy of the pixels, since CG won't take our
103         // 565 directly
104         bm.copyTo(copy, SkBitmap::kARGB_8888_Config);
105     } else {
106         copy = new SkBitmap(bm);
107     }
108     return copy;
109 }
110 
111 #undef HAS_ARGB_SHIFTS
112 
SkCreateCGImageRefWithColorspace(const SkBitmap & bm,CGColorSpaceRef colorSpace)113 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm,
114                                             CGColorSpaceRef colorSpace) {
115     size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING;
116     CGBitmapInfo info       SK_INIT_TO_AVOID_WARNING;
117 
118     SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info);
119     if (NULL == bitmap) {
120         return NULL;
121     }
122 
123     const int w = bitmap->width();
124     const int h = bitmap->height();
125     const size_t s = bitmap->getSize();
126 
127     // our provider "owns" the bitmap*, and will take care of deleting it
128     // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release
129     // proc, which will in turn unlock the pixels
130     bitmap->lockPixels();
131     CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s,
132                                                              SkBitmap_ReleaseInfo);
133 
134     bool releaseColorSpace = false;
135     if (NULL == colorSpace) {
136         colorSpace = CGColorSpaceCreateDeviceRGB();
137         releaseColorSpace = true;
138     }
139 
140     CGImageRef ref = CGImageCreate(w, h, bitsPerComponent,
141                                    bitmap->bytesPerPixel() * 8,
142                                    bitmap->rowBytes(), colorSpace, info, dataRef,
143                                    NULL, false, kCGRenderingIntentDefault);
144 
145     if (releaseColorSpace) {
146         CGColorSpaceRelease(colorSpace);
147     }
148     CGDataProviderRelease(dataRef);
149     return ref;
150 }
151 
SkCGDrawBitmap(CGContextRef cg,const SkBitmap & bm,float x,float y)152 void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) {
153     CGImageRef img = SkCreateCGImageRef(bm);
154 
155     if (img) {
156         CGRect r = CGRectMake(0, 0, bm.width(), bm.height());
157 
158         CGContextSaveGState(cg);
159         CGContextTranslateCTM(cg, x, r.size.height + y);
160         CGContextScaleCTM(cg, 1, -1);
161 
162         CGContextDrawImage(cg, r, img);
163 
164         CGContextRestoreGState(cg);
165 
166         CGImageRelease(img);
167     }
168 }
169 
170 ///////////////////////////////////////////////////////////////////////////////
171 
172 #include "SkStream.h"
173 
174 class SkAutoPDFRelease {
175 public:
SkAutoPDFRelease(CGPDFDocumentRef doc)176     SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {}
~SkAutoPDFRelease()177     ~SkAutoPDFRelease() {
178         if (fDoc) {
179             CGPDFDocumentRelease(fDoc);
180         }
181     }
182 private:
183     CGPDFDocumentRef fDoc;
184 };
185 
CGDataProviderReleaseData_FromMalloc(void *,const void * data,size_t size)186 static void CGDataProviderReleaseData_FromMalloc(void*, const void* data,
187                                                  size_t size) {
188     sk_free((void*)data);
189 }
190 
SkPDFDocumentToBitmap(SkStream * stream,SkBitmap * output)191 bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) {
192     size_t size = stream->getLength();
193     void* ptr = sk_malloc_throw(size);
194     stream->read(ptr, size);
195     CGDataProviderRef data = CGDataProviderCreateWithData(NULL, ptr, size,
196                                           CGDataProviderReleaseData_FromMalloc);
197     if (NULL == data) {
198         return false;
199     }
200 
201     CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data);
202     CGDataProviderRelease(data);
203     if (NULL == pdf) {
204         return false;
205     }
206     SkAutoPDFRelease releaseMe(pdf);
207 
208     CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1);
209     if (NULL == page) {
210         return false;
211     }
212 
213     CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox);
214 
215     int w = (int)CGRectGetWidth(bounds);
216     int h = (int)CGRectGetHeight(bounds);
217 
218     SkBitmap bitmap;
219     bitmap.setConfig(SkBitmap::kARGB_8888_Config, w, h);
220     bitmap.allocPixels();
221     bitmap.eraseColor(SK_ColorWHITE);
222 
223     size_t bitsPerComponent;
224     CGBitmapInfo info;
225     getBitmapInfo(bitmap, &bitsPerComponent, &info, NULL);
226 
227     CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
228     CGContextRef ctx = CGBitmapContextCreate(bitmap.getPixels(), w, h,
229                                              bitsPerComponent, bitmap.rowBytes(),
230                                              cs, info);
231     CGColorSpaceRelease(cs);
232 
233     if (ctx) {
234         CGContextDrawPDFPage(ctx, page);
235         CGContextRelease(ctx);
236     }
237 
238     output->swap(bitmap);
239     return true;
240 }
241