OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2008 The Android Open Source Project | 2 * Copyright 2008 The Android Open Source Project |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license that can be | 4 * Use of this source code is governed by a BSD-style license that can be |
5 * found in the LICENSE file. | 5 * found in the LICENSE file. |
6 */ | 6 */ |
7 | 7 |
8 #include "SkTypes.h" | 8 #include "SkTypes.h" |
9 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) | 9 #if defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) |
10 | 10 |
11 #include "SkCGUtils.h" | 11 #include "SkCGUtils.h" |
12 #include "SkColorPriv.h" | 12 #include "SkColorPriv.h" |
13 #include "SkData.h" | 13 #include "SkData.h" |
14 #include "SkImageDecoder.h" | |
15 #include "SkImageEncoder.h" | 14 #include "SkImageEncoder.h" |
16 #include "SkMovie.h" | 15 #include "SkMovie.h" |
17 #include "SkStream.h" | 16 #include "SkStream.h" |
18 #include "SkStreamPriv.h" | 17 #include "SkStreamPriv.h" |
19 #include "SkTemplates.h" | 18 #include "SkTemplates.h" |
20 #include "SkUnPreMultiply.h" | 19 #include "SkUnPreMultiply.h" |
21 | 20 |
22 #ifdef SK_BUILD_FOR_MAC | 21 #ifdef SK_BUILD_FOR_MAC |
23 #include <ApplicationServices/ApplicationServices.h> | 22 #include <ApplicationServices/ApplicationServices.h> |
24 #endif | 23 #endif |
25 | 24 |
26 #ifdef SK_BUILD_FOR_IOS | 25 #ifdef SK_BUILD_FOR_IOS |
27 #include <CoreGraphics/CoreGraphics.h> | 26 #include <CoreGraphics/CoreGraphics.h> |
28 #include <ImageIO/ImageIO.h> | 27 #include <ImageIO/ImageIO.h> |
29 #include <MobileCoreServices/MobileCoreServices.h> | 28 #include <MobileCoreServices/MobileCoreServices.h> |
30 #endif | 29 #endif |
31 | 30 |
32 static void data_unref_proc(void* skdata, const void*, size_t) { | |
33 SkASSERT(skdata); | |
34 static_cast<SkData*>(skdata)->unref(); | |
35 } | |
36 | |
37 static CGDataProviderRef SkStreamToDataProvider(SkStream* stream) { | |
38 // TODO: use callbacks, so we don't have to load all the data into RAM | |
39 SkData* skdata = SkCopyStreamToData(stream).release(); | |
40 if (!skdata) { | |
41 return nullptr; | |
42 } | |
43 | |
44 return CGDataProviderCreateWithData(skdata, skdata->data(), skdata->size(),
data_unref_proc); | |
45 } | |
46 | |
47 static CGImageSourceRef SkStreamToCGImageSource(SkStream* stream) { | |
48 CGDataProviderRef data = SkStreamToDataProvider(stream); | |
49 if (!data) { | |
50 return nullptr; | |
51 } | |
52 CGImageSourceRef imageSrc = CGImageSourceCreateWithDataProvider(data, 0); | |
53 CGDataProviderRelease(data); | |
54 return imageSrc; | |
55 } | |
56 | |
57 class SkImageDecoder_CG : public SkImageDecoder { | |
58 protected: | |
59 virtual Result onDecode(SkStream* stream, SkBitmap* bm, Mode); | |
60 }; | |
61 | |
62 static void argb_4444_force_opaque(void* row, int count) { | |
63 uint16_t* row16 = (uint16_t*)row; | |
64 for (int i = 0; i < count; ++i) { | |
65 row16[i] |= 0xF000; | |
66 } | |
67 } | |
68 | |
69 static void argb_8888_force_opaque(void* row, int count) { | |
70 // can use RGBA or BGRA, they have the same shift for alpha | |
71 const uint32_t alphaMask = 0xFF << SK_RGBA_A32_SHIFT; | |
72 uint32_t* row32 = (uint32_t*)row; | |
73 for (int i = 0; i < count; ++i) { | |
74 row32[i] |= alphaMask; | |
75 } | |
76 } | |
77 | |
78 static void alpha_8_force_opaque(void* row, int count) { | |
79 memset(row, 0xFF, count); | |
80 } | |
81 | |
82 static void force_opaque(SkBitmap* bm) { | |
83 SkAutoLockPixels alp(*bm); | |
84 if (!bm->getPixels()) { | |
85 return; | |
86 } | |
87 | |
88 void (*proc)(void*, int); | |
89 switch (bm->colorType()) { | |
90 case kARGB_4444_SkColorType: | |
91 proc = argb_4444_force_opaque; | |
92 break; | |
93 case kRGBA_8888_SkColorType: | |
94 case kBGRA_8888_SkColorType: | |
95 proc = argb_8888_force_opaque; | |
96 break; | |
97 case kAlpha_8_SkColorType: | |
98 proc = alpha_8_force_opaque; | |
99 break; | |
100 default: | |
101 return; | |
102 } | |
103 | |
104 char* row = (char*)bm->getPixels(); | |
105 for (int y = 0; y < bm->height(); ++y) { | |
106 proc(row, bm->width()); | |
107 row += bm->rowBytes(); | |
108 } | |
109 bm->setAlphaType(kOpaque_SkAlphaType); | |
110 } | |
111 | |
112 #define BITMAP_INFO (kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast) | |
113 | |
114 class AutoCFDataRelease { | |
115 CFDataRef fDR; | |
116 public: | |
117 AutoCFDataRelease(CFDataRef dr) : fDR(dr) {} | |
118 ~AutoCFDataRelease() { if (fDR) { CFRelease(fDR); } } | |
119 | |
120 operator CFDataRef () { return fDR; } | |
121 }; | |
122 | |
123 static bool colorspace_is_sRGB(CGColorSpaceRef cs) { | |
124 #ifdef SK_BUILD_FOR_IOS | |
125 return true; // iOS seems to define itself to always return sRGB <reed> | |
126 #else | |
127 AutoCFDataRelease data(CGColorSpaceCopyICCProfile(cs)); | |
128 if (data) { | |
129 // found by inspection -- need a cleaner way to sniff a profile | |
130 const CFIndex ICC_PROFILE_OFFSET_TO_SRGB_TAG = 52; | |
131 | |
132 if (CFDataGetLength(data) >= ICC_PROFILE_OFFSET_TO_SRGB_TAG + 4) { | |
133 return !memcmp(CFDataGetBytePtr(data) + ICC_PROFILE_OFFSET_TO_SRGB_T
AG, "sRGB", 4); | |
134 } | |
135 } | |
136 return false; | |
137 #endif | |
138 } | |
139 | |
140 SkImageDecoder::Result SkImageDecoder_CG::onDecode(SkStream* stream, SkBitmap* b
m, Mode mode) { | |
141 CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); | |
142 | |
143 if (nullptr == imageSrc) { | |
144 return kFailure; | |
145 } | |
146 SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); | |
147 | |
148 CGImageRef image = CGImageSourceCreateImageAtIndex(imageSrc, 0, nullptr); | |
149 if (nullptr == image) { | |
150 return kFailure; | |
151 } | |
152 SkAutoTCallVProc<CGImage, CGImageRelease> arimage(image); | |
153 | |
154 const int width = SkToInt(CGImageGetWidth(image)); | |
155 const int height = SkToInt(CGImageGetHeight(image)); | |
156 SkColorProfileType cpType = kLinear_SkColorProfileType; | |
157 | |
158 CGColorSpaceRef cs = CGImageGetColorSpace(image); | |
159 if (cs) { | |
160 CGColorSpaceModel m = CGColorSpaceGetModel(cs); | |
161 if (kCGColorSpaceModelRGB == m && colorspace_is_sRGB(cs)) { | |
162 cpType = kSRGB_SkColorProfileType; | |
163 } | |
164 } | |
165 | |
166 SkAlphaType at = kPremul_SkAlphaType; | |
167 switch (CGImageGetAlphaInfo(image)) { | |
168 case kCGImageAlphaNone: | |
169 case kCGImageAlphaNoneSkipLast: | |
170 case kCGImageAlphaNoneSkipFirst: | |
171 at = kOpaque_SkAlphaType; | |
172 break; | |
173 default: | |
174 break; | |
175 } | |
176 | |
177 bm->setInfo(SkImageInfo::Make(width, height, kN32_SkColorType, at, cpType)); | |
178 if (SkImageDecoder::kDecodeBounds_Mode == mode) { | |
179 return kSuccess; | |
180 } | |
181 | |
182 if (!this->allocPixelRef(bm, nullptr)) { | |
183 return kFailure; | |
184 } | |
185 | |
186 SkAutoLockPixels alp(*bm); | |
187 | |
188 if (!SkCopyPixelsFromCGImage(bm->info(), bm->rowBytes(), bm->getPixels(), im
age)) { | |
189 return kFailure; | |
190 } | |
191 | |
192 CGImageAlphaInfo info = CGImageGetAlphaInfo(image); | |
193 switch (info) { | |
194 case kCGImageAlphaNone: | |
195 case kCGImageAlphaNoneSkipLast: | |
196 case kCGImageAlphaNoneSkipFirst: | |
197 // We're opaque, but we can't rely on the data always having 0xFF | |
198 // in the alpha slot (which Skia wants), so we have to ram it in | |
199 // ourselves. | |
200 force_opaque(bm); | |
201 break; | |
202 default: | |
203 // we don't know if we're opaque or not, so compute it. | |
204 if (SkBitmap::ComputeIsOpaque(*bm)) { | |
205 bm->setAlphaType(kOpaque_SkAlphaType); | |
206 } | |
207 } | |
208 if (!bm->isOpaque() && this->getRequireUnpremultipliedColors()) { | |
209 // CGBitmapContext does not support unpremultiplied, so the image has be
en premultiplied. | |
210 // Convert to unpremultiplied. | |
211 for (int i = 0; i < width; ++i) { | |
212 for (int j = 0; j < height; ++j) { | |
213 uint32_t* addr = bm->getAddr32(i, j); | |
214 *addr = SkUnPreMultiply::UnPreMultiplyPreservingByteOrder(*addr)
; | |
215 } | |
216 } | |
217 bm->setAlphaType(kUnpremul_SkAlphaType); | |
218 } | |
219 return kSuccess; | |
220 } | |
221 | |
222 /////////////////////////////////////////////////////////////////////////////// | |
223 | |
224 extern SkImageDecoder* image_decoder_from_stream(SkStreamRewindable*); | |
225 | |
226 SkImageDecoder* SkImageDecoder::Factory(SkStreamRewindable* stream) { | |
227 SkImageDecoder* decoder = image_decoder_from_stream(stream); | |
228 if (nullptr == decoder) { | |
229 // If no image decoder specific to the stream exists, use SkImageDecoder
_CG. | |
230 return new SkImageDecoder_CG; | |
231 } else { | |
232 return decoder; | |
233 } | |
234 } | |
235 | |
236 ///////////////////////////////////////////////////////////////////////// | |
237 | |
238 SkMovie* SkMovie::DecodeStream(SkStreamRewindable* stream) { | |
239 return nullptr; | |
240 } | |
241 | |
242 ///////////////////////////////////////////////////////////////////////// | |
243 | |
244 static size_t consumer_put(void* info, const void* buffer, size_t count) { | 31 static size_t consumer_put(void* info, const void* buffer, size_t count) { |
245 SkWStream* stream = reinterpret_cast<SkWStream*>(info); | 32 SkWStream* stream = reinterpret_cast<SkWStream*>(info); |
246 return stream->write(buffer, count) ? count : 0; | 33 return stream->write(buffer, count) ? count : 0; |
247 } | 34 } |
248 | 35 |
249 static void consumer_release(void* info) { | 36 static void consumer_release(void* info) { |
250 // we do nothing, since by design we don't "own" the stream (i.e. info) | 37 // we do nothing, since by design we don't "own" the stream (i.e. info) |
251 } | 38 } |
252 | 39 |
253 static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) { | 40 static CGDataConsumerRef SkStreamToCGDataConsumer(SkWStream* stream) { |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
359 class SkPNGImageEncoder_IOS : public SkImageEncoder_CG { | 146 class SkPNGImageEncoder_IOS : public SkImageEncoder_CG { |
360 public: | 147 public: |
361 SkPNGImageEncoder_IOS() | 148 SkPNGImageEncoder_IOS() |
362 : SkImageEncoder_CG(kPNG_Type) { | 149 : SkImageEncoder_CG(kPNG_Type) { |
363 } | 150 } |
364 }; | 151 }; |
365 | 152 |
366 DEFINE_ENCODER_CREATOR(PNGImageEncoder_IOS); | 153 DEFINE_ENCODER_CREATOR(PNGImageEncoder_IOS); |
367 #endif | 154 #endif |
368 | 155 |
369 struct FormatConversion { | |
370 CFStringRef fUTType; | |
371 SkImageDecoder::Format fFormat; | |
372 }; | |
373 | |
374 // Array of the types supported by the decoder. | |
375 static const FormatConversion gFormatConversions[] = { | |
376 { kUTTypeBMP, SkImageDecoder::kBMP_Format }, | |
377 { kUTTypeGIF, SkImageDecoder::kGIF_Format }, | |
378 { kUTTypeICO, SkImageDecoder::kICO_Format }, | |
379 { kUTTypeJPEG, SkImageDecoder::kJPEG_Format }, | |
380 // Also include JPEG2000 | |
381 { kUTTypeJPEG2000, SkImageDecoder::kJPEG_Format }, | |
382 { kUTTypePNG, SkImageDecoder::kPNG_Format }, | |
383 }; | |
384 | |
385 static SkImageDecoder::Format UTType_to_Format(const CFStringRef uttype) { | |
386 for (size_t i = 0; i < SK_ARRAY_COUNT(gFormatConversions); i++) { | |
387 if (CFStringCompare(uttype, gFormatConversions[i].fUTType, 0) == kCFComp
areEqualTo) { | |
388 return gFormatConversions[i].fFormat; | |
389 } | |
390 } | |
391 return SkImageDecoder::kUnknown_Format; | |
392 } | |
393 | |
394 static SkImageDecoder::Format get_format_cg(SkStreamRewindable* stream) { | |
395 CGImageSourceRef imageSrc = SkStreamToCGImageSource(stream); | |
396 | |
397 if (nullptr == imageSrc) { | |
398 return SkImageDecoder::kUnknown_Format; | |
399 } | |
400 | |
401 SkAutoTCallVProc<const void, CFRelease> arsrc(imageSrc); | |
402 const CFStringRef name = CGImageSourceGetType(imageSrc); | |
403 if (nullptr == name) { | |
404 return SkImageDecoder::kUnknown_Format; | |
405 } | |
406 return UTType_to_Format(name); | |
407 } | |
408 | |
409 static SkImageDecoder_FormatReg gFormatReg(get_format_cg); | |
410 | |
411 #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) | 156 #endif//defined(SK_BUILD_FOR_MAC) || defined(SK_BUILD_FOR_IOS) |
OLD | NEW |