|
1 /* |
|
2 * Copyright 2013 Google Inc. |
|
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 "SkData.h" |
|
9 #include "SkDecodingImageGenerator.h" |
|
10 #include "SkImageDecoder.h" |
|
11 #include "SkImageInfo.h" |
|
12 #include "SkImageGenerator.h" |
|
13 #include "SkImagePriv.h" |
|
14 #include "SkStream.h" |
|
15 #include "SkUtils.h" |
|
16 |
|
17 static bool equal_modulo_alpha(const SkImageInfo& a, const SkImageInfo& b) { |
|
18 return a.width() == b.width() && a.height() == b.height() && |
|
19 a.colorType() == b.colorType(); |
|
20 } |
|
21 |
|
22 namespace { |
|
23 /** |
|
24 * Special allocator used by getPixels(). Uses preallocated memory |
|
25 * provided if possible, else fall-back on the default allocator |
|
26 */ |
|
27 class TargetAllocator : public SkBitmap::Allocator { |
|
28 public: |
|
29 TargetAllocator(const SkImageInfo& info, |
|
30 void* target, |
|
31 size_t rowBytes) |
|
32 : fInfo(info) |
|
33 , fTarget(target) |
|
34 , fRowBytes(rowBytes) |
|
35 {} |
|
36 |
|
37 bool isReady() { return (fTarget != NULL); } |
|
38 |
|
39 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) { |
|
40 if (NULL == fTarget || !equal_modulo_alpha(fInfo, bm->info())) { |
|
41 // Call default allocator. |
|
42 return bm->allocPixels(NULL, ct); |
|
43 } |
|
44 |
|
45 // TODO(halcanary): verify that all callers of this function |
|
46 // will respect new RowBytes. Will be moot once rowbytes belongs |
|
47 // to PixelRef. |
|
48 bm->installPixels(fInfo, fTarget, fRowBytes, NULL, NULL); |
|
49 |
|
50 fTarget = NULL; // never alloc same pixels twice! |
|
51 return true; |
|
52 } |
|
53 |
|
54 private: |
|
55 const SkImageInfo fInfo; |
|
56 void* fTarget; // Block of memory to be supplied as pixel memory |
|
57 // in allocPixelRef. Must be large enough to hold |
|
58 // a bitmap described by fInfo and fRowBytes |
|
59 const size_t fRowBytes; // rowbytes for the destination bitmap |
|
60 |
|
61 typedef SkBitmap::Allocator INHERITED; |
|
62 }; |
|
63 |
|
64 // TODO(halcanary): Give this macro a better name and move it into SkTypes.h |
|
65 #ifdef SK_DEBUG |
|
66 #define SkCheckResult(expr, value) SkASSERT((value) == (expr)) |
|
67 #else |
|
68 #define SkCheckResult(expr, value) (void)(expr) |
|
69 #endif |
|
70 |
|
71 #ifdef SK_DEBUG |
|
72 inline bool check_alpha(SkAlphaType reported, SkAlphaType actual) { |
|
73 return ((reported == actual) |
|
74 || ((reported == kPremul_SkAlphaType) |
|
75 && (actual == kOpaque_SkAlphaType))); |
|
76 } |
|
77 #endif // SK_DEBUG |
|
78 |
|
79 } // namespace |
|
80 //////////////////////////////////////////////////////////////////////////////// |
|
81 |
|
82 SkDecodingImageGenerator::SkDecodingImageGenerator( |
|
83 SkData* data, |
|
84 SkStreamRewindable* stream, |
|
85 const SkImageInfo& info, |
|
86 int sampleSize, |
|
87 bool ditherImage) |
|
88 : fData(data) |
|
89 , fStream(stream) |
|
90 , fInfo(info) |
|
91 , fSampleSize(sampleSize) |
|
92 , fDitherImage(ditherImage) |
|
93 { |
|
94 SkASSERT(stream != NULL); |
|
95 SkSafeRef(fData); // may be NULL. |
|
96 } |
|
97 |
|
98 SkDecodingImageGenerator::~SkDecodingImageGenerator() { |
|
99 SkSafeUnref(fData); |
|
100 fStream->unref(); |
|
101 } |
|
102 |
|
103 bool SkDecodingImageGenerator::getInfo(SkImageInfo* info) { |
|
104 if (info != NULL) { |
|
105 *info = fInfo; |
|
106 } |
|
107 return true; |
|
108 } |
|
109 |
|
110 SkData* SkDecodingImageGenerator::refEncodedData() { |
|
111 // This functionality is used in `gm --serialize` |
|
112 // Does not encode options. |
|
113 if (fData != NULL) { |
|
114 return SkSafeRef(fData); |
|
115 } |
|
116 // TODO(halcanary): SkStreamRewindable needs a refData() function |
|
117 // which returns a cheap copy of the underlying data. |
|
118 if (!fStream->rewind()) { |
|
119 return NULL; |
|
120 } |
|
121 size_t length = fStream->getLength(); |
|
122 if (0 == length) { |
|
123 return NULL; |
|
124 } |
|
125 void* buffer = sk_malloc_flags(length, 0); |
|
126 SkCheckResult(fStream->read(buffer, length), length); |
|
127 fData = SkData::NewFromMalloc(buffer, length); |
|
128 return SkSafeRef(fData); |
|
129 } |
|
130 |
|
131 bool SkDecodingImageGenerator::getPixels(const SkImageInfo& info, |
|
132 void* pixels, |
|
133 size_t rowBytes) { |
|
134 if (NULL == pixels) { |
|
135 return false; |
|
136 } |
|
137 if (fInfo != info) { |
|
138 // The caller has specified a different info. This is an |
|
139 // error for this kind of SkImageGenerator. Use the Options |
|
140 // to change the settings. |
|
141 return false; |
|
142 } |
|
143 if (info.minRowBytes() > rowBytes) { |
|
144 // The caller has specified a bad rowBytes. |
|
145 return false; |
|
146 } |
|
147 |
|
148 SkAssertResult(fStream->rewind()); |
|
149 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(fStream)); |
|
150 if (NULL == decoder.get()) { |
|
151 return false; |
|
152 } |
|
153 decoder->setDitherImage(fDitherImage); |
|
154 decoder->setSampleSize(fSampleSize); |
|
155 |
|
156 SkBitmap bitmap; |
|
157 TargetAllocator allocator(fInfo, pixels, rowBytes); |
|
158 decoder->setAllocator(&allocator); |
|
159 // TODO: need to be able to pass colortype directly to decoder |
|
160 SkBitmap::Config legacyConfig = SkColorTypeToBitmapConfig(info.colorType()); |
|
161 bool success = decoder->decode(fStream, &bitmap, legacyConfig, |
|
162 SkImageDecoder::kDecodePixels_Mode); |
|
163 decoder->setAllocator(NULL); |
|
164 if (!success) { |
|
165 return false; |
|
166 } |
|
167 if (allocator.isReady()) { // Did not use pixels! |
|
168 SkBitmap bm; |
|
169 SkASSERT(bitmap.canCopyTo(info.colorType())); |
|
170 bool copySuccess = bitmap.copyTo(&bm, info.colorType(), &allocator); |
|
171 if (!copySuccess || allocator.isReady()) { |
|
172 SkDEBUGFAIL("bitmap.copyTo(requestedConfig) failed."); |
|
173 // Earlier we checked canCopyto(); we expect consistency. |
|
174 return false; |
|
175 } |
|
176 SkASSERT(check_alpha(info.alphaType(), bm.alphaType())); |
|
177 } else { |
|
178 SkASSERT(check_alpha(info.alphaType(), bitmap.alphaType())); |
|
179 } |
|
180 return true; |
|
181 } |
|
182 |
|
183 SkImageGenerator* SkDecodingImageGenerator::Create( |
|
184 SkData* data, |
|
185 const SkDecodingImageGenerator::Options& opts) { |
|
186 SkASSERT(data != NULL); |
|
187 if (NULL == data) { |
|
188 return NULL; |
|
189 } |
|
190 SkStreamRewindable* stream = SkNEW_ARGS(SkMemoryStream, (data)); |
|
191 SkASSERT(stream != NULL); |
|
192 SkASSERT(stream->unique()); |
|
193 return SkDecodingImageGenerator::Create(data, stream, opts); |
|
194 } |
|
195 |
|
196 SkImageGenerator* SkDecodingImageGenerator::Create( |
|
197 SkStreamRewindable* stream, |
|
198 const SkDecodingImageGenerator::Options& opts) { |
|
199 SkASSERT(stream != NULL); |
|
200 SkASSERT(stream->unique()); |
|
201 if ((stream == NULL) || !stream->unique()) { |
|
202 SkSafeUnref(stream); |
|
203 return NULL; |
|
204 } |
|
205 return SkDecodingImageGenerator::Create(NULL, stream, opts); |
|
206 } |
|
207 |
|
208 // A contructor-type function that returns NULL on failure. This |
|
209 // prevents the returned SkImageGenerator from ever being in a bad |
|
210 // state. Called by both Create() functions |
|
211 SkImageGenerator* SkDecodingImageGenerator::Create( |
|
212 SkData* data, |
|
213 SkStreamRewindable* stream, |
|
214 const SkDecodingImageGenerator::Options& opts) { |
|
215 SkASSERT(stream); |
|
216 SkAutoTUnref<SkStreamRewindable> autoStream(stream); // always unref this. |
|
217 if (opts.fUseRequestedColorType && |
|
218 (kIndex_8_SkColorType == opts.fRequestedColorType)) { |
|
219 // We do not support indexed color with SkImageGenerators, |
|
220 return NULL; |
|
221 } |
|
222 SkAssertResult(autoStream->rewind()); |
|
223 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(autoStream)); |
|
224 if (NULL == decoder.get()) { |
|
225 return NULL; |
|
226 } |
|
227 SkBitmap bitmap; |
|
228 decoder->setSampleSize(opts.fSampleSize); |
|
229 if (!decoder->decode(stream, &bitmap, |
|
230 SkImageDecoder::kDecodeBounds_Mode)) { |
|
231 return NULL; |
|
232 } |
|
233 if (bitmap.config() == SkBitmap::kNo_Config) { |
|
234 return NULL; |
|
235 } |
|
236 |
|
237 SkImageInfo info = bitmap.info(); |
|
238 |
|
239 if (!opts.fUseRequestedColorType) { |
|
240 // Use default |
|
241 if (kIndex_8_SkColorType == bitmap.colorType()) { |
|
242 // We don't support kIndex8 because we don't support |
|
243 // colortables in this workflow. |
|
244 info.fColorType = kPMColor_SkColorType; |
|
245 } |
|
246 } else { |
|
247 if (!bitmap.canCopyTo(opts.fRequestedColorType)) { |
|
248 SkASSERT(bitmap.colorType() != opts.fRequestedColorType); |
|
249 return NULL; // Can not translate to needed config. |
|
250 } |
|
251 info.fColorType = opts.fRequestedColorType; |
|
252 } |
|
253 return SkNEW_ARGS(SkDecodingImageGenerator, |
|
254 (data, autoStream.detach(), info, |
|
255 opts.fSampleSize, opts.fDitherImage)); |
|
256 } |