blob: 223ae152646ed09fe933c9754641067c6e907d97 [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)
24 , fDefaultPref(SkBitmap::kNo_Config)
25 , fDitherImage(true)
26 , fUsePrefTable(false)
scroggo@google.com8d239242013-10-01 17:27:15 +000027 , fSkipWritingZeroes(false)
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000028 , fPreferQualityOverSpeed(false)
29 , fRequireUnpremultipliedColors(false) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000030}
31
32SkImageDecoder::~SkImageDecoder() {
reed@google.com82065d62011-02-07 15:30:46 +000033 SkSafeUnref(fPeeker);
reed5926b862014-06-11 10:33:13 -070034#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed@google.com82065d62011-02-07 15:30:46 +000035 SkSafeUnref(fChooser);
reed5926b862014-06-11 10:33:13 -070036#endif
reed@google.com82065d62011-02-07 15:30:46 +000037 SkSafeUnref(fAllocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +000038}
39
scroggo@google.com468142b2013-07-09 15:48:24 +000040void SkImageDecoder::copyFieldsToOther(SkImageDecoder* other) {
41 if (NULL == other) {
42 return;
43 }
44 other->setPeeker(fPeeker);
reed5926b862014-06-11 10:33:13 -070045#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
scroggo@google.com468142b2013-07-09 15:48:24 +000046 other->setChooser(fChooser);
reed5926b862014-06-11 10:33:13 -070047#endif
scroggo@google.com468142b2013-07-09 15:48:24 +000048 other->setAllocator(fAllocator);
49 other->setSampleSize(fSampleSize);
50 if (fUsePrefTable) {
51 other->setPrefConfigTable(fPrefTable);
52 } else {
53 other->fDefaultPref = fDefaultPref;
54 }
scroggo@google.com8d239242013-10-01 17:27:15 +000055 other->setDitherImage(fDitherImage);
56 other->setSkipWritingZeroes(fSkipWritingZeroes);
scroggo@google.com468142b2013-07-09 15:48:24 +000057 other->setPreferQualityOverSpeed(fPreferQualityOverSpeed);
58 other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors);
59}
60
reed@android.com8a1c16f2008-12-17 15:59:43 +000061SkImageDecoder::Format SkImageDecoder::getFormat() const {
62 return kUnknown_Format;
63}
64
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000065const char* SkImageDecoder::getFormatName() const {
scroggo@google.comf98118e2013-05-15 14:53:49 +000066 return GetFormatName(this->getFormat());
67}
68
69const char* SkImageDecoder::GetFormatName(Format format) {
70 switch (format) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +000071 case kUnknown_Format:
72 return "Unknown Format";
73 case kBMP_Format:
74 return "BMP";
75 case kGIF_Format:
76 return "GIF";
77 case kICO_Format:
78 return "ICO";
robertphillips@google.com8cf81e02014-05-22 18:40:29 +000079 case kPKM_Format:
80 return "PKM";
krajcevski99ffe242014-06-03 13:04:35 -070081 case kKTX_Format:
82 return "KTX";
scroggo@google.com39edf4c2013-04-25 17:33:51 +000083 case kJPEG_Format:
84 return "JPEG";
85 case kPNG_Format:
86 return "PNG";
87 case kWBMP_Format:
88 return "WBMP";
89 case kWEBP_Format:
90 return "WEBP";
91 default:
mtklein@google.com330313a2013-08-22 15:37:26 +000092 SkDEBUGFAIL("Invalid format type!");
scroggo@google.com39edf4c2013-04-25 17:33:51 +000093 }
94 return "Unknown Format";
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000095}
96
reed@android.com8a1c16f2008-12-17 15:59:43 +000097SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
98 SkRefCnt_SafeAssign(fPeeker, peeker);
99 return peeker;
100}
101
reed5926b862014-06-11 10:33:13 -0700102#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000103SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
104 SkRefCnt_SafeAssign(fChooser, chooser);
105 return chooser;
106}
reed5926b862014-06-11 10:33:13 -0700107#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108
109SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
110 SkRefCnt_SafeAssign(fAllocator, alloc);
111 return alloc;
112}
113
114void SkImageDecoder::setSampleSize(int size) {
115 if (size < 1) {
116 size = 1;
117 }
118 fSampleSize = size;
119}
120
reed5926b862014-06-11 10:33:13 -0700121#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed6c225732014-06-09 19:52:07 -0700122// TODO: change Chooser virtual to take colorType, so we can stop calling SkColorTypeToBitmapConfig
123//
124bool SkImageDecoder::chooseFromOneChoice(SkColorType colorType, int width, int height) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 Chooser* chooser = fChooser;
reed6c225732014-06-09 19:52:07 -0700126
reed@android.com8a1c16f2008-12-17 15:59:43 +0000127 if (NULL == chooser) { // no chooser, we just say YES to decoding :)
128 return true;
129 }
130 chooser->begin(1);
reed6c225732014-06-09 19:52:07 -0700131 chooser->inspect(0, SkColorTypeToBitmapConfig(colorType), width, height);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000132 return chooser->choose() == 0;
133}
reed5926b862014-06-11 10:33:13 -0700134#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000135
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
scroggo@google.comf698c822013-07-18 19:34:49 +0000143void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) {
144 fUsePrefTable = true;
145 fPrefTable = prefTable;
146}
147
reed2f785a22014-06-12 09:21:31 -0700148// TODO: use colortype in fPrefTable, fDefaultPref so we can stop using SkBitmapConfigToColorType()
reed6c225732014-06-09 19:52:07 -0700149//
150SkColorType SkImageDecoder::getPrefColorType(SrcDepth srcDepth, bool srcHasAlpha) const {
scroggo@google.com12d06422013-07-18 19:42:35 +0000151 SkBitmap::Config config = SkBitmap::kNo_Config;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000152
153 if (fUsePrefTable) {
reed@android.com3f1f06a2010-03-03 21:04:12 +0000154 switch (srcDepth) {
155 case kIndex_SrcDepth:
scroggo@google.comf698c822013-07-18 19:34:49 +0000156 config = srcHasAlpha ? fPrefTable.fPrefFor_8Index_YesAlpha_src
157 : fPrefTable.fPrefFor_8Index_NoAlpha_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000158 break;
scroggo@google.comf698c822013-07-18 19:34:49 +0000159 case k8BitGray_SrcDepth:
160 config = fPrefTable.fPrefFor_8Gray_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000161 break;
162 case k32Bit_SrcDepth:
scroggo@google.comf698c822013-07-18 19:34:49 +0000163 config = srcHasAlpha ? fPrefTable.fPrefFor_8bpc_YesAlpha_src
164 : fPrefTable.fPrefFor_8bpc_NoAlpha_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000165 break;
166 }
reed@android.com3f1f06a2010-03-03 21:04:12 +0000167 } else {
168 config = fDefaultPref;
169 }
170
reed6c225732014-06-09 19:52:07 -0700171 return SkBitmapConfigToColorType(config);
reed@android.com3f1f06a2010-03-03 21:04:12 +0000172}
173
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
scroggo@google.combc69ce92013-07-09 15:45:14 +0000175 SkBitmap::Config pref, Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000176 // we reset this to false before calling onDecode
177 fShouldCancelDecode = false;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000178 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
179 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000181 // pass a temporary bitmap, so that if we return false, we are assured of
182 // leaving the caller's bitmap untouched.
183 SkBitmap tmp;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000184 if (!this->onDecode(stream, &tmp, mode)) {
reed@android.com62900b42009-02-11 15:07:19 +0000185 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000186 }
reed@android.com62900b42009-02-11 15:07:19 +0000187 bm->swap(tmp);
188 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000189}
190
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000191bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000192 SkBitmap::Config pref) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000193 // we reset this to false before calling onDecodeSubset
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000194 fShouldCancelDecode = false;
195 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
196 fDefaultPref = pref;
197
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000198 return this->onDecodeSubset(bm, rect);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000199}
200
scroggo@google.comb5571b32013-09-25 21:34:24 +0000201bool SkImageDecoder::buildTileIndex(SkStreamRewindable* stream,
202 int *width, int *height) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000203 // we reset this to false before calling onBuildTileIndex
204 fShouldCancelDecode = false;
205
206 return this->onBuildTileIndex(stream, width, height);
207}
208
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000209bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
210 int dstX, int dstY, int width, int height,
211 int srcX, int srcY) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000212 int w = width / sampleSize;
213 int h = height / sampleSize;
reed@google.com44699382013-10-31 17:28:30 +0000214 if (src->config() == SkBitmap::kIndex8_Config) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000215 // kIndex8 does not allow drawing via an SkCanvas, as is done below.
216 // Instead, use extractSubset. Note that this shares the SkPixelRef and
217 // SkColorTable.
218 // FIXME: Since src is discarded in practice, this holds on to more
219 // pixels than is strictly necessary. Switch to a copy if memory
220 // savings are more important than speed here. This also means
221 // that the pixels in dst can not be reused (though there is no
222 // allocation, which was already done on src).
223 int x = (dstX - srcX) / sampleSize;
224 int y = (dstY - srcY) / sampleSize;
225 SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
226 return src->extractSubset(dst, subset);
227 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000228 // if the destination has no pixels then we must allocate them.
229 if (dst->isNull()) {
reed6c225732014-06-09 19:52:07 -0700230 dst->setInfo(src->info().makeWH(w, h));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000231
232 if (!this->allocPixelRef(dst, NULL)) {
233 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000234 return false;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000235 }
236 }
237 // check to see if the destination is large enough to decode the desired
238 // region. If this assert fails we will just draw as much of the source
239 // into the destination that we can.
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000240 if (dst->width() < w || dst->height() < h) {
241 SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n"));
242 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000243
244 // Set the Src_Mode for the paint to prevent transparency issue in the
245 // dest in the event that the dest was being re-used.
246 SkPaint paint;
247 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
248
249 SkCanvas canvas(*dst);
250 canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
251 (srcY - dstY) / sampleSize,
252 &paint);
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000253 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000254}
255
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256///////////////////////////////////////////////////////////////////////////////
257
258bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000259 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000260 SkASSERT(file);
261 SkASSERT(bm);
262
scroggo@google.comb5571b32013-09-25 21:34:24 +0000263 SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(file));
mike@reedtribe.orgf3811622013-03-19 02:18:33 +0000264 if (stream.get()) {
265 if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000266 bm->pixelRef()->setURI(file);
tomhudson@google.com1a366212012-01-03 14:42:08 +0000267 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000268 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 }
270 return false;
271}
272
273bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000274 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000275 if (0 == size) {
276 return false;
277 }
278 SkASSERT(buffer);
279
280 SkMemoryStream stream(buffer, size);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000281 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000282}
283
scroggo@google.comb5571b32013-09-25 21:34:24 +0000284bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm,
285 SkBitmap::Config pref, Mode mode,
286 Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287 SkASSERT(stream);
288 SkASSERT(bm);
289
290 bool success = false;
291 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
292
293 if (NULL != codec) {
294 success = codec->decode(stream, bm, pref, mode);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000295 if (success && format) {
296 *format = codec->getFormat();
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000297 if (kUnknown_Format == *format) {
298 if (stream->rewind()) {
299 *format = GetStreamFormat(stream);
300 }
301 }
reed@android.comb3ade9d2009-06-15 13:04:45 +0000302 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000303 delete codec;
304 }
305 return success;
306}