|
1 /* |
|
2 * Copyright 2006 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 |
|
9 #include "SkImageDecoder.h" |
|
10 #include "SkBitmap.h" |
|
11 #include "SkImagePriv.h" |
|
12 #include "SkPixelRef.h" |
|
13 #include "SkStream.h" |
|
14 #include "SkTemplates.h" |
|
15 #include "SkCanvas.h" |
|
16 |
|
17 static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config; |
|
18 |
|
19 SkBitmap::Config SkImageDecoder::GetDeviceConfig() |
|
20 { |
|
21 return gDeviceConfig; |
|
22 } |
|
23 |
|
24 void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config) |
|
25 { |
|
26 gDeviceConfig = config; |
|
27 } |
|
28 |
|
29 /////////////////////////////////////////////////////////////////////////////// |
|
30 |
|
31 SkImageDecoder::SkImageDecoder() |
|
32 : fPeeker(NULL) |
|
33 , fChooser(NULL) |
|
34 , fAllocator(NULL) |
|
35 , fSampleSize(1) |
|
36 , fDefaultPref(SkBitmap::kNo_Config) |
|
37 , fDitherImage(true) |
|
38 , fUsePrefTable(false) |
|
39 , fSkipWritingZeroes(false) |
|
40 , fPreferQualityOverSpeed(false) |
|
41 , fRequireUnpremultipliedColors(false) { |
|
42 } |
|
43 |
|
44 SkImageDecoder::~SkImageDecoder() { |
|
45 SkSafeUnref(fPeeker); |
|
46 SkSafeUnref(fChooser); |
|
47 SkSafeUnref(fAllocator); |
|
48 } |
|
49 |
|
50 void SkImageDecoder::copyFieldsToOther(SkImageDecoder* other) { |
|
51 if (NULL == other) { |
|
52 return; |
|
53 } |
|
54 other->setPeeker(fPeeker); |
|
55 other->setChooser(fChooser); |
|
56 other->setAllocator(fAllocator); |
|
57 other->setSampleSize(fSampleSize); |
|
58 if (fUsePrefTable) { |
|
59 other->setPrefConfigTable(fPrefTable); |
|
60 } else { |
|
61 other->fDefaultPref = fDefaultPref; |
|
62 } |
|
63 other->setDitherImage(fDitherImage); |
|
64 other->setSkipWritingZeroes(fSkipWritingZeroes); |
|
65 other->setPreferQualityOverSpeed(fPreferQualityOverSpeed); |
|
66 other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors); |
|
67 } |
|
68 |
|
69 SkImageDecoder::Format SkImageDecoder::getFormat() const { |
|
70 return kUnknown_Format; |
|
71 } |
|
72 |
|
73 const char* SkImageDecoder::getFormatName() const { |
|
74 return GetFormatName(this->getFormat()); |
|
75 } |
|
76 |
|
77 const char* SkImageDecoder::GetFormatName(Format format) { |
|
78 switch (format) { |
|
79 case kUnknown_Format: |
|
80 return "Unknown Format"; |
|
81 case kBMP_Format: |
|
82 return "BMP"; |
|
83 case kGIF_Format: |
|
84 return "GIF"; |
|
85 case kICO_Format: |
|
86 return "ICO"; |
|
87 case kJPEG_Format: |
|
88 return "JPEG"; |
|
89 case kPNG_Format: |
|
90 return "PNG"; |
|
91 case kWBMP_Format: |
|
92 return "WBMP"; |
|
93 case kWEBP_Format: |
|
94 return "WEBP"; |
|
95 default: |
|
96 SkDEBUGFAIL("Invalid format type!"); |
|
97 } |
|
98 return "Unknown Format"; |
|
99 } |
|
100 |
|
101 SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) { |
|
102 SkRefCnt_SafeAssign(fPeeker, peeker); |
|
103 return peeker; |
|
104 } |
|
105 |
|
106 SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) { |
|
107 SkRefCnt_SafeAssign(fChooser, chooser); |
|
108 return chooser; |
|
109 } |
|
110 |
|
111 SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) { |
|
112 SkRefCnt_SafeAssign(fAllocator, alloc); |
|
113 return alloc; |
|
114 } |
|
115 |
|
116 void SkImageDecoder::setSampleSize(int size) { |
|
117 if (size < 1) { |
|
118 size = 1; |
|
119 } |
|
120 fSampleSize = size; |
|
121 } |
|
122 |
|
123 bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width, |
|
124 int height) const { |
|
125 Chooser* chooser = fChooser; |
|
126 |
|
127 if (NULL == chooser) { // no chooser, we just say YES to decoding :) |
|
128 return true; |
|
129 } |
|
130 chooser->begin(1); |
|
131 chooser->inspect(0, config, width, height); |
|
132 return chooser->choose() == 0; |
|
133 } |
|
134 |
|
135 bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap, |
|
136 SkColorTable* ctable) const { |
|
137 return bitmap->allocPixels(fAllocator, ctable); |
|
138 } |
|
139 |
|
140 /////////////////////////////////////////////////////////////////////////////// |
|
141 |
|
142 void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) { |
|
143 fUsePrefTable = true; |
|
144 fPrefTable = prefTable; |
|
145 } |
|
146 |
|
147 SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth, |
|
148 bool srcHasAlpha) const { |
|
149 SkBitmap::Config config = SkBitmap::kNo_Config; |
|
150 |
|
151 if (fUsePrefTable) { |
|
152 switch (srcDepth) { |
|
153 case kIndex_SrcDepth: |
|
154 config = srcHasAlpha ? fPrefTable.fPrefFor_8Index_YesAlpha_src |
|
155 : fPrefTable.fPrefFor_8Index_NoAlpha_src; |
|
156 break; |
|
157 case k8BitGray_SrcDepth: |
|
158 config = fPrefTable.fPrefFor_8Gray_src; |
|
159 break; |
|
160 case k32Bit_SrcDepth: |
|
161 config = srcHasAlpha ? fPrefTable.fPrefFor_8bpc_YesAlpha_src |
|
162 : fPrefTable.fPrefFor_8bpc_NoAlpha_src; |
|
163 break; |
|
164 } |
|
165 } else { |
|
166 config = fDefaultPref; |
|
167 } |
|
168 |
|
169 if (SkBitmap::kNo_Config == config) { |
|
170 config = SkImageDecoder::GetDeviceConfig(); |
|
171 } |
|
172 return config; |
|
173 } |
|
174 |
|
175 bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, |
|
176 SkBitmap::Config pref, Mode mode) { |
|
177 // we reset this to false before calling onDecode |
|
178 fShouldCancelDecode = false; |
|
179 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false |
|
180 fDefaultPref = pref; |
|
181 |
|
182 // pass a temporary bitmap, so that if we return false, we are assured of |
|
183 // leaving the caller's bitmap untouched. |
|
184 SkBitmap tmp; |
|
185 if (!this->onDecode(stream, &tmp, mode)) { |
|
186 return false; |
|
187 } |
|
188 bm->swap(tmp); |
|
189 return true; |
|
190 } |
|
191 |
|
192 bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect, |
|
193 SkBitmap::Config pref) { |
|
194 // we reset this to false before calling onDecodeSubset |
|
195 fShouldCancelDecode = false; |
|
196 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false |
|
197 fDefaultPref = pref; |
|
198 |
|
199 return this->onDecodeSubset(bm, rect); |
|
200 } |
|
201 |
|
202 bool SkImageDecoder::buildTileIndex(SkStreamRewindable* stream, |
|
203 int *width, int *height) { |
|
204 // we reset this to false before calling onBuildTileIndex |
|
205 fShouldCancelDecode = false; |
|
206 |
|
207 return this->onBuildTileIndex(stream, width, height); |
|
208 } |
|
209 |
|
210 bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize, |
|
211 int dstX, int dstY, int width, int height, |
|
212 int srcX, int srcY) { |
|
213 int w = width / sampleSize; |
|
214 int h = height / sampleSize; |
|
215 if (src->config() == SkBitmap::kIndex8_Config) { |
|
216 // kIndex8 does not allow drawing via an SkCanvas, as is done below. |
|
217 // Instead, use extractSubset. Note that this shares the SkPixelRef and |
|
218 // SkColorTable. |
|
219 // FIXME: Since src is discarded in practice, this holds on to more |
|
220 // pixels than is strictly necessary. Switch to a copy if memory |
|
221 // savings are more important than speed here. This also means |
|
222 // that the pixels in dst can not be reused (though there is no |
|
223 // allocation, which was already done on src). |
|
224 int x = (dstX - srcX) / sampleSize; |
|
225 int y = (dstY - srcY) / sampleSize; |
|
226 SkIRect subset = SkIRect::MakeXYWH(x, y, w, h); |
|
227 return src->extractSubset(dst, subset); |
|
228 } |
|
229 // if the destination has no pixels then we must allocate them. |
|
230 if (dst->isNull()) { |
|
231 dst->setConfig(src->config(), w, h, 0, src->alphaType()); |
|
232 |
|
233 if (!this->allocPixelRef(dst, NULL)) { |
|
234 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap")); |
|
235 return false; |
|
236 } |
|
237 } |
|
238 // check to see if the destination is large enough to decode the desired |
|
239 // region. If this assert fails we will just draw as much of the source |
|
240 // into the destination that we can. |
|
241 if (dst->width() < w || dst->height() < h) { |
|
242 SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n")); |
|
243 } |
|
244 |
|
245 // Set the Src_Mode for the paint to prevent transparency issue in the |
|
246 // dest in the event that the dest was being re-used. |
|
247 SkPaint paint; |
|
248 paint.setXfermodeMode(SkXfermode::kSrc_Mode); |
|
249 |
|
250 SkCanvas canvas(*dst); |
|
251 canvas.drawSprite(*src, (srcX - dstX) / sampleSize, |
|
252 (srcY - dstY) / sampleSize, |
|
253 &paint); |
|
254 return true; |
|
255 } |
|
256 |
|
257 /////////////////////////////////////////////////////////////////////////////// |
|
258 |
|
259 bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, |
|
260 SkBitmap::Config pref, Mode mode, Format* format) { |
|
261 SkASSERT(file); |
|
262 SkASSERT(bm); |
|
263 |
|
264 SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(file)); |
|
265 if (stream.get()) { |
|
266 if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) { |
|
267 bm->pixelRef()->setURI(file); |
|
268 return true; |
|
269 } |
|
270 } |
|
271 return false; |
|
272 } |
|
273 |
|
274 bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm, |
|
275 SkBitmap::Config pref, Mode mode, Format* format) { |
|
276 if (0 == size) { |
|
277 return false; |
|
278 } |
|
279 SkASSERT(buffer); |
|
280 |
|
281 SkMemoryStream stream(buffer, size); |
|
282 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format); |
|
283 } |
|
284 |
|
285 bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, |
|
286 SkBitmap::Config pref, Mode mode, |
|
287 Format* format) { |
|
288 SkASSERT(stream); |
|
289 SkASSERT(bm); |
|
290 |
|
291 bool success = false; |
|
292 SkImageDecoder* codec = SkImageDecoder::Factory(stream); |
|
293 |
|
294 if (NULL != codec) { |
|
295 success = codec->decode(stream, bm, pref, mode); |
|
296 if (success && format) { |
|
297 *format = codec->getFormat(); |
|
298 if (kUnknown_Format == *format) { |
|
299 if (stream->rewind()) { |
|
300 *format = GetStreamFormat(stream); |
|
301 } |
|
302 } |
|
303 } |
|
304 delete codec; |
|
305 } |
|
306 return success; |
|
307 } |