blob: c5e973903d61a3fe25e6b4a7aaa99f1043b04335 [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)
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);
reedbe08ace2014-07-08 11:15:59 -070050 other->setPreserveSrcDepth(fPreserveSrcDepth);
scroggo@google.com8d239242013-10-01 17:27:15 +000051 other->setDitherImage(fDitherImage);
52 other->setSkipWritingZeroes(fSkipWritingZeroes);
scroggo@google.com468142b2013-07-09 15:48:24 +000053 other->setPreferQualityOverSpeed(fPreferQualityOverSpeed);
54 other->setRequireUnpremultipliedColors(fRequireUnpremultipliedColors);
55}
56
reed@android.com8a1c16f2008-12-17 15:59:43 +000057SkImageDecoder::Format SkImageDecoder::getFormat() const {
58 return kUnknown_Format;
59}
60
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000061const char* SkImageDecoder::getFormatName() const {
scroggo@google.comf98118e2013-05-15 14:53:49 +000062 return GetFormatName(this->getFormat());
63}
64
65const char* SkImageDecoder::GetFormatName(Format format) {
66 switch (format) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +000067 case kUnknown_Format:
68 return "Unknown Format";
69 case kBMP_Format:
70 return "BMP";
71 case kGIF_Format:
72 return "GIF";
73 case kICO_Format:
74 return "ICO";
robertphillips@google.com8cf81e02014-05-22 18:40:29 +000075 case kPKM_Format:
76 return "PKM";
krajcevski99ffe242014-06-03 13:04:35 -070077 case kKTX_Format:
78 return "KTX";
krajcevski95b1b3d2014-08-07 12:58:38 -070079 case kASTC_Format:
80 return "ASTC";
scroggo@google.com39edf4c2013-04-25 17:33:51 +000081 case kJPEG_Format:
82 return "JPEG";
83 case kPNG_Format:
84 return "PNG";
85 case kWBMP_Format:
86 return "WBMP";
87 case kWEBP_Format:
88 return "WEBP";
89 default:
mtklein@google.com330313a2013-08-22 15:37:26 +000090 SkDEBUGFAIL("Invalid format type!");
scroggo@google.com39edf4c2013-04-25 17:33:51 +000091 }
92 return "Unknown Format";
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000093}
94
reed@android.com8a1c16f2008-12-17 15:59:43 +000095SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
96 SkRefCnt_SafeAssign(fPeeker, peeker);
97 return peeker;
98}
99
reed5926b862014-06-11 10:33:13 -0700100#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed@android.com8a1c16f2008-12-17 15:59:43 +0000101SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
102 SkRefCnt_SafeAssign(fChooser, chooser);
103 return chooser;
104}
reed5926b862014-06-11 10:33:13 -0700105#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106
107SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
108 SkRefCnt_SafeAssign(fAllocator, alloc);
109 return alloc;
110}
111
112void SkImageDecoder::setSampleSize(int size) {
113 if (size < 1) {
114 size = 1;
115 }
116 fSampleSize = size;
117}
118
reed5926b862014-06-11 10:33:13 -0700119#ifdef SK_SUPPORT_LEGACY_IMAGEDECODER_CHOOSER
reed6c225732014-06-09 19:52:07 -0700120// TODO: change Chooser virtual to take colorType, so we can stop calling SkColorTypeToBitmapConfig
121//
122bool SkImageDecoder::chooseFromOneChoice(SkColorType colorType, int width, int height) const {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000123 Chooser* chooser = fChooser;
reed6c225732014-06-09 19:52:07 -0700124
reed@android.com8a1c16f2008-12-17 15:59:43 +0000125 if (NULL == chooser) { // no chooser, we just say YES to decoding :)
126 return true;
127 }
128 chooser->begin(1);
reed6c225732014-06-09 19:52:07 -0700129 chooser->inspect(0, SkColorTypeToBitmapConfig(colorType), width, height);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000130 return chooser->choose() == 0;
131}
reed5926b862014-06-11 10:33:13 -0700132#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000133
134bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
135 SkColorTable* ctable) const {
reed84825042014-09-02 12:50:45 -0700136 return bitmap->tryAllocPixels(fAllocator, ctable);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000137}
138
139///////////////////////////////////////////////////////////////////////////////
reed@android.comb6137c32009-07-29 20:56:52 +0000140
reed6c225732014-06-09 19:52:07 -0700141SkColorType SkImageDecoder::getPrefColorType(SrcDepth srcDepth, bool srcHasAlpha) const {
reedbfefc7c2014-06-12 17:40:00 -0700142 SkColorType ct = fDefaultPref;
reedbe08ace2014-07-08 11:15:59 -0700143 if (fPreserveSrcDepth) {
144 switch (srcDepth) {
145 case kIndex_SrcDepth:
146 ct = kIndex_8_SkColorType;
147 break;
148 case k8BitGray_SrcDepth:
149 ct = kN32_SkColorType;
150 break;
151 case k32Bit_SrcDepth:
152 ct = kN32_SkColorType;
153 break;
154 }
155 }
reedbfefc7c2014-06-12 17:40:00 -0700156 return ct;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000157}
158
scroggo2a120802014-10-22 12:07:00 -0700159SkImageDecoder::Result SkImageDecoder::decode(SkStream* stream, SkBitmap* bm, SkColorType pref,
160 Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161 // we reset this to false before calling onDecode
162 fShouldCancelDecode = false;
reedbfefc7c2014-06-12 17:40:00 -0700163 // assign this, for use by getPrefColorType(), in case fUsePrefTable is false
reed@android.com3f1f06a2010-03-03 21:04:12 +0000164 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000166 // pass a temporary bitmap, so that if we return false, we are assured of
167 // leaving the caller's bitmap untouched.
scroggo2a120802014-10-22 12:07:00 -0700168 SkBitmap tmp;
169 const Result result = this->onDecode(stream, &tmp, mode);
170 if (kFailure != result) {
171 bm->swap(tmp);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172 }
scroggo2a120802014-10-22 12:07:00 -0700173 return result;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000174}
175
reedbfefc7c2014-06-12 17:40:00 -0700176bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect, SkColorType pref) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000177 // we reset this to false before calling onDecodeSubset
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000178 fShouldCancelDecode = false;
reedbfefc7c2014-06-12 17:40:00 -0700179 // assign this, for use by getPrefColorType(), in case fUsePrefTable is false
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000180 fDefaultPref = pref;
181
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000182 return this->onDecodeSubset(bm, rect);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000183}
184
reedbfefc7c2014-06-12 17:40:00 -0700185bool SkImageDecoder::buildTileIndex(SkStreamRewindable* stream, int *width, int *height) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000186 // we reset this to false before calling onBuildTileIndex
187 fShouldCancelDecode = false;
188
189 return this->onBuildTileIndex(stream, width, height);
190}
191
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000192bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
193 int dstX, int dstY, int width, int height,
194 int srcX, int srcY) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000195 int w = width / sampleSize;
196 int h = height / sampleSize;
reed0689d7b2014-06-14 05:30:20 -0700197 if (src->colorType() == kIndex_8_SkColorType) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000198 // kIndex8 does not allow drawing via an SkCanvas, as is done below.
199 // Instead, use extractSubset. Note that this shares the SkPixelRef and
200 // SkColorTable.
201 // FIXME: Since src is discarded in practice, this holds on to more
202 // pixels than is strictly necessary. Switch to a copy if memory
203 // savings are more important than speed here. This also means
204 // that the pixels in dst can not be reused (though there is no
205 // allocation, which was already done on src).
206 int x = (dstX - srcX) / sampleSize;
207 int y = (dstY - srcY) / sampleSize;
208 SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
209 return src->extractSubset(dst, subset);
210 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000211 // if the destination has no pixels then we must allocate them.
212 if (dst->isNull()) {
reed6c225732014-06-09 19:52:07 -0700213 dst->setInfo(src->info().makeWH(w, h));
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000214
215 if (!this->allocPixelRef(dst, NULL)) {
216 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000217 return false;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000218 }
219 }
220 // check to see if the destination is large enough to decode the desired
221 // region. If this assert fails we will just draw as much of the source
222 // into the destination that we can.
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000223 if (dst->width() < w || dst->height() < h) {
224 SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n"));
225 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000226
227 // Set the Src_Mode for the paint to prevent transparency issue in the
228 // dest in the event that the dest was being re-used.
229 SkPaint paint;
230 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
231
232 SkCanvas canvas(*dst);
233 canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
234 (srcY - dstY) / sampleSize,
235 &paint);
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000236 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000237}
238
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239///////////////////////////////////////////////////////////////////////////////
240
reedbfefc7c2014-06-12 17:40:00 -0700241bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm, SkColorType pref, Mode mode,
242 Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000243 SkASSERT(file);
244 SkASSERT(bm);
245
scroggo@google.comb5571b32013-09-25 21:34:24 +0000246 SkAutoTUnref<SkStreamRewindable> stream(SkStream::NewFromFile(file));
mike@reedtribe.orgf3811622013-03-19 02:18:33 +0000247 if (stream.get()) {
248 if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000249 bm->pixelRef()->setURI(file);
tomhudson@google.com1a366212012-01-03 14:42:08 +0000250 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000252 }
253 return false;
254}
255
reedbfefc7c2014-06-12 17:40:00 -0700256bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm, SkColorType pref,
257 Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000258 if (0 == size) {
259 return false;
260 }
261 SkASSERT(buffer);
262
263 SkMemoryStream stream(buffer, size);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000264 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000265}
266
reedbfefc7c2014-06-12 17:40:00 -0700267bool SkImageDecoder::DecodeStream(SkStreamRewindable* stream, SkBitmap* bm, SkColorType pref,
268 Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000269 SkASSERT(stream);
270 SkASSERT(bm);
271
272 bool success = false;
273 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
274
bsalomon49f085d2014-09-05 13:34:00 -0700275 if (codec) {
scroggo2a120802014-10-22 12:07:00 -0700276 success = codec->decode(stream, bm, pref, mode) != kFailure;
reed@android.comb3ade9d2009-06-15 13:04:45 +0000277 if (success && format) {
278 *format = codec->getFormat();
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000279 if (kUnknown_Format == *format) {
280 if (stream->rewind()) {
281 *format = GetStreamFormat(stream);
282 }
283 }
reed@android.comb3ade9d2009-06-15 13:04:45 +0000284 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000285 delete codec;
286 }
287 return success;
288}
sugoib227e372014-10-16 13:10:57 -0700289
290bool SkImageDecoder::decodeYUV8Planes(SkStream* stream, SkISize componentSizes[3], void* planes[3],
291 size_t rowBytes[3], SkYUVColorSpace* colorSpace) {
292 // we reset this to false before calling onDecodeYUV8Planes
293 fShouldCancelDecode = false;
294
295 return this->onDecodeYUV8Planes(stream, componentSizes, planes, rowBytes, colorSpace);
296}