blob: a671607ef36f811695fd8fbe4831749a294634d6 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001/*
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
reed@android.com8a1c16f2008-12-17 15:59:43 +00008
9#include "SkImageDecoder.h"
10#include "SkBitmap.h"
scroggo@google.comf8d7d272013-02-22 21:38:35 +000011#include "SkImagePriv.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000012#include "SkPixelRef.h"
13#include "SkStream.h"
14#include "SkTemplates.h"
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000015#include "SkCanvas.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000016
robertphillips@google.com15e9d3e2012-06-21 20:25:03 +000017SK_DEFINE_INST_COUNT(SkImageDecoder::Peeker)
18SK_DEFINE_INST_COUNT(SkImageDecoder::Chooser)
19SK_DEFINE_INST_COUNT(SkImageDecoderFactory)
20
reed@android.com8a1c16f2008-12-17 15:59:43 +000021static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
22
23SkBitmap::Config SkImageDecoder::GetDeviceConfig()
24{
25 return gDeviceConfig;
26}
27
28void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
29{
30 gDeviceConfig = config;
31}
32
33///////////////////////////////////////////////////////////////////////////////
34
35SkImageDecoder::SkImageDecoder()
scroggo@google.com7e6fcee2013-05-03 20:14:28 +000036 : fPeeker(NULL)
37 , fChooser(NULL)
38 , fAllocator(NULL)
39 , fSampleSize(1)
40 , fDefaultPref(SkBitmap::kNo_Config)
41 , fDitherImage(true)
42 , fUsePrefTable(false)
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000043 , fPreferQualityOverSpeed(false)
44 , fRequireUnpremultipliedColors(false) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000045}
46
47SkImageDecoder::~SkImageDecoder() {
reed@google.com82065d62011-02-07 15:30:46 +000048 SkSafeUnref(fPeeker);
49 SkSafeUnref(fChooser);
50 SkSafeUnref(fAllocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +000051}
52
scroggo@google.com468142b2013-07-09 15:48:24 +000053void SkImageDecoder::copyFieldsToOther(SkImageDecoder* other) {
54 if (NULL == other) {
55 return;
56 }
57 other->setPeeker(fPeeker);
58 other->setChooser(fChooser);
59 other->setAllocator(fAllocator);
60 other->setSampleSize(fSampleSize);
61 if (fUsePrefTable) {
62 other->setPrefConfigTable(fPrefTable);
63 } else {
64 other->fDefaultPref = fDefaultPref;
65 }
66 other->setPreferQualityOverSpeed(fPreferQualityOverSpeed);
67 other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors);
68}
69
reed@android.com8a1c16f2008-12-17 15:59:43 +000070SkImageDecoder::Format SkImageDecoder::getFormat() const {
71 return kUnknown_Format;
72}
73
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000074const char* SkImageDecoder::getFormatName() const {
scroggo@google.comf98118e2013-05-15 14:53:49 +000075 return GetFormatName(this->getFormat());
76}
77
78const char* SkImageDecoder::GetFormatName(Format format) {
79 switch (format) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +000080 case kUnknown_Format:
81 return "Unknown Format";
82 case kBMP_Format:
83 return "BMP";
84 case kGIF_Format:
85 return "GIF";
86 case kICO_Format:
87 return "ICO";
88 case kJPEG_Format:
89 return "JPEG";
90 case kPNG_Format:
91 return "PNG";
92 case kWBMP_Format:
93 return "WBMP";
94 case kWEBP_Format:
95 return "WEBP";
96 default:
97 SkASSERT(!"Invalid format type!");
98 }
99 return "Unknown Format";
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000100}
101
reed@android.com8a1c16f2008-12-17 15:59:43 +0000102SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
103 SkRefCnt_SafeAssign(fPeeker, peeker);
104 return peeker;
105}
106
107SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
108 SkRefCnt_SafeAssign(fChooser, chooser);
109 return chooser;
110}
111
112SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
113 SkRefCnt_SafeAssign(fAllocator, alloc);
114 return alloc;
115}
116
117void SkImageDecoder::setSampleSize(int size) {
118 if (size < 1) {
119 size = 1;
120 }
121 fSampleSize = size;
122}
123
124bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
125 int height) const {
126 Chooser* chooser = fChooser;
127
128 if (NULL == chooser) { // no chooser, we just say YES to decoding :)
129 return true;
130 }
131 chooser->begin(1);
132 chooser->inspect(0, config, width, height);
133 return chooser->choose() == 0;
134}
135
136bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
137 SkColorTable* ctable) const {
138 return bitmap->allocPixels(fAllocator, ctable);
139}
140
141///////////////////////////////////////////////////////////////////////////////
reed@android.comb6137c32009-07-29 20:56:52 +0000142
reed@android.com3f1f06a2010-03-03 21:04:12 +0000143void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
144 if (NULL == pref) {
145 fUsePrefTable = false;
146 } else {
147 fUsePrefTable = true;
scroggo@google.comf698c822013-07-18 19:34:49 +0000148 fPrefTable.fPrefFor_8Index_NoAlpha_src = pref[0];
149 fPrefTable.fPrefFor_8Index_YesAlpha_src = pref[1];
150 fPrefTable.fPrefFor_8Gray_src = SkBitmap::kNo_Config;
151 fPrefTable.fPrefFor_8bpc_NoAlpha_src = pref[4];
152 fPrefTable.fPrefFor_8bpc_YesAlpha_src = pref[5];
reed@android.com3f1f06a2010-03-03 21:04:12 +0000153 }
154}
155
scroggo@google.comf698c822013-07-18 19:34:49 +0000156void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) {
157 fUsePrefTable = true;
158 fPrefTable = prefTable;
159}
160
reed@android.com3f1f06a2010-03-03 21:04:12 +0000161SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
162 bool srcHasAlpha) const {
scroggo@google.com12d06422013-07-18 19:42:35 +0000163 SkBitmap::Config config = SkBitmap::kNo_Config;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000164
165 if (fUsePrefTable) {
reed@android.com3f1f06a2010-03-03 21:04:12 +0000166 switch (srcDepth) {
167 case kIndex_SrcDepth:
scroggo@google.comf698c822013-07-18 19:34:49 +0000168 config = srcHasAlpha ? fPrefTable.fPrefFor_8Index_YesAlpha_src
169 : fPrefTable.fPrefFor_8Index_NoAlpha_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000170 break;
scroggo@google.comf698c822013-07-18 19:34:49 +0000171 case k8BitGray_SrcDepth:
172 config = fPrefTable.fPrefFor_8Gray_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000173 break;
174 case k32Bit_SrcDepth:
scroggo@google.comf698c822013-07-18 19:34:49 +0000175 config = srcHasAlpha ? fPrefTable.fPrefFor_8bpc_YesAlpha_src
176 : fPrefTable.fPrefFor_8bpc_NoAlpha_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000177 break;
178 }
reed@android.com3f1f06a2010-03-03 21:04:12 +0000179 } else {
180 config = fDefaultPref;
181 }
182
183 if (SkBitmap::kNo_Config == config) {
184 config = SkImageDecoder::GetDeviceConfig();
185 }
186 return config;
187}
188
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
scroggo@google.combc69ce92013-07-09 15:45:14 +0000190 SkBitmap::Config pref, Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 // we reset this to false before calling onDecode
192 fShouldCancelDecode = false;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000193 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
194 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000195
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000196 // pass a temporary bitmap, so that if we return false, we are assured of
197 // leaving the caller's bitmap untouched.
198 SkBitmap tmp;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000199 if (!this->onDecode(stream, &tmp, mode)) {
reed@android.com62900b42009-02-11 15:07:19 +0000200 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000201 }
reed@android.com62900b42009-02-11 15:07:19 +0000202 bm->swap(tmp);
203 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204}
205
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000206bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000207 SkBitmap::Config pref) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000208 // we reset this to false before calling onDecodeSubset
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000209 fShouldCancelDecode = false;
210 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
211 fDefaultPref = pref;
212
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000213 return this->onDecodeSubset(bm, rect);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000214}
215
216bool SkImageDecoder::buildTileIndex(SkStream* stream,
217 int *width, int *height) {
218 // we reset this to false before calling onBuildTileIndex
219 fShouldCancelDecode = false;
220
221 return this->onBuildTileIndex(stream, width, height);
222}
223
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000224bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
225 int dstX, int dstY, int width, int height,
226 int srcX, int srcY) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000227 int w = width / sampleSize;
228 int h = height / sampleSize;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000229 if (src->getConfig() == SkBitmap::kIndex8_Config) {
230 // kIndex8 does not allow drawing via an SkCanvas, as is done below.
231 // Instead, use extractSubset. Note that this shares the SkPixelRef and
232 // SkColorTable.
233 // FIXME: Since src is discarded in practice, this holds on to more
234 // pixels than is strictly necessary. Switch to a copy if memory
235 // savings are more important than speed here. This also means
236 // that the pixels in dst can not be reused (though there is no
237 // allocation, which was already done on src).
238 int x = (dstX - srcX) / sampleSize;
239 int y = (dstY - srcY) / sampleSize;
240 SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
241 return src->extractSubset(dst, subset);
242 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000243 // if the destination has no pixels then we must allocate them.
244 if (dst->isNull()) {
245 dst->setConfig(src->getConfig(), w, h);
246 dst->setIsOpaque(src->isOpaque());
247
248 if (!this->allocPixelRef(dst, NULL)) {
249 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000250 return false;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000251 }
252 }
253 // check to see if the destination is large enough to decode the desired
254 // region. If this assert fails we will just draw as much of the source
255 // into the destination that we can.
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000256 if (dst->width() < w || dst->height() < h) {
257 SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n"));
258 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000259
260 // Set the Src_Mode for the paint to prevent transparency issue in the
261 // dest in the event that the dest was being re-used.
262 SkPaint paint;
263 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
264
265 SkCanvas canvas(*dst);
266 canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
267 (srcY - dstY) / sampleSize,
268 &paint);
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000269 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000270}
271
reed@android.com8a1c16f2008-12-17 15:59:43 +0000272///////////////////////////////////////////////////////////////////////////////
273
274bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000275 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276 SkASSERT(file);
277 SkASSERT(bm);
278
mike@reedtribe.orgf3811622013-03-19 02:18:33 +0000279 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(file));
280 if (stream.get()) {
281 if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282 bm->pixelRef()->setURI(file);
tomhudson@google.com1a366212012-01-03 14:42:08 +0000283 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000284 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 }
286 return false;
287}
288
289bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000290 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000291 if (0 == size) {
292 return false;
293 }
294 SkASSERT(buffer);
295
296 SkMemoryStream stream(buffer, size);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000297 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000298}
299
scroggo@google.com67882dd2013-05-20 16:58:16 +0000300/**
301 * Special allocator used by DecodeMemoryToTarget. Uses preallocated memory
302 * provided if the bm is 8888. Otherwise, uses a heap allocator. The same
303 * allocator will be used again for a copy to 8888, when the preallocated
304 * memory will be used.
305 */
306class TargetAllocator : public SkBitmap::HeapAllocator {
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000307
308public:
309 TargetAllocator(void* target)
310 : fTarget(target) {}
311
312 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
scroggo@google.com67882dd2013-05-20 16:58:16 +0000313 // If the config is not 8888, allocate a pixelref using the heap.
314 // fTarget will be used to store the final pixels when copied to
315 // 8888.
316 if (bm->config() != SkBitmap::kARGB_8888_Config) {
317 return INHERITED::allocPixelRef(bm, ct);
318 }
319 // In kARGB_8888_Config, there is no colortable.
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000320 SkASSERT(NULL == ct);
321 bm->setPixels(fTarget);
322 return true;
323 }
324
325private:
326 void* fTarget;
scroggo@google.com67882dd2013-05-20 16:58:16 +0000327 typedef SkBitmap::HeapAllocator INHERITED;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000328};
329
scroggo@google.com67882dd2013-05-20 16:58:16 +0000330/**
331 * Helper function for DecodeMemoryToTarget. DecodeMemoryToTarget wants
332 * 8888, so set the config to it. All parameters must not be null.
333 * @param decoder Decoder appropriate for this stream.
334 * @param stream Rewound stream to the encoded data.
335 * @param bitmap On success, will have its bounds set to the bounds of the
336 * encoded data, and its config set to 8888.
337 * @return True if the bounds were decoded and the bitmap is 8888 or can be
338 * copied to 8888.
339 */
340static bool decode_bounds_to_8888(SkImageDecoder* decoder, SkStream* stream,
341 SkBitmap* bitmap) {
342 SkASSERT(decoder != NULL);
343 SkASSERT(stream != NULL);
344 SkASSERT(bitmap != NULL);
345
346 if (!decoder->decode(stream, bitmap, SkImageDecoder::kDecodeBounds_Mode)) {
347 return false;
348 }
349
350 if (bitmap->config() == SkBitmap::kARGB_8888_Config) {
351 return true;
352 }
353
354 if (!bitmap->canCopyTo(SkBitmap::kARGB_8888_Config)) {
355 return false;
356 }
357
358 bitmap->setConfig(SkBitmap::kARGB_8888_Config, bitmap->width(), bitmap->height());
359 return true;
360}
361
362/**
363 * Helper function for DecodeMemoryToTarget. Decodes the stream into bitmap, and if
364 * the bitmap is not 8888, then it is copied to 8888. Either way, the end result has
365 * its pixels stored in target. All parameters must not be null.
366 * @param decoder Decoder appropriate for this stream.
367 * @param stream Rewound stream to the encoded data.
368 * @param bitmap On success, will contain the decoded image, with its pixels stored
369 * at target.
370 * @param target Preallocated memory for storing pixels.
371 * @return bool Whether the decode (and copy, if necessary) succeeded.
372 */
373static bool decode_pixels_to_8888(SkImageDecoder* decoder, SkStream* stream,
374 SkBitmap* bitmap, void* target) {
375 SkASSERT(decoder != NULL);
376 SkASSERT(stream != NULL);
377 SkASSERT(bitmap != NULL);
378 SkASSERT(target != NULL);
379
380 TargetAllocator allocator(target);
381 decoder->setAllocator(&allocator);
382
383 bool success = decoder->decode(stream, bitmap, SkImageDecoder::kDecodePixels_Mode);
384 decoder->setAllocator(NULL);
385
386 if (!success) {
387 return false;
388 }
389
390 if (bitmap->config() == SkBitmap::kARGB_8888_Config) {
391 return true;
392 }
393
394 SkBitmap bm8888;
395 if (!bitmap->copyTo(&bm8888, SkBitmap::kARGB_8888_Config, &allocator)) {
396 return false;
397 }
398
399 bitmap->swap(bm8888);
400 return true;
401}
402
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000403bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
404 SkImage::Info* info,
405 const SkBitmapFactory::Target* target) {
406 if (NULL == info) {
407 return false;
408 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000409
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000410 // FIXME: Just to get this working, implement in terms of existing
411 // ImageDecoder calls.
412 SkBitmap bm;
413 SkMemoryStream stream(buffer, size);
414 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
scroggo@google.com67882dd2013-05-20 16:58:16 +0000415 if (NULL == decoder.get()) {
416 return false;
417 }
418
419 if (!decode_bounds_to_8888(decoder.get(), &stream, &bm)) {
420 return false;
421 }
422
423 SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config);
424
425 // Now set info properly.
426 // Since Config is SkBitmap::kARGB_8888_Config, SkBitmapToImageInfo
427 // will always succeed.
428 SkAssertResult(SkBitmapToImageInfo(bm, info));
429
430 if (NULL == target) {
431 return true;
432 }
433
434 if (target->fRowBytes != SkToU32(bm.rowBytes())) {
435 if (target->fRowBytes < SkImageMinRowBytes(*info)) {
436 SkASSERT(!"Desired row bytes is too small");
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000437 return false;
438 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000439 bm.setConfig(bm.config(), bm.width(), bm.height(), target->fRowBytes);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000440 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000441
442 // SkMemoryStream.rewind() will always return true.
443 SkAssertResult(stream.rewind());
444 return decode_pixels_to_8888(decoder.get(), &stream, &bm, target->fAddr);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000445}
446
447
reed@android.com8a1c16f2008-12-17 15:59:43 +0000448bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000449 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 SkASSERT(stream);
451 SkASSERT(bm);
452
453 bool success = false;
454 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
455
456 if (NULL != codec) {
457 success = codec->decode(stream, bm, pref, mode);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000458 if (success && format) {
459 *format = codec->getFormat();
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000460 if (kUnknown_Format == *format) {
461 if (stream->rewind()) {
462 *format = GetStreamFormat(stream);
463 }
464 }
reed@android.comb3ade9d2009-06-15 13:04:45 +0000465 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000466 delete codec;
467 }
468 return success;
469}