blob: 52c43c83f17e8616397bb34c5875a6c14f8226f5 [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)
43 , fPreferQualityOverSpeed(false) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000044}
45
46SkImageDecoder::~SkImageDecoder() {
reed@google.com82065d62011-02-07 15:30:46 +000047 SkSafeUnref(fPeeker);
48 SkSafeUnref(fChooser);
49 SkSafeUnref(fAllocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +000050}
51
52SkImageDecoder::Format SkImageDecoder::getFormat() const {
53 return kUnknown_Format;
54}
55
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000056const char* SkImageDecoder::getFormatName() const {
scroggo@google.comf98118e2013-05-15 14:53:49 +000057 return GetFormatName(this->getFormat());
58}
59
60const char* SkImageDecoder::GetFormatName(Format format) {
61 switch (format) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +000062 case kUnknown_Format:
63 return "Unknown Format";
64 case kBMP_Format:
65 return "BMP";
66 case kGIF_Format:
67 return "GIF";
68 case kICO_Format:
69 return "ICO";
70 case kJPEG_Format:
71 return "JPEG";
72 case kPNG_Format:
73 return "PNG";
74 case kWBMP_Format:
75 return "WBMP";
76 case kWEBP_Format:
77 return "WEBP";
78 default:
79 SkASSERT(!"Invalid format type!");
80 }
81 return "Unknown Format";
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000082}
83
reed@android.com8a1c16f2008-12-17 15:59:43 +000084SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
85 SkRefCnt_SafeAssign(fPeeker, peeker);
86 return peeker;
87}
88
89SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
90 SkRefCnt_SafeAssign(fChooser, chooser);
91 return chooser;
92}
93
94SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
95 SkRefCnt_SafeAssign(fAllocator, alloc);
96 return alloc;
97}
98
99void SkImageDecoder::setSampleSize(int size) {
100 if (size < 1) {
101 size = 1;
102 }
103 fSampleSize = size;
104}
105
106bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
107 int height) const {
108 Chooser* chooser = fChooser;
109
110 if (NULL == chooser) { // no chooser, we just say YES to decoding :)
111 return true;
112 }
113 chooser->begin(1);
114 chooser->inspect(0, config, width, height);
115 return chooser->choose() == 0;
116}
117
118bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
119 SkColorTable* ctable) const {
120 return bitmap->allocPixels(fAllocator, ctable);
121}
122
123///////////////////////////////////////////////////////////////////////////////
reed@android.comb6137c32009-07-29 20:56:52 +0000124
reed@android.com3f1f06a2010-03-03 21:04:12 +0000125void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
126 if (NULL == pref) {
127 fUsePrefTable = false;
128 } else {
129 fUsePrefTable = true;
130 memcpy(fPrefTable, pref, sizeof(fPrefTable));
131 }
132}
133
134SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
135 bool srcHasAlpha) const {
136 SkBitmap::Config config;
137
138 if (fUsePrefTable) {
139 int index = 0;
140 switch (srcDepth) {
141 case kIndex_SrcDepth:
142 index = 0;
143 break;
144 case k16Bit_SrcDepth:
145 index = 2;
146 break;
147 case k32Bit_SrcDepth:
148 index = 4;
149 break;
150 }
151 if (srcHasAlpha) {
152 index += 1;
153 }
154 config = fPrefTable[index];
155 } else {
156 config = fDefaultPref;
157 }
158
159 if (SkBitmap::kNo_Config == config) {
160 config = SkImageDecoder::GetDeviceConfig();
161 }
162 return config;
163}
164
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000166 SkBitmap::Config pref, Mode mode, bool reuseBitmap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 // we reset this to false before calling onDecode
168 fShouldCancelDecode = false;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000169 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
170 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000171
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000172 if (reuseBitmap) {
173 SkAutoLockPixels alp(*bm);
174 if (NULL != bm->getPixels()) {
175 return this->onDecode(stream, bm, mode);
176 }
177 }
178
179 // pass a temporary bitmap, so that if we return false, we are assured of
180 // leaving the caller's bitmap untouched.
181 SkBitmap tmp;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000182 if (!this->onDecode(stream, &tmp, mode)) {
reed@android.com62900b42009-02-11 15:07:19 +0000183 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000184 }
reed@android.com62900b42009-02-11 15:07:19 +0000185 bm->swap(tmp);
186 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000187}
188
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000189bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000190 SkBitmap::Config pref) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000191 // we reset this to false before calling onDecodeSubset
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000192 fShouldCancelDecode = false;
193 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
194 fDefaultPref = pref;
195
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000196 return this->onDecodeSubset(bm, rect);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000197}
198
199bool SkImageDecoder::buildTileIndex(SkStream* stream,
200 int *width, int *height) {
201 // we reset this to false before calling onBuildTileIndex
202 fShouldCancelDecode = false;
203
204 return this->onBuildTileIndex(stream, width, height);
205}
206
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000207bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
208 int dstX, int dstY, int width, int height,
209 int srcX, int srcY) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000210 int w = width / sampleSize;
211 int h = height / sampleSize;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000212 if (src->getConfig() == SkBitmap::kIndex8_Config) {
213 // kIndex8 does not allow drawing via an SkCanvas, as is done below.
214 // Instead, use extractSubset. Note that this shares the SkPixelRef and
215 // SkColorTable.
216 // FIXME: Since src is discarded in practice, this holds on to more
217 // pixels than is strictly necessary. Switch to a copy if memory
218 // savings are more important than speed here. This also means
219 // that the pixels in dst can not be reused (though there is no
220 // allocation, which was already done on src).
221 int x = (dstX - srcX) / sampleSize;
222 int y = (dstY - srcY) / sampleSize;
223 SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
224 return src->extractSubset(dst, subset);
225 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000226 // if the destination has no pixels then we must allocate them.
227 if (dst->isNull()) {
228 dst->setConfig(src->getConfig(), w, h);
229 dst->setIsOpaque(src->isOpaque());
230
231 if (!this->allocPixelRef(dst, NULL)) {
232 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000233 return false;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000234 }
235 }
236 // check to see if the destination is large enough to decode the desired
237 // region. If this assert fails we will just draw as much of the source
238 // into the destination that we can.
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000239 if (dst->width() < w || dst->height() < h) {
240 SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n"));
241 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000242
243 // Set the Src_Mode for the paint to prevent transparency issue in the
244 // dest in the event that the dest was being re-used.
245 SkPaint paint;
246 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
247
248 SkCanvas canvas(*dst);
249 canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
250 (srcY - dstY) / sampleSize,
251 &paint);
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000252 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000253}
254
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255///////////////////////////////////////////////////////////////////////////////
256
257bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000258 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000259 SkASSERT(file);
260 SkASSERT(bm);
261
mike@reedtribe.orgf3811622013-03-19 02:18:33 +0000262 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(file));
263 if (stream.get()) {
264 if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 bm->pixelRef()->setURI(file);
tomhudson@google.com1a366212012-01-03 14:42:08 +0000266 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000267 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 }
269 return false;
270}
271
272bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000273 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 if (0 == size) {
275 return false;
276 }
277 SkASSERT(buffer);
278
279 SkMemoryStream stream(buffer, size);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000280 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000281}
282
scroggo@google.com67882dd2013-05-20 16:58:16 +0000283/**
284 * Special allocator used by DecodeMemoryToTarget. Uses preallocated memory
285 * provided if the bm is 8888. Otherwise, uses a heap allocator. The same
286 * allocator will be used again for a copy to 8888, when the preallocated
287 * memory will be used.
288 */
289class TargetAllocator : public SkBitmap::HeapAllocator {
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000290
291public:
292 TargetAllocator(void* target)
293 : fTarget(target) {}
294
295 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
scroggo@google.com67882dd2013-05-20 16:58:16 +0000296 // If the config is not 8888, allocate a pixelref using the heap.
297 // fTarget will be used to store the final pixels when copied to
298 // 8888.
299 if (bm->config() != SkBitmap::kARGB_8888_Config) {
300 return INHERITED::allocPixelRef(bm, ct);
301 }
302 // In kARGB_8888_Config, there is no colortable.
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000303 SkASSERT(NULL == ct);
304 bm->setPixels(fTarget);
305 return true;
306 }
307
308private:
309 void* fTarget;
scroggo@google.com67882dd2013-05-20 16:58:16 +0000310 typedef SkBitmap::HeapAllocator INHERITED;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000311};
312
scroggo@google.com67882dd2013-05-20 16:58:16 +0000313/**
314 * Helper function for DecodeMemoryToTarget. DecodeMemoryToTarget wants
315 * 8888, so set the config to it. All parameters must not be null.
316 * @param decoder Decoder appropriate for this stream.
317 * @param stream Rewound stream to the encoded data.
318 * @param bitmap On success, will have its bounds set to the bounds of the
319 * encoded data, and its config set to 8888.
320 * @return True if the bounds were decoded and the bitmap is 8888 or can be
321 * copied to 8888.
322 */
323static bool decode_bounds_to_8888(SkImageDecoder* decoder, SkStream* stream,
324 SkBitmap* bitmap) {
325 SkASSERT(decoder != NULL);
326 SkASSERT(stream != NULL);
327 SkASSERT(bitmap != NULL);
328
329 if (!decoder->decode(stream, bitmap, SkImageDecoder::kDecodeBounds_Mode)) {
330 return false;
331 }
332
333 if (bitmap->config() == SkBitmap::kARGB_8888_Config) {
334 return true;
335 }
336
337 if (!bitmap->canCopyTo(SkBitmap::kARGB_8888_Config)) {
338 return false;
339 }
340
341 bitmap->setConfig(SkBitmap::kARGB_8888_Config, bitmap->width(), bitmap->height());
342 return true;
343}
344
345/**
346 * Helper function for DecodeMemoryToTarget. Decodes the stream into bitmap, and if
347 * the bitmap is not 8888, then it is copied to 8888. Either way, the end result has
348 * its pixels stored in target. All parameters must not be null.
349 * @param decoder Decoder appropriate for this stream.
350 * @param stream Rewound stream to the encoded data.
351 * @param bitmap On success, will contain the decoded image, with its pixels stored
352 * at target.
353 * @param target Preallocated memory for storing pixels.
354 * @return bool Whether the decode (and copy, if necessary) succeeded.
355 */
356static bool decode_pixels_to_8888(SkImageDecoder* decoder, SkStream* stream,
357 SkBitmap* bitmap, void* target) {
358 SkASSERT(decoder != NULL);
359 SkASSERT(stream != NULL);
360 SkASSERT(bitmap != NULL);
361 SkASSERT(target != NULL);
362
363 TargetAllocator allocator(target);
364 decoder->setAllocator(&allocator);
365
366 bool success = decoder->decode(stream, bitmap, SkImageDecoder::kDecodePixels_Mode);
367 decoder->setAllocator(NULL);
368
369 if (!success) {
370 return false;
371 }
372
373 if (bitmap->config() == SkBitmap::kARGB_8888_Config) {
374 return true;
375 }
376
377 SkBitmap bm8888;
378 if (!bitmap->copyTo(&bm8888, SkBitmap::kARGB_8888_Config, &allocator)) {
379 return false;
380 }
381
382 bitmap->swap(bm8888);
383 return true;
384}
385
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000386bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
387 SkImage::Info* info,
388 const SkBitmapFactory::Target* target) {
389 if (NULL == info) {
390 return false;
391 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000392
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000393 // FIXME: Just to get this working, implement in terms of existing
394 // ImageDecoder calls.
395 SkBitmap bm;
396 SkMemoryStream stream(buffer, size);
397 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
scroggo@google.com67882dd2013-05-20 16:58:16 +0000398 if (NULL == decoder.get()) {
399 return false;
400 }
401
402 if (!decode_bounds_to_8888(decoder.get(), &stream, &bm)) {
403 return false;
404 }
405
406 SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config);
407
408 // Now set info properly.
409 // Since Config is SkBitmap::kARGB_8888_Config, SkBitmapToImageInfo
410 // will always succeed.
411 SkAssertResult(SkBitmapToImageInfo(bm, info));
412
413 if (NULL == target) {
414 return true;
415 }
416
417 if (target->fRowBytes != SkToU32(bm.rowBytes())) {
418 if (target->fRowBytes < SkImageMinRowBytes(*info)) {
419 SkASSERT(!"Desired row bytes is too small");
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000420 return false;
421 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000422 bm.setConfig(bm.config(), bm.width(), bm.height(), target->fRowBytes);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000423 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000424
425 // SkMemoryStream.rewind() will always return true.
426 SkAssertResult(stream.rewind());
427 return decode_pixels_to_8888(decoder.get(), &stream, &bm, target->fAddr);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000428}
429
430
reed@android.com8a1c16f2008-12-17 15:59:43 +0000431bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000432 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000433 SkASSERT(stream);
434 SkASSERT(bm);
435
436 bool success = false;
437 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
438
439 if (NULL != codec) {
440 success = codec->decode(stream, bm, pref, mode);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000441 if (success && format) {
442 *format = codec->getFormat();
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000443 if (kUnknown_Format == *format) {
444 if (stream->rewind()) {
445 *format = GetStreamFormat(stream);
446 }
447 }
reed@android.comb3ade9d2009-06-15 13:04:45 +0000448 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000449 delete codec;
450 }
451 return success;
452}
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000453
scroggo@google.com82449982013-05-01 21:40:58 +0000454/**
455 * This function leaks, but that is okay because it is not intended
456 * to be called. It is only here so that the linker will include the
457 * decoders.
458 * Make sure to keep it in sync with images.gyp, so only the encoders
459 * which are created on a platform are linked.
460 */
461void force_linking();
462void force_linking() {
463 SkASSERT(false);
464 CreateJPEGImageDecoder();
465 CreateWEBPImageDecoder();
466 CreateBMPImageDecoder();
467 CreateICOImageDecoder();
468 CreateWBMPImageDecoder();
469 // Only link GIF and PNG on platforms that build them. See images.gyp
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000470#if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN) && !defined(SK_BUILD_FOR_NACL)
scroggo@google.com82449982013-05-01 21:40:58 +0000471 CreateGIFImageDecoder();
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000472#endif
473#if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN)
scroggo@google.com82449982013-05-01 21:40:58 +0000474 CreatePNGImageDecoder();
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000475#endif
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000476}