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