|
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 |
|
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 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.colorType()) { |
|
26 case kRGB_565_SkColorType: |
|
27 #if 0 |
|
28 // doesn't see quite right. Are they thinking 1555? |
|
29 *bitsPerComponent = 5; |
|
30 *info = kCGBitmapByteOrder16Little | kCGImageAlphaNone; |
|
31 break; |
|
32 #endif |
|
33 if (upscaleTo32) { |
|
34 *upscaleTo32 = true; |
|
35 } |
|
36 // fall through |
|
37 case kPMColor_SkColorType: |
|
38 *bitsPerComponent = 8; |
|
39 #if SK_PMCOLOR_BYTE_ORDER(R,G,B,A) |
|
40 *info = kCGBitmapByteOrder32Big; |
|
41 if (bm.isOpaque()) { |
|
42 *info |= kCGImageAlphaNoneSkipLast; |
|
43 } else { |
|
44 *info |= kCGImageAlphaPremultipliedLast; |
|
45 } |
|
46 #elif SK_PMCOLOR_BYTE_ORDER(B,G,R,A) |
|
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 case kARGB_4444_SkColorType: |
|
70 *bitsPerComponent = 4; |
|
71 *info = kCGBitmapByteOrder16Little; |
|
72 if (bm.isOpaque()) { |
|
73 *info |= kCGImageAlphaNoneSkipLast; |
|
74 } else { |
|
75 *info |= kCGImageAlphaPremultipliedLast; |
|
76 } |
|
77 break; |
|
78 default: |
|
79 return false; |
|
80 } |
|
81 return true; |
|
82 } |
|
83 |
|
84 static SkBitmap* prepareForImageRef(const SkBitmap& bm, |
|
85 size_t* bitsPerComponent, |
|
86 CGBitmapInfo* info) { |
|
87 bool upscaleTo32; |
|
88 if (!getBitmapInfo(bm, bitsPerComponent, info, &upscaleTo32)) { |
|
89 return NULL; |
|
90 } |
|
91 |
|
92 SkBitmap* copy; |
|
93 if (upscaleTo32) { |
|
94 copy = new SkBitmap; |
|
95 // here we make a ceep copy of the pixels, since CG won't take our |
|
96 // 565 directly |
|
97 bm.copyTo(copy, kPMColor_SkColorType); |
|
98 } else { |
|
99 copy = new SkBitmap(bm); |
|
100 } |
|
101 return copy; |
|
102 } |
|
103 |
|
104 CGImageRef SkCreateCGImageRefWithColorspace(const SkBitmap& bm, |
|
105 CGColorSpaceRef colorSpace) { |
|
106 size_t bitsPerComponent SK_INIT_TO_AVOID_WARNING; |
|
107 CGBitmapInfo info SK_INIT_TO_AVOID_WARNING; |
|
108 |
|
109 SkBitmap* bitmap = prepareForImageRef(bm, &bitsPerComponent, &info); |
|
110 if (NULL == bitmap) { |
|
111 return NULL; |
|
112 } |
|
113 |
|
114 const int w = bitmap->width(); |
|
115 const int h = bitmap->height(); |
|
116 const size_t s = bitmap->getSize(); |
|
117 |
|
118 // our provider "owns" the bitmap*, and will take care of deleting it |
|
119 // we initially lock it, so we can access the pixels. The bitmap will be deleted in the release |
|
120 // proc, which will in turn unlock the pixels |
|
121 bitmap->lockPixels(); |
|
122 CGDataProviderRef dataRef = CGDataProviderCreateWithData(bitmap, bitmap->getPixels(), s, |
|
123 SkBitmap_ReleaseInfo); |
|
124 |
|
125 bool releaseColorSpace = false; |
|
126 if (NULL == colorSpace) { |
|
127 colorSpace = CGColorSpaceCreateDeviceRGB(); |
|
128 releaseColorSpace = true; |
|
129 } |
|
130 |
|
131 CGImageRef ref = CGImageCreate(w, h, bitsPerComponent, |
|
132 bitmap->bytesPerPixel() * 8, |
|
133 bitmap->rowBytes(), colorSpace, info, dataRef, |
|
134 NULL, false, kCGRenderingIntentDefault); |
|
135 |
|
136 if (releaseColorSpace) { |
|
137 CGColorSpaceRelease(colorSpace); |
|
138 } |
|
139 CGDataProviderRelease(dataRef); |
|
140 return ref; |
|
141 } |
|
142 |
|
143 void SkCGDrawBitmap(CGContextRef cg, const SkBitmap& bm, float x, float y) { |
|
144 CGImageRef img = SkCreateCGImageRef(bm); |
|
145 |
|
146 if (img) { |
|
147 CGRect r = CGRectMake(0, 0, bm.width(), bm.height()); |
|
148 |
|
149 CGContextSaveGState(cg); |
|
150 CGContextTranslateCTM(cg, x, r.size.height + y); |
|
151 CGContextScaleCTM(cg, 1, -1); |
|
152 |
|
153 CGContextDrawImage(cg, r, img); |
|
154 |
|
155 CGContextRestoreGState(cg); |
|
156 |
|
157 CGImageRelease(img); |
|
158 } |
|
159 } |
|
160 |
|
161 /////////////////////////////////////////////////////////////////////////////// |
|
162 |
|
163 #include "SkStream.h" |
|
164 |
|
165 class SkAutoPDFRelease { |
|
166 public: |
|
167 SkAutoPDFRelease(CGPDFDocumentRef doc) : fDoc(doc) {} |
|
168 ~SkAutoPDFRelease() { |
|
169 if (fDoc) { |
|
170 CGPDFDocumentRelease(fDoc); |
|
171 } |
|
172 } |
|
173 private: |
|
174 CGPDFDocumentRef fDoc; |
|
175 }; |
|
176 #define SkAutoPDFRelease(...) SK_REQUIRE_LOCAL_VAR(SkAutoPDFRelease) |
|
177 |
|
178 static void CGDataProviderReleaseData_FromMalloc(void*, const void* data, |
|
179 size_t size) { |
|
180 sk_free((void*)data); |
|
181 } |
|
182 |
|
183 bool SkPDFDocumentToBitmap(SkStream* stream, SkBitmap* output) { |
|
184 size_t size = stream->getLength(); |
|
185 void* ptr = sk_malloc_throw(size); |
|
186 stream->read(ptr, size); |
|
187 CGDataProviderRef data = CGDataProviderCreateWithData(NULL, ptr, size, |
|
188 CGDataProviderReleaseData_FromMalloc); |
|
189 if (NULL == data) { |
|
190 return false; |
|
191 } |
|
192 |
|
193 CGPDFDocumentRef pdf = CGPDFDocumentCreateWithProvider(data); |
|
194 CGDataProviderRelease(data); |
|
195 if (NULL == pdf) { |
|
196 return false; |
|
197 } |
|
198 SkAutoPDFRelease releaseMe(pdf); |
|
199 |
|
200 CGPDFPageRef page = CGPDFDocumentGetPage(pdf, 1); |
|
201 if (NULL == page) { |
|
202 return false; |
|
203 } |
|
204 |
|
205 CGRect bounds = CGPDFPageGetBoxRect(page, kCGPDFMediaBox); |
|
206 |
|
207 int w = (int)CGRectGetWidth(bounds); |
|
208 int h = (int)CGRectGetHeight(bounds); |
|
209 |
|
210 SkBitmap bitmap; |
|
211 if (!bitmap.allocPixels(SkImageInfo::MakeN32Premul(w, h))) { |
|
212 return false; |
|
213 } |
|
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 } |