|
1 /* |
|
2 * Copyright 2008 The Android Open Source Project |
|
3 * |
|
4 * Use of this source code is governed by a BSD-style license that can be |
|
5 * found in the LICENSE file. |
|
6 */ |
|
7 |
|
8 #include "SkCGUtils.h" |
|
9 #include "SkColorPriv.h" |
|
10 #include "SkImageDecoder.h" |
|
11 #include "SkImageEncoder.h" |
|
12 #include "SkMovie.h" |
|
13 #include "SkStream.h" |
|
14 #include "SkStreamHelpers.h" |
|
15 #include "SkTemplates.h" |
|
16 #include "SkUnPreMultiply.h" |
|
17 |
|
18 #ifdef SK_BUILD_FOR_MAC |
|
19 #include <ApplicationServices/ApplicationServices.h> |
|
20 #endif |
|
21 |
|
22 #ifdef SK_BUILD_FOR_IOS |
|
23 #include <CoreGraphics/CoreGraphics.h> |
|
24 #include <ImageIO/ImageIO.h> |
|
25 #include <MobileCoreServices/MobileCoreServices.h> |
|
26 #endif |
|
27 |
|
28 static void malloc_release_proc(void* info, const void* data, size_t size) { |
|
29 sk_free(info); |
|
30 } |
|
31 |
|
32 static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) { |
|
33 // TODO: use callbacks, so we don't have to load all the data into RAM |
|
34 SkAutoMalloc storage; |
|
35 const size_t len = CopyStreamToStorage(&storage, stream); |
|
36 void* data = storage.detach(); |
|
37 |
|
38 return CGDataProviderCreateWithData(data, data, len, malloc_release_proc); |
|
39 } |
|
40 |
|
41 static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) { |
|
42 CGDataProviderRef data = SkStreamToDataProvider(stream); |
|
43 CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0); |
|
44 CGDataProviderRelease(data); |
|
45 return imageSrc; |
|
46 } |
|
47 |
|
48 class SkImageDecoder_CG : public SkImageDecoder { |
|
49 protected: |
|
50 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode); |
|
51 }; |
|
52 |
|
53 #define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast) |
|
54 |
|
55 bool SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) { |
|
56 CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); |
|
57 |
|
58 if (NULL == imageSrc) { |
|
59 return false; |
|
60 } |
|
61 SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); |
|
62 |
|
63 CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, NULL); |
|
64 if (NULL == image) { |
|
65 return false; |
|
66 } |
|
67 SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image); |
|
68 |
|
69 const int width = SkToInt(CGImageGetWidth(image)); |
|
70 const int height = SkToInt(CGImageGetHeight(image)); |
|
71 bm->setConfig(SkBitmap::kARGB_8888_Config, width, height); |
|
72 if (SkImageDecoder::kDecodeBounds_Mode == mode) { |
|
73 return true; |
|
74 } |
|
75 |
|
76 if (!this->allocPixelRef(bm, NULL)) { |
|
77 return false; |
|
78 } |
|
79 |
|
80 bm->lockPixels(); |
|
81 bm->eraseColor(SK_ColorTRANSPARENT); |
|
82 |
|
83 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB(); |
|
84 CGContextRef cg = CGBitmapContextCreate(bm->getPixels(), width, height, 8, bm->rowBytes(), cs, BITMAP_INFO); |
|
85 CFRelease(cs); |
|
86 |
|
87 CGContextDrawImage(cg, CGRectMake(0, 0, width, height), image); |
|
88 CGContextRelease(cg); |
|
89 |
|
90 CGImageAlphaInfo info = CGImageGetAlphaInfo(image); |
|
91 switch (info) { |
|
92 case kCGImageAlphaNone: |
|
93 case kCGImageAlphaNoneSkipLast: |
|
94 case kCGImageAlphaNoneSkipFirst: |
|
95 SkASSERT(SkBitmap::ComputeIsOpaque(*bm)); |
|
96 bm->setAlphaType(kOpaque_SkAlphaType); |
|
97 break; |
|
98 default: |
|
99 // we don't know if we're opaque or not, so compute it. |
|
100 if (SkBitmap::ComputeIsOpaque(*bm)) { |
|
101 bm->setAlphaType(kOpaque_SkAlphaType); |
|
102 } |
|
103 } |
|
104 if (!bm->isOpaque() && this->getRequireUnpremultipliedColors()) { |
|
105 // CGBitmapContext does not support unpremultiplied, so the image has been premultiplied. |
|
106 // Convert to unpremultiplied. |
|
107 for (int i = 0; i < width; ++i) { |
|
108 for (int j = 0; j < height; ++j) { |
|
109 uint32_t* addr = bm->getAddr32(i, j); |
|
110 *addr = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(*addr); |
|
111 } |
|
112 } |
|
113 bm->setAlphaType(kUnpremul_SkAlphaType); |
|
114 } |
|
115 bm->unlockPixels(); |
|
116 return true; |
|
117 } |
|
118 |
|
119 /////////////////////////////////////////////////////////////////////////////// |
|
120 |
|
121 extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*); |
|
122 |
|
123 SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) { |
|
124 SkImageDecoder* decoder = image_decoder_from_stream(stream); |
|
125 if (NULL == decoder) { |
|
126 // If no image decoder specific to the stream exists, use SkImageDecoder_CG. |
|
127 return SkNEW(SkImageDecoder_CG); |
|
128 } else { |
|
129 return decoder; |
|
130 } |
|
131 } |
|
132 |
|
133 ///////////////////////////////////////////////////////////////////////// |
|
134 |
|
135 SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) { |
|
136 return NULL; |
|
137 } |
|
138 |
|
139 ///////////////////////////////////////////////////////////////////////// |
|
140 |
|
141 static size_t consumer_put(void* info, const void* buffer, size_t count) { |
|
142 SkWStream* stream = reinterpret_cast<SkWStream*>(info); |
|
143 return stream->write(buffer, count) ? count : 0; |
|
144 } |
|
145 |
|
146 static void consumer_release(void* info) { |
|
147 // we do nothing, since by design we don't "own" the stream (i.e. info) |
|
148 } |
|
149 |
|
150 static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) { |
|
151 CGDataConsumerCallbacks procs; |
|
152 procs.putBytes = consumer_put; |
|
153 procs.releaseConsumer = consumer_release; |
|
154 // we don't own/reference the stream, so it our consumer must not live |
|
155 // longer that our caller's ownership of the stream |
|
156 return CGDataConsumerCreate(stream, &procs); |
|
157 } |
|
158 |
|
159 static CGImageDestinationRef SkStreamToImageDestination(SkWStream* stream, |
|
160 CFStringRef type) { |
|
161 CGDataConsumerRef consumer = SkStreamToCGDataConsumer(stream); |
|
162 if (NULL == consumer) { |
|
163 return NULL; |
|
164 } |
|
165 SkAutoTCallVProc<const void, CFRelease> arconsumer(consumer); |
|
166 |
|
167 return CGImageDestinationCreateWithDataConsumer(consumer, type, 1, NULL); |
|
168 } |
|
169 |
|
170 class SkImageEncoder_CG : public SkImageEncoder { |
|
171 public: |
|
172 SkImageEncoder_CG(Type t) : fType(t) {} |
|
173 |
|
174 protected: |
|
175 virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality); |
|
176 |
|
177 private: |
|
178 Type fType; |
|
179 }; |
|
180 |
|
181 /* Encode bitmaps via CGImageDestination. We setup a DataConsumer which writes |
|
182 to our SkWStream. Since we don't reference/own the SkWStream, our consumer |
|
183 must only live for the duration of the onEncode() method. |
|
184 */ |
|
185 bool SkImageEncoder_CG::onEncode(SkWStream* stream, const SkBitmap& bm, |
|
186 int quality) { |
|
187 // Used for converting a bitmap to 8888. |
|
188 const SkBitmap* bmPtr = &bm; |
|
189 SkBitmap bitmap8888; |
|
190 |
|
191 CFStringRef type; |
|
192 switch (fType) { |
|
193 case kICO_Type: |
|
194 type = kUTTypeICO; |
|
195 break; |
|
196 case kBMP_Type: |
|
197 type = kUTTypeBMP; |
|
198 break; |
|
199 case kGIF_Type: |
|
200 type = kUTTypeGIF; |
|
201 break; |
|
202 case kJPEG_Type: |
|
203 type = kUTTypeJPEG; |
|
204 break; |
|
205 case kPNG_Type: |
|
206 // PNG encoding an ARGB_4444 bitmap gives the following errors in GM: |
|
207 // <Error>: CGImageDestinationAddImage image could not be converted to destination |
|
208 // format. |
|
209 // <Error>: CGImageDestinationFinalize image destination does not have enough images |
|
210 // So instead we copy to 8888. |
|
211 if (bm.colorType() == kARGB_4444_SkColorType) { |
|
212 bm.copyTo(&bitmap8888, kPMColor_SkColorType); |
|
213 bmPtr = &bitmap8888; |
|
214 } |
|
215 type = kUTTypePNG; |
|
216 break; |
|
217 default: |
|
218 return false; |
|
219 } |
|
220 |
|
221 CGImageDestinationRef dst = SkStreamToImageDestination(stream, type); |
|
222 if (NULL == dst) { |
|
223 return false; |
|
224 } |
|
225 SkAutoTCallVProc<const void, CFRelease> ardst(dst); |
|
226 |
|
227 CGImageRef image = SkCreateCGImageRef(*bmPtr); |
|
228 if (NULL == image) { |
|
229 return false; |
|
230 } |
|
231 SkAutoTCallVProc<CGImage, CGImageRelease> agimage(image); |
|
232 |
|
233 CGImageDestinationAddImage(dst, image, NULL); |
|
234 return CGImageDestinationFinalize(dst); |
|
235 } |
|
236 |
|
237 /////////////////////////////////////////////////////////////////////////////// |
|
238 |
|
239 static SkImageEncoder* sk_imageencoder_cg_factory(SkImageEncoder::Type t) { |
|
240 switch (t) { |
|
241 case SkImageEncoder::kICO_Type: |
|
242 case SkImageEncoder::kBMP_Type: |
|
243 case SkImageEncoder::kGIF_Type: |
|
244 case SkImageEncoder::kJPEG_Type: |
|
245 case SkImageEncoder::kPNG_Type: |
|
246 break; |
|
247 default: |
|
248 return NULL; |
|
249 } |
|
250 return SkNEW_ARGS(SkImageEncoder_CG, (t)); |
|
251 } |
|
252 |
|
253 static SkImageEncoder_EncodeReg gEReg(sk_imageencoder_cg_factory); |
|
254 |
|
255 struct FormatConversion { |
|
256 CFStringRef fUTType; |
|
257 SkImageDecoder::Format fFormat; |
|
258 }; |
|
259 |
|
260 // Array of the types supported by the decoder. |
|
261 static const FormatConversion gFormatConversions[] = { |
|
262 { kUTTypeBMP, SkImageDecoder::kBMP_Format }, |
|
263 { kUTTypeGIF, SkImageDecoder::kGIF_Format }, |
|
264 { kUTTypeICO, SkImageDecoder::kICO_Format }, |
|
265 { kUTTypeJPEG, SkImageDecoder::kJPEG_Format }, |
|
266 // Also include JPEG2000 |
|
267 { kUTTypeJPEG2000, SkImageDecoder::kJPEG_Format }, |
|
268 { kUTTypePNG, SkImageDecoder::kPNG_Format }, |
|
269 }; |
|
270 |
|
271 static SkImageDecoder::Format UTType_to_Format(const CFStringRef uttype) { |
|
272 for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) { |
|
273 if (CFStringCompare(uttype, gFormatConversions[i].fUTType, 0) == kCFCompareEqualTo) { |
|
274 return gFormatConversions[i].fFormat; |
|
275 } |
|
276 } |
|
277 return SkImageDecoder::kUnknown_Format; |
|
278 } |
|
279 |
|
280 static SkImageDecoder::Format get_format_cg(SkStreamRewindable* stream) { |
|
281 CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); |
|
282 |
|
283 if (NULL == imageSrc) { |
|
284 return SkImageDecoder::kUnknown_Format; |
|
285 } |
|
286 |
|
287 SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); |
|
288 const CFStringRef name = CGImageSourceGetType(imageSrc); |
|
289 if (NULL == name) { |
|
290 return SkImageDecoder::kUnknown_Format; |
|
291 } |
|
292 return UTType_to_Format(name); |
|
293 } |
|
294 |
|
295 static SkImageDecoder_FormatReg gFormatReg(get_format_cg); |