blob: 15cd1a60c0dd6dc27bf517f0a85d9453e5301e70 [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.com8d239242013-10-01 17:27:15 +000043 , fSkipWritingZeroes(false)
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000044 , fPreferQualityOverSpeed(false)
45 , fRequireUnpremultipliedColors(false) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000046}
47
48SkImageDecoder::~SkImageDecoder() {
reed@google.com82065d62011-02-07 15:30:46 +000049 SkSafeUnref(fPeeker);
50 SkSafeUnref(fChooser);
51 SkSafeUnref(fAllocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +000052}
53
scroggo@google.com468142b2013-07-09 15:48:24 +000054void SkImageDecoder::copyFieldsToOther(SkImageDecoder* other) {
55 if (NULL == other) {
56 return;
57 }
58 other->setPeeker(fPeeker);
59 other->setChooser(fChooser);
60 other->setAllocator(fAllocator);
61 other->setSampleSize(fSampleSize);
62 if (fUsePrefTable) {
63 other->setPrefConfigTable(fPrefTable);
64 } else {
65 other->fDefaultPref = fDefaultPref;
66 }
scroggo@google.com8d239242013-10-01 17:27:15 +000067 other->setDitherImage(fDitherImage);
68 other->setSkipWritingZeroes(fSkipWritingZeroes);
scroggo@google.com468142b2013-07-09 15:48:24 +000069 other->setPreferQualityOverSpeed(fPreferQualityOverSpeed);
70 other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors);
71}
72
reed@android.com8a1c16f2008-12-17 15:59:43 +000073SkImageDecoder::Format SkImageDecoder::getFormat() const {
74 return kUnknown_Format;
75}
76
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000077const char* SkImageDecoder::getFormatName() const {
scroggo@google.comf98118e2013-05-15 14:53:49 +000078 return GetFormatName(this->getFormat());
79}
80
81const char* SkImageDecoder::GetFormatName(Format format) {
82 switch (format) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +000083 case kUnknown_Format:
84 return "Unknown Format";
85 case kBMP_Format:
86 return "BMP";
87 case kGIF_Format:
88 return "GIF";
89 case kICO_Format:
90 return "ICO";
91 case kJPEG_Format:
92 return "JPEG";
93 case kPNG_Format:
94 return "PNG";
95 case kWBMP_Format:
96 return "WBMP";
97 case kWEBP_Format:
98 return "WEBP";
99 default:
mtklein@google.com330313a2013-08-22 15:37:26 +0000100 SkDEBUGFAIL("Invalid format type!");
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000101 }
102 return "Unknown Format";
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000103}
104
reed@android.com8a1c16f2008-12-17 15:59:43 +0000105SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
106 SkRefCnt_SafeAssign(fPeeker, peeker);
107 return peeker;
108}
109
110SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
111 SkRefCnt_SafeAssign(fChooser, chooser);
112 return chooser;
113}
114
115SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
116 SkRefCnt_SafeAssign(fAllocator, alloc);
117 return alloc;
118}
119
120void SkImageDecoder::setSampleSize(int size) {
121 if (size < 1) {
122 size = 1;
123 }
124 fSampleSize = size;
125}
126
127bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
128 int height) const {
129 Chooser* chooser = fChooser;
130
131 if (NULL == chooser) { // no chooser, we just say YES to decoding :)
132 return true;
133 }
134 chooser->begin(1);
135 chooser->inspect(0, config, width, height);
136 return chooser->choose() == 0;
137}
138
139bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
140 SkColorTable* ctable) const {
141 return bitmap->allocPixels(fAllocator, ctable);
142}
143
144///////////////////////////////////////////////////////////////////////////////
reed@android.comb6137c32009-07-29 20:56:52 +0000145
reed@android.com3f1f06a2010-03-03 21:04:12 +0000146void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
147 if (NULL == pref) {
148 fUsePrefTable = false;
149 } else {
150 fUsePrefTable = true;
scroggo@google.comf698c822013-07-18 19:34:49 +0000151 fPrefTable.fPrefFor_8Index_NoAlpha_src = pref[0];
152 fPrefTable.fPrefFor_8Index_YesAlpha_src = pref[1];
153 fPrefTable.fPrefFor_8Gray_src = SkBitmap::kNo_Config;
154 fPrefTable.fPrefFor_8bpc_NoAlpha_src = pref[4];
155 fPrefTable.fPrefFor_8bpc_YesAlpha_src = pref[5];
reed@android.com3f1f06a2010-03-03 21:04:12 +0000156 }
157}
158
scroggo@google.comf698c822013-07-18 19:34:49 +0000159void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) {
160 fUsePrefTable = true;
161 fPrefTable = prefTable;
162}
163
reed@android.com3f1f06a2010-03-03 21:04:12 +0000164SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
165 bool srcHasAlpha) const {
scroggo@google.com12d06422013-07-18 19:42:35 +0000166 SkBitmap::Config config = SkBitmap::kNo_Config;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000167
168 if (fUsePrefTable) {
reed@android.com3f1f06a2010-03-03 21:04:12 +0000169 switch (srcDepth) {
170 case kIndex_SrcDepth:
scroggo@google.comf698c822013-07-18 19:34:49 +0000171 config = srcHasAlpha ? fPrefTable.fPrefFor_8Index_YesAlpha_src
172 : fPrefTable.fPrefFor_8Index_NoAlpha_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000173 break;
scroggo@google.comf698c822013-07-18 19:34:49 +0000174 case k8BitGray_SrcDepth:
175 config = fPrefTable.fPrefFor_8Gray_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000176 break;
177 case k32Bit_SrcDepth:
scroggo@google.comf698c822013-07-18 19:34:49 +0000178 config = srcHasAlpha ? fPrefTable.fPrefFor_8bpc_YesAlpha_src
179 : fPrefTable.fPrefFor_8bpc_NoAlpha_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000180 break;
181 }
reed@android.com3f1f06a2010-03-03 21:04:12 +0000182 } else {
183 config = fDefaultPref;
184 }
185
186 if (SkBitmap::kNo_Config == config) {
187 config = SkImageDecoder::GetDeviceConfig();
188 }
189 return config;
190}
191
reed@android.com8a1c16f2008-12-17 15:59:43 +0000192bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
scroggo@google.combc69ce92013-07-09 15:45:14 +0000193 SkBitmap::Config pref, Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 // we reset this to false before calling onDecode
195 fShouldCancelDecode = false;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000196 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
197 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000199 // pass a temporary bitmap, so that if we return false, we are assured of
200 // leaving the caller's bitmap untouched.
201 SkBitmap tmp;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000202 if (!this->onDecode(stream, &tmp, mode)) {
reed@android.com62900b42009-02-11 15:07:19 +0000203 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000204 }
reed@android.com62900b42009-02-11 15:07:19 +0000205 bm->swap(tmp);
206 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000207}
208
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000209bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000210 SkBitmap::Config pref) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000211 // we reset this to false before calling onDecodeSubset
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000212 fShouldCancelDecode = false;
213 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
214 fDefaultPref = pref;
215
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000216 return this->onDecodeSubset(bm, rect);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000217}
218
scroggo@google.comb5571b32013-09-25 21:34:24 +0000219bool SkImageDecoder::buildTileIndex(SkStreamRewindable* stream,
220 int *width, int *height) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000221 // we reset this to false before calling onBuildTileIndex
222 fShouldCancelDecode = false;
223
224 return this->onBuildTileIndex(stream, width, height);
225}
226
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000227bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
228 int dstX, int dstY, int width, int height,
229 int srcX, int srcY) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000230 int w = width / sampleSize;
231 int h = height / sampleSize;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000232 if (src->getConfig() == SkBitmap::kIndex8_Config) {
233 // kIndex8 does not allow drawing via an SkCanvas, as is done below.
234 // Instead, use extractSubset. Note that this shares the SkPixelRef and
235 // SkColorTable.
236 // FIXME: Since src is discarded in practice, this holds on to more
237 // pixels than is strictly necessary. Switch to a copy if memory
238 // savings are more important than speed here. This also means
239 // that the pixels in dst can not be reused (though there is no
240 // allocation, which was already done on src).
241 int x = (dstX - srcX) / sampleSize;
242 int y = (dstY - srcY) / sampleSize;
243 SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
244 return src->extractSubset(dst, subset);
245 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000246 // if the destination has no pixels then we must allocate them.
247 if (dst->isNull()) {
248 dst->setConfig(src->getConfig(), w, h);
249 dst->setIsOpaque(src->isOpaque());
250
251 if (!this->allocPixelRef(dst, NULL)) {
252 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000253 return false;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000254 }
255 }
256 // check to see if the destination is large enough to decode the desired
257 // region. If this assert fails we will just draw as much of the source
258 // into the destination that we can.
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000259 if (dst->width() < w || dst->height() < h) {
260 SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n"));
261 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000262
263 // Set the Src_Mode for the paint to prevent transparency issue in the
264 // dest in the event that the dest was being re-used.
265 SkPaint paint;
266 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
267
268 SkCanvas canvas(*dst);
269 canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
270 (srcY - dstY) / sampleSize,
271 &paint);
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000272 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000273}
274
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275///////////////////////////////////////////////////////////////////////////////
276
277bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000278 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000279 SkASSERT(file);
280 SkASSERT(bm);
281
scroggo@google.comb5571b32013-09-25 21:34:24 +0000282 SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(file));
mike@reedtribe.orgf3811622013-03-19 02:18:33 +0000283 if (stream.get()) {
284 if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 bm->pixelRef()->setURI(file);
tomhudson@google.com1a366212012-01-03 14:42:08 +0000286 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 }
289 return false;
290}
291
292bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000293 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000294 if (0 == size) {
295 return false;
296 }
297 SkASSERT(buffer);
298
299 SkMemoryStream stream(buffer, size);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000300 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000301}
302
scroggo@google.com67882dd2013-05-20 16:58:16 +0000303/**
304 * Special allocator used by DecodeMemoryToTarget. Uses preallocated memory
305 * provided if the bm is 8888. Otherwise, uses a heap allocator. The same
306 * allocator will be used again for a copy to 8888, when the preallocated
307 * memory will be used.
308 */
309class TargetAllocator : public SkBitmap::HeapAllocator {
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000310
311public:
312 TargetAllocator(void* target)
313 : fTarget(target) {}
314
315 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
scroggo@google.com67882dd2013-05-20 16:58:16 +0000316 // If the config is not 8888, allocate a pixelref using the heap.
317 // fTarget will be used to store the final pixels when copied to
318 // 8888.
319 if (bm->config() != SkBitmap::kARGB_8888_Config) {
320 return INHERITED::allocPixelRef(bm, ct);
321 }
322 // In kARGB_8888_Config, there is no colortable.
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000323 SkASSERT(NULL == ct);
324 bm->setPixels(fTarget);
325 return true;
326 }
327
328private:
329 void* fTarget;
scroggo@google.com67882dd2013-05-20 16:58:16 +0000330 typedef SkBitmap::HeapAllocator INHERITED;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000331};
332
scroggo@google.com67882dd2013-05-20 16:58:16 +0000333/**
334 * Helper function for DecodeMemoryToTarget. DecodeMemoryToTarget wants
335 * 8888, so set the config to it. All parameters must not be null.
336 * @param decoder Decoder appropriate for this stream.
337 * @param stream Rewound stream to the encoded data.
338 * @param bitmap On success, will have its bounds set to the bounds of the
339 * encoded data, and its config set to 8888.
340 * @return True if the bounds were decoded and the bitmap is 8888 or can be
341 * copied to 8888.
342 */
343static bool decode_bounds_to_8888(SkImageDecoder* decoder, SkStream* stream,
344 SkBitmap* bitmap) {
345 SkASSERT(decoder != NULL);
346 SkASSERT(stream != NULL);
347 SkASSERT(bitmap != NULL);
348
349 if (!decoder->decode(stream, bitmap, SkImageDecoder::kDecodeBounds_Mode)) {
350 return false;
351 }
352
353 if (bitmap->config() == SkBitmap::kARGB_8888_Config) {
354 return true;
355 }
356
357 if (!bitmap->canCopyTo(SkBitmap::kARGB_8888_Config)) {
358 return false;
359 }
360
361 bitmap->setConfig(SkBitmap::kARGB_8888_Config, bitmap->width(), bitmap->height());
362 return true;
363}
364
365/**
366 * Helper function for DecodeMemoryToTarget. Decodes the stream into bitmap, and if
367 * the bitmap is not 8888, then it is copied to 8888. Either way, the end result has
368 * its pixels stored in target. All parameters must not be null.
369 * @param decoder Decoder appropriate for this stream.
370 * @param stream Rewound stream to the encoded data.
371 * @param bitmap On success, will contain the decoded image, with its pixels stored
372 * at target.
373 * @param target Preallocated memory for storing pixels.
374 * @return bool Whether the decode (and copy, if necessary) succeeded.
375 */
376static bool decode_pixels_to_8888(SkImageDecoder* decoder, SkStream* stream,
377 SkBitmap* bitmap, void* target) {
378 SkASSERT(decoder != NULL);
379 SkASSERT(stream != NULL);
380 SkASSERT(bitmap != NULL);
381 SkASSERT(target != NULL);
382
383 TargetAllocator allocator(target);
384 decoder->setAllocator(&allocator);
385
386 bool success = decoder->decode(stream, bitmap, SkImageDecoder::kDecodePixels_Mode);
387 decoder->setAllocator(NULL);
388
389 if (!success) {
390 return false;
391 }
392
393 if (bitmap->config() == SkBitmap::kARGB_8888_Config) {
394 return true;
395 }
396
397 SkBitmap bm8888;
398 if (!bitmap->copyTo(&bm8888, SkBitmap::kARGB_8888_Config, &allocator)) {
399 return false;
400 }
401
402 bitmap->swap(bm8888);
403 return true;
404}
405
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000406bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
407 SkImage::Info* info,
408 const SkBitmapFactory::Target* target) {
409 if (NULL == info) {
410 return false;
411 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000412
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000413 // FIXME: Just to get this working, implement in terms of existing
414 // ImageDecoder calls.
415 SkBitmap bm;
416 SkMemoryStream stream(buffer, size);
417 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
scroggo@google.com67882dd2013-05-20 16:58:16 +0000418 if (NULL == decoder.get()) {
419 return false;
420 }
421
422 if (!decode_bounds_to_8888(decoder.get(), &stream, &bm)) {
423 return false;
424 }
425
426 SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config);
427
428 // Now set info properly.
429 // Since Config is SkBitmap::kARGB_8888_Config, SkBitmapToImageInfo
430 // will always succeed.
431 SkAssertResult(SkBitmapToImageInfo(bm, info));
432
433 if (NULL == target) {
434 return true;
435 }
436
437 if (target->fRowBytes != SkToU32(bm.rowBytes())) {
438 if (target->fRowBytes < SkImageMinRowBytes(*info)) {
mtklein@google.com330313a2013-08-22 15:37:26 +0000439 SkDEBUGFAIL("Desired row bytes is too small");
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000440 return false;
441 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000442 bm.setConfig(bm.config(), bm.width(), bm.height(), target->fRowBytes);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000443 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000444
445 // SkMemoryStream.rewind() will always return true.
446 SkAssertResult(stream.rewind());
447 return decode_pixels_to_8888(decoder.get(), &stream, &bm, target->fAddr);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000448}
449
450
scroggo@google.comb5571b32013-09-25 21:34:24 +0000451bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm,
452 SkBitmap::Config pref, Mode mode,
453 Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000454 SkASSERT(stream);
455 SkASSERT(bm);
456
457 bool success = false;
458 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
459
460 if (NULL != codec) {
461 success = codec->decode(stream, bm, pref, mode);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000462 if (success && format) {
463 *format = codec->getFormat();
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000464 if (kUnknown_Format == *format) {
465 if (stream->rewind()) {
466 *format = GetStreamFormat(stream);
467 }
468 }
reed@android.comb3ade9d2009-06-15 13:04:45 +0000469 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000470 delete codec;
471 }
472 return success;
473}