blob: fcba91069a43faaf7fa1341d7af2fae741fd1a9e [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
reed@android.com8a1c16f2008-12-17 15:59:43 +000017SkImageDecoder::SkImageDecoder()
scroggo@google.com7e6fcee2013-05-03 20:14:28 +000018 : fPeeker(NULL)
reed5926b862014-06-11 10:33:13 -070019#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
scroggo@google.com7e6fcee2013-05-03 20:14:28 +000020 , fChooser(NULL)
reed5926b862014-06-11 10:33:13 -070021#endif
scroggo@google.com7e6fcee2013-05-03 20:14:28 +000022 , fAllocator(NULL)
23 , fSampleSize(1)
reedbfefc7c2014-06-12 17:40:00 -070024 , fDefaultPref(kUnknown_SkColorType)
reedbe08ace2014-07-08 11:15:59 -070025 , fPreserveSrcDepth(false)
scroggo@google.com7e6fcee2013-05-03 20:14:28 +000026 , fDitherImage(true)
reedc3b32662014-06-17 08:38:31 -070027#ifdef SK_SUPPORT_LEGACY_BITMAP_CONFIG
scroggo@google.com7e6fcee2013-05-03 20:14:28 +000028 , fUsePrefTable(false)
reedc3b32662014-06-17 08:38:31 -070029#endif
scroggo@google.com8d239242013-10-01 17:27:15 +000030 , fSkipWritingZeroes(false)
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000031 , fPreferQualityOverSpeed(false)
32 , fRequireUnpremultipliedColors(false) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000033}
34
35SkImageDecoder::~SkImageDecoder() {
reed@google.com82065d62011-02-07 15:30:46 +000036 SkSafeUnref(fPeeker);
reed5926b862014-06-11 10:33:13 -070037#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed@google.com82065d62011-02-07 15:30:46 +000038 SkSafeUnref(fChooser);
reed5926b862014-06-11 10:33:13 -070039#endif
reed@google.com82065d62011-02-07 15:30:46 +000040 SkSafeUnref(fAllocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +000041}
42
scroggo@google.com468142b2013-07-09 15:48:24 +000043void SkImageDecoder::copyFieldsToOther(SkImageDecoder* other) {
44 if (NULL == other) {
45 return;
46 }
47 other->setPeeker(fPeeker);
reed5926b862014-06-11 10:33:13 -070048#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
scroggo@google.com468142b2013-07-09 15:48:24 +000049 other->setChooser(fChooser);
reed5926b862014-06-11 10:33:13 -070050#endif
scroggo@google.com468142b2013-07-09 15:48:24 +000051 other->setAllocator(fAllocator);
52 other->setSampleSize(fSampleSize);
reedc3b32662014-06-17 08:38:31 -070053#ifdef SK_SUPPORT_LEGACY_BITMAP_CONFIG
scroggo@google.com468142b2013-07-09 15:48:24 +000054 if (fUsePrefTable) {
55 other->setPrefConfigTable(fPrefTable);
56 } else {
57 other->fDefaultPref = fDefaultPref;
58 }
reedc3b32662014-06-17 08:38:31 -070059#endif
reedbe08ace2014-07-08 11:15:59 -070060 other->setPreserveSrcDepth(fPreserveSrcDepth);
scroggo@google.com8d239242013-10-01 17:27:15 +000061 other->setDitherImage(fDitherImage);
62 other->setSkipWritingZeroes(fSkipWritingZeroes);
scroggo@google.com468142b2013-07-09 15:48:24 +000063 other->setPreferQualityOverSpeed(fPreferQualityOverSpeed);
64 other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors);
65}
66
reed@android.com8a1c16f2008-12-17 15:59:43 +000067SkImageDecoder::Format SkImageDecoder::getFormat() const {
68 return kUnknown_Format;
69}
70
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000071const char* SkImageDecoder::getFormatName() const {
scroggo@google.comf98118e2013-05-15 14:53:49 +000072 return GetFormatName(this->getFormat());
73}
74
75const char* SkImageDecoder::GetFormatName(Format format) {
76 switch (format) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +000077 case kUnknown_Format:
78 return "Unknown Format";
79 case kBMP_Format:
80 return "BMP";
81 case kGIF_Format:
82 return "GIF";
83 case kICO_Format:
84 return "ICO";
robertphillips@google.com8cf81e02014-05-22 18:40:29 +000085 case kPKM_Format:
86 return "PKM";
krajcevski99ffe242014-06-03 13:04:35 -070087 case kKTX_Format:
88 return "KTX";
scroggo@google.com39edf4c2013-04-25 17:33:51 +000089 case kJPEG_Format:
90 return "JPEG";
91 case kPNG_Format:
92 return "PNG";
93 case kWBMP_Format:
94 return "WBMP";
95 case kWEBP_Format:
96 return "WEBP";
97 default:
mtklein@google.com330313a2013-08-22 15:37:26 +000098 SkDEBUGFAIL("Invalid format type!");
scroggo@google.com39edf4c2013-04-25 17:33:51 +000099 }
100 return "Unknown Format";
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000101}
102
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
104 SkRefCnt_SafeAssign(fPeeker, peeker);
105 return peeker;
106}
107
reed5926b862014-06-11 10:33:13 -0700108#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000109SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
110 SkRefCnt_SafeAssign(fChooser, chooser);
111 return chooser;
112}
reed5926b862014-06-11 10:33:13 -0700113#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114
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
reed5926b862014-06-11 10:33:13 -0700127#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed6c225732014-06-09 19:52:07 -0700128// TODO: change Chooser virtual to take colorType, so we can stop calling SkColorTypeToBitmapConfig
129//
130bool SkImageDecoder::chooseFromOneChoice(SkColorType colorType, int width, int height) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000131 Chooser* chooser = fChooser;
reed6c225732014-06-09 19:52:07 -0700132
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133 if (NULL == chooser) { // no chooser, we just say YES to decoding :)
134 return true;
135 }
136 chooser->begin(1);
reed6c225732014-06-09 19:52:07 -0700137 chooser->inspect(0, SkColorTypeToBitmapConfig(colorType), width, height);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 return chooser->choose() == 0;
139}
reed5926b862014-06-11 10:33:13 -0700140#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000141
142bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
143 SkColorTable* ctable) const {
144 return bitmap->allocPixels(fAllocator, ctable);
145}
146
147///////////////////////////////////////////////////////////////////////////////
reed@android.comb6137c32009-07-29 20:56:52 +0000148
reedc3b32662014-06-17 08:38:31 -0700149#ifdef SK_SUPPORT_LEGACY_BITMAP_CONFIG
scroggo@google.comf698c822013-07-18 19:34:49 +0000150void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) {
151 fUsePrefTable = true;
152 fPrefTable = prefTable;
153}
reedc3b32662014-06-17 08:38:31 -0700154#endif
scroggo@google.comf698c822013-07-18 19:34:49 +0000155
reed2f785a22014-06-12 09:21:31 -0700156// TODO: use colortype in fPrefTable, fDefaultPref so we can stop using SkBitmapConfigToColorType()
reed6c225732014-06-09 19:52:07 -0700157//
158SkColorType SkImageDecoder::getPrefColorType(SrcDepth srcDepth, bool srcHasAlpha) const {
reedbfefc7c2014-06-12 17:40:00 -0700159 SkColorType ct = fDefaultPref;
reedc3b32662014-06-17 08:38:31 -0700160#ifdef SK_SUPPORT_LEGACY_BITMAP_CONFIG
reed@android.com3f1f06a2010-03-03 21:04:12 +0000161 if (fUsePrefTable) {
reedbfefc7c2014-06-12 17:40:00 -0700162 // Until we kill or change the PrefTable, we have to go into Config land for a moment.
163 SkBitmap::Config config = SkBitmap::kNo_Config;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000164 switch (srcDepth) {
165 case kIndex_SrcDepth:
scroggo@google.comf698c822013-07-18 19:34:49 +0000166 config = srcHasAlpha ? fPrefTable.fPrefFor_8Index_YesAlpha_src
167 : fPrefTable.fPrefFor_8Index_NoAlpha_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000168 break;
scroggo@google.comf698c822013-07-18 19:34:49 +0000169 case k8BitGray_SrcDepth:
170 config = fPrefTable.fPrefFor_8Gray_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000171 break;
172 case k32Bit_SrcDepth:
scroggo@google.comf698c822013-07-18 19:34:49 +0000173 config = srcHasAlpha ? fPrefTable.fPrefFor_8bpc_YesAlpha_src
174 : fPrefTable.fPrefFor_8bpc_NoAlpha_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000175 break;
176 }
reedbfefc7c2014-06-12 17:40:00 -0700177 // now return to SkColorType land
178 ct = SkBitmapConfigToColorType(config);
reed@android.com3f1f06a2010-03-03 21:04:12 +0000179 }
reedc3b32662014-06-17 08:38:31 -0700180#endif
reedbe08ace2014-07-08 11:15:59 -0700181 if (fPreserveSrcDepth) {
182 switch (srcDepth) {
183 case kIndex_SrcDepth:
184 ct = kIndex_8_SkColorType;
185 break;
186 case k8BitGray_SrcDepth:
187 ct = kN32_SkColorType;
188 break;
189 case k32Bit_SrcDepth:
190 ct = kN32_SkColorType;
191 break;
192 }
193 }
reedbfefc7c2014-06-12 17:40:00 -0700194 return ct;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000195}
196
reedbfefc7c2014-06-12 17:40:00 -0700197bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, SkColorType pref, Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000198 // we reset this to false before calling onDecode
199 fShouldCancelDecode = false;
reedbfefc7c2014-06-12 17:40:00 -0700200 // assign this, for use by getPrefColorType(), in case fUsePrefTable is false
reed@android.com3f1f06a2010-03-03 21:04:12 +0000201 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000202
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000203 // pass a temporary bitmap, so that if we return false, we are assured of
204 // leaving the caller's bitmap untouched.
205 SkBitmap tmp;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000206 if (!this->onDecode(stream, &tmp, mode)) {
reed@android.com62900b42009-02-11 15:07:19 +0000207 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000208 }
reed@android.com62900b42009-02-11 15:07:19 +0000209 bm->swap(tmp);
210 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000211}
212
reedbfefc7c2014-06-12 17:40:00 -0700213bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect, SkColorType pref) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000214 // we reset this to false before calling onDecodeSubset
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000215 fShouldCancelDecode = false;
reedbfefc7c2014-06-12 17:40:00 -0700216 // assign this, for use by getPrefColorType(), in case fUsePrefTable is false
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000217 fDefaultPref = pref;
218
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000219 return this->onDecodeSubset(bm, rect);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000220}
221
reedbfefc7c2014-06-12 17:40:00 -0700222bool SkImageDecoder::buildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000223 // we reset this to false before calling onBuildTileIndex
224 fShouldCancelDecode = false;
225
226 return this->onBuildTileIndex(stream, width, height);
227}
228
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000229bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
230 int dstX, int dstY, int width, int height,
231 int srcX, int srcY) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000232 int w = width / sampleSize;
233 int h = height / sampleSize;
reed0689d7b2014-06-14 05:30:20 -0700234 if (src->colorType() == kIndex_8_SkColorType) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000235 // kIndex8 does not allow drawing via an SkCanvas, as is done below.
236 // Instead, use extractSubset. Note that this shares the SkPixelRef and
237 // SkColorTable.
238 // FIXME: Since src is discarded in practice, this holds on to more
239 // pixels than is strictly necessary. Switch to a copy if memory
240 // savings are more important than speed here. This also means
241 // that the pixels in dst can not be reused (though there is no
242 // allocation, which was already done on src).
243 int x = (dstX - srcX) / sampleSize;
244 int y = (dstY - srcY) / sampleSize;
245 SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
246 return src->extractSubset(dst, subset);
247 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000248 // if the destination has no pixels then we must allocate them.
249 if (dst->isNull()) {
reed6c225732014-06-09 19:52:07 -0700250 dst->setInfo(src->info().makeWH(w, h));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000251
252 if (!this->allocPixelRef(dst, NULL)) {
253 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000254 return false;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000255 }
256 }
257 // check to see if the destination is large enough to decode the desired
258 // region. If this assert fails we will just draw as much of the source
259 // into the destination that we can.
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000260 if (dst->width() < w || dst->height() < h) {
261 SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n"));
262 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000263
264 // Set the Src_Mode for the paint to prevent transparency issue in the
265 // dest in the event that the dest was being re-used.
266 SkPaint paint;
267 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
268
269 SkCanvas canvas(*dst);
270 canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
271 (srcY - dstY) / sampleSize,
272 &paint);
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000273 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000274}
275
reed@android.com8a1c16f2008-12-17 15:59:43 +0000276///////////////////////////////////////////////////////////////////////////////
277
reedbfefc7c2014-06-12 17:40:00 -0700278bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, SkColorType pref, Mode mode,
279 Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 SkASSERT(file);
281 SkASSERT(bm);
282
scroggo@google.comb5571b32013-09-25 21:34:24 +0000283 SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(file));
mike@reedtribe.orgf3811622013-03-19 02:18:33 +0000284 if (stream.get()) {
285 if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000286 bm->pixelRef()->setURI(file);
tomhudson@google.com1a366212012-01-03 14:42:08 +0000287 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000288 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000289 }
290 return false;
291}
292
reedbfefc7c2014-06-12 17:40:00 -0700293bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm, SkColorType pref,
294 Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000295 if (0 == size) {
296 return false;
297 }
298 SkASSERT(buffer);
299
300 SkMemoryStream stream(buffer, size);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000301 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000302}
303
reedbfefc7c2014-06-12 17:40:00 -0700304bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, SkColorType pref,
305 Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000306 SkASSERT(stream);
307 SkASSERT(bm);
308
309 bool success = false;
310 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
311
312 if (NULL != codec) {
313 success = codec->decode(stream, bm, pref, mode);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000314 if (success && format) {
315 *format = codec->getFormat();
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000316 if (kUnknown_Format == *format) {
317 if (stream->rewind()) {
318 *format = GetStreamFormat(stream);
319 }
320 }
reed@android.comb3ade9d2009-06-15 13:04:45 +0000321 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 delete codec;
323 }
324 return success;
325}