blob: 491d9aa72f9124b430148316d8ad37c7873a75ac [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
17static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
18
19SkBitmap::Config SkImageDecoder::GetDeviceConfig()
20{
21 return gDeviceConfig;
22}
23
24void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
25{
26 gDeviceConfig = config;
27}
28
29///////////////////////////////////////////////////////////////////////////////
30
31SkImageDecoder::SkImageDecoder()
scroggo@google.com7e6fcee2013-05-03 20:14:28 +000032 : fPeeker(NULL)
33 , fChooser(NULL)
34 , fAllocator(NULL)
35 , fSampleSize(1)
36 , fDefaultPref(SkBitmap::kNo_Config)
37 , fDitherImage(true)
38 , fUsePrefTable(false)
scroggo@google.com8d239242013-10-01 17:27:15 +000039 , fSkipWritingZeroes(false)
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000040 , fPreferQualityOverSpeed(false)
41 , fRequireUnpremultipliedColors(false) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000042}
43
44SkImageDecoder::~SkImageDecoder() {
reed@google.com82065d62011-02-07 15:30:46 +000045 SkSafeUnref(fPeeker);
46 SkSafeUnref(fChooser);
47 SkSafeUnref(fAllocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +000048}
49
scroggo@google.com468142b2013-07-09 15:48:24 +000050void SkImageDecoder::copyFieldsToOther(SkImageDecoder* other) {
51 if (NULL == other) {
52 return;
53 }
54 other->setPeeker(fPeeker);
55 other->setChooser(fChooser);
56 other->setAllocator(fAllocator);
57 other->setSampleSize(fSampleSize);
58 if (fUsePrefTable) {
59 other->setPrefConfigTable(fPrefTable);
60 } else {
61 other->fDefaultPref = fDefaultPref;
62 }
scroggo@google.com8d239242013-10-01 17:27:15 +000063 other->setDitherImage(fDitherImage);
64 other->setSkipWritingZeroes(fSkipWritingZeroes);
scroggo@google.com468142b2013-07-09 15:48:24 +000065 other->setPreferQualityOverSpeed(fPreferQualityOverSpeed);
66 other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors);
67}
68
reed@android.com8a1c16f2008-12-17 15:59:43 +000069SkImageDecoder::Format SkImageDecoder::getFormat() const {
70 return kUnknown_Format;
71}
72
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000073const char* SkImageDecoder::getFormatName() const {
scroggo@google.comf98118e2013-05-15 14:53:49 +000074 return GetFormatName(this->getFormat());
75}
76
77const char* SkImageDecoder::GetFormatName(Format format) {
78 switch (format) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +000079 case kUnknown_Format:
80 return "Unknown Format";
81 case kBMP_Format:
82 return "BMP";
83 case kGIF_Format:
84 return "GIF";
85 case kICO_Format:
86 return "ICO";
robertphillips@google.com8cf81e02014-05-22 18:40:29 +000087 case kPKM_Format:
88 return "PKM";
krajcevski99ffe242014-06-03 13:04:35 -070089 case kKTX_Format:
90 return "KTX";
scroggo@google.com39edf4c2013-04-25 17:33:51 +000091 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
scroggo@google.comf698c822013-07-18 19:34:49 +0000146void SkImageDecoder::setPrefConfigTable(const PrefConfigTable& prefTable) {
147 fUsePrefTable = true;
148 fPrefTable = prefTable;
149}
150
reed@android.com3f1f06a2010-03-03 21:04:12 +0000151SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
152 bool srcHasAlpha) const {
scroggo@google.com12d06422013-07-18 19:42:35 +0000153 SkBitmap::Config config = SkBitmap::kNo_Config;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000154
155 if (fUsePrefTable) {
reed@android.com3f1f06a2010-03-03 21:04:12 +0000156 switch (srcDepth) {
157 case kIndex_SrcDepth:
scroggo@google.comf698c822013-07-18 19:34:49 +0000158 config = srcHasAlpha ? fPrefTable.fPrefFor_8Index_YesAlpha_src
159 : fPrefTable.fPrefFor_8Index_NoAlpha_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000160 break;
scroggo@google.comf698c822013-07-18 19:34:49 +0000161 case k8BitGray_SrcDepth:
162 config = fPrefTable.fPrefFor_8Gray_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000163 break;
164 case k32Bit_SrcDepth:
scroggo@google.comf698c822013-07-18 19:34:49 +0000165 config = srcHasAlpha ? fPrefTable.fPrefFor_8bpc_YesAlpha_src
166 : fPrefTable.fPrefFor_8bpc_NoAlpha_src;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000167 break;
168 }
reed@android.com3f1f06a2010-03-03 21:04:12 +0000169 } else {
170 config = fDefaultPref;
171 }
172
173 if (SkBitmap::kNo_Config == config) {
174 config = SkImageDecoder::GetDeviceConfig();
175 }
176 return config;
177}
178
reed@android.com8a1c16f2008-12-17 15:59:43 +0000179bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
scroggo@google.combc69ce92013-07-09 15:45:14 +0000180 SkBitmap::Config pref, Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000181 // we reset this to false before calling onDecode
182 fShouldCancelDecode = false;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000183 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
184 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000186 // pass a temporary bitmap, so that if we return false, we are assured of
187 // leaving the caller's bitmap untouched.
188 SkBitmap tmp;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000189 if (!this->onDecode(stream, &tmp, mode)) {
reed@android.com62900b42009-02-11 15:07:19 +0000190 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000191 }
reed@android.com62900b42009-02-11 15:07:19 +0000192 bm->swap(tmp);
193 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194}
195
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000196bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000197 SkBitmap::Config pref) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000198 // we reset this to false before calling onDecodeSubset
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000199 fShouldCancelDecode = false;
200 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
201 fDefaultPref = pref;
202
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000203 return this->onDecodeSubset(bm, rect);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000204}
205
scroggo@google.comb5571b32013-09-25 21:34:24 +0000206bool SkImageDecoder::buildTileIndex(SkStreamRewindable* stream,
207 int *width, int *height) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000208 // we reset this to false before calling onBuildTileIndex
209 fShouldCancelDecode = false;
210
211 return this->onBuildTileIndex(stream, width, height);
212}
213
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000214bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
215 int dstX, int dstY, int width, int height,
216 int srcX, int srcY) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000217 int w = width / sampleSize;
218 int h = height / sampleSize;
reed@google.com44699382013-10-31 17:28:30 +0000219 if (src->config() == SkBitmap::kIndex8_Config) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000220 // kIndex8 does not allow drawing via an SkCanvas, as is done below.
221 // Instead, use extractSubset. Note that this shares the SkPixelRef and
222 // SkColorTable.
223 // FIXME: Since src is discarded in practice, this holds on to more
224 // pixels than is strictly necessary. Switch to a copy if memory
225 // savings are more important than speed here. This also means
226 // that the pixels in dst can not be reused (though there is no
227 // allocation, which was already done on src).
228 int x = (dstX - srcX) / sampleSize;
229 int y = (dstY - srcY) / sampleSize;
230 SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
231 return src->extractSubset(dst, subset);
232 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000233 // if the destination has no pixels then we must allocate them.
234 if (dst->isNull()) {
reed@google.com44699382013-10-31 17:28:30 +0000235 dst->setConfig(src->config(), w, h, 0, src->alphaType());
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000236
237 if (!this->allocPixelRef(dst, NULL)) {
238 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000239 return false;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000240 }
241 }
242 // check to see if the destination is large enough to decode the desired
243 // region. If this assert fails we will just draw as much of the source
244 // into the destination that we can.
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000245 if (dst->width() < w || dst->height() < h) {
246 SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n"));
247 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000248
249 // Set the Src_Mode for the paint to prevent transparency issue in the
250 // dest in the event that the dest was being re-used.
251 SkPaint paint;
252 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
253
254 SkCanvas canvas(*dst);
255 canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
256 (srcY - dstY) / sampleSize,
257 &paint);
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000258 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000259}
260
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261///////////////////////////////////////////////////////////////////////////////
262
263bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000264 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265 SkASSERT(file);
266 SkASSERT(bm);
267
scroggo@google.comb5571b32013-09-25 21:34:24 +0000268 SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(file));
mike@reedtribe.orgf3811622013-03-19 02:18:33 +0000269 if (stream.get()) {
270 if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000271 bm->pixelRef()->setURI(file);
tomhudson@google.com1a366212012-01-03 14:42:08 +0000272 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000273 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000274 }
275 return false;
276}
277
278bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000279 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000280 if (0 == size) {
281 return false;
282 }
283 SkASSERT(buffer);
284
285 SkMemoryStream stream(buffer, size);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000286 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000287}
288
scroggo@google.comb5571b32013-09-25 21:34:24 +0000289bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm,
290 SkBitmap::Config pref, Mode mode,
291 Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000292 SkASSERT(stream);
293 SkASSERT(bm);
294
295 bool success = false;
296 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
297
298 if (NULL != codec) {
299 success = codec->decode(stream, bm, pref, mode);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000300 if (success && format) {
301 *format = codec->getFormat();
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000302 if (kUnknown_Format == *format) {
303 if (stream->rewind()) {
304 *format = GetStreamFormat(stream);
305 }
306 }
reed@android.comb3ade9d2009-06-15 13:04:45 +0000307 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000308 delete codec;
309 }
310 return success;
311}