blob: 9ffae5f0e7f9bdac29d642641cc823ef2d806dcf [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)
43 , fPreferQualityOverSpeed(false) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000044}
45
46SkImageDecoder::~SkImageDecoder() {
reed@google.com82065d62011-02-07 15:30:46 +000047 SkSafeUnref(fPeeker);
48 SkSafeUnref(fChooser);
49 SkSafeUnref(fAllocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +000050}
51
52SkImageDecoder::Format SkImageDecoder::getFormat() const {
53 return kUnknown_Format;
54}
55
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000056const char* SkImageDecoder::getFormatName() const {
scroggo@google.com39edf4c2013-04-25 17:33:51 +000057 switch (this->getFormat()) {
58 case kUnknown_Format:
59 return "Unknown Format";
60 case kBMP_Format:
61 return "BMP";
62 case kGIF_Format:
63 return "GIF";
64 case kICO_Format:
65 return "ICO";
66 case kJPEG_Format:
67 return "JPEG";
68 case kPNG_Format:
69 return "PNG";
70 case kWBMP_Format:
71 return "WBMP";
72 case kWEBP_Format:
73 return "WEBP";
74 default:
75 SkASSERT(!"Invalid format type!");
76 }
77 return "Unknown Format";
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000078}
79
reed@android.com8a1c16f2008-12-17 15:59:43 +000080SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
81 SkRefCnt_SafeAssign(fPeeker, peeker);
82 return peeker;
83}
84
85SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
86 SkRefCnt_SafeAssign(fChooser, chooser);
87 return chooser;
88}
89
90SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
91 SkRefCnt_SafeAssign(fAllocator, alloc);
92 return alloc;
93}
94
95void SkImageDecoder::setSampleSize(int size) {
96 if (size < 1) {
97 size = 1;
98 }
99 fSampleSize = size;
100}
101
102bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
103 int height) const {
104 Chooser* chooser = fChooser;
105
106 if (NULL == chooser) { // no chooser, we just say YES to decoding :)
107 return true;
108 }
109 chooser->begin(1);
110 chooser->inspect(0, config, width, height);
111 return chooser->choose() == 0;
112}
113
114bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
115 SkColorTable* ctable) const {
116 return bitmap->allocPixels(fAllocator, ctable);
117}
118
119///////////////////////////////////////////////////////////////////////////////
reed@android.comb6137c32009-07-29 20:56:52 +0000120
reed@android.com3f1f06a2010-03-03 21:04:12 +0000121void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
122 if (NULL == pref) {
123 fUsePrefTable = false;
124 } else {
125 fUsePrefTable = true;
126 memcpy(fPrefTable, pref, sizeof(fPrefTable));
127 }
128}
129
130SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
131 bool srcHasAlpha) const {
132 SkBitmap::Config config;
133
134 if (fUsePrefTable) {
135 int index = 0;
136 switch (srcDepth) {
137 case kIndex_SrcDepth:
138 index = 0;
139 break;
140 case k16Bit_SrcDepth:
141 index = 2;
142 break;
143 case k32Bit_SrcDepth:
144 index = 4;
145 break;
146 }
147 if (srcHasAlpha) {
148 index += 1;
149 }
150 config = fPrefTable[index];
151 } else {
152 config = fDefaultPref;
153 }
154
155 if (SkBitmap::kNo_Config == config) {
156 config = SkImageDecoder::GetDeviceConfig();
157 }
158 return config;
159}
160
reed@android.com8a1c16f2008-12-17 15:59:43 +0000161bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000162 SkBitmap::Config pref, Mode mode, bool reuseBitmap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000163 // we reset this to false before calling onDecode
164 fShouldCancelDecode = false;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000165 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
166 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000168 if (reuseBitmap) {
169 SkAutoLockPixels alp(*bm);
170 if (NULL != bm->getPixels()) {
171 return this->onDecode(stream, bm, mode);
172 }
173 }
174
175 // pass a temporary bitmap, so that if we return false, we are assured of
176 // leaving the caller's bitmap untouched.
177 SkBitmap tmp;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000178 if (!this->onDecode(stream, &tmp, mode)) {
reed@android.com62900b42009-02-11 15:07:19 +0000179 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 }
reed@android.com62900b42009-02-11 15:07:19 +0000181 bm->swap(tmp);
182 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183}
184
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000185bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000186 SkBitmap::Config pref) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000187 // we reset this to false before calling onDecodeSubset
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000188 fShouldCancelDecode = false;
189 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
190 fDefaultPref = pref;
191
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000192 return this->onDecodeSubset(bm, rect);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000193}
194
195bool SkImageDecoder::buildTileIndex(SkStream* stream,
196 int *width, int *height) {
197 // we reset this to false before calling onBuildTileIndex
198 fShouldCancelDecode = false;
199
200 return this->onBuildTileIndex(stream, width, height);
201}
202
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000203bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
204 int dstX, int dstY, int width, int height,
205 int srcX, int srcY) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000206 int w = width / sampleSize;
207 int h = height / sampleSize;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000208 if (src->getConfig() == SkBitmap::kIndex8_Config) {
209 // kIndex8 does not allow drawing via an SkCanvas, as is done below.
210 // Instead, use extractSubset. Note that this shares the SkPixelRef and
211 // SkColorTable.
212 // FIXME: Since src is discarded in practice, this holds on to more
213 // pixels than is strictly necessary. Switch to a copy if memory
214 // savings are more important than speed here. This also means
215 // that the pixels in dst can not be reused (though there is no
216 // allocation, which was already done on src).
217 int x = (dstX - srcX) / sampleSize;
218 int y = (dstY - srcY) / sampleSize;
219 SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
220 return src->extractSubset(dst, subset);
221 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000222 // if the destination has no pixels then we must allocate them.
223 if (dst->isNull()) {
224 dst->setConfig(src->getConfig(), w, h);
225 dst->setIsOpaque(src->isOpaque());
226
227 if (!this->allocPixelRef(dst, NULL)) {
228 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000229 return false;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000230 }
231 }
232 // check to see if the destination is large enough to decode the desired
233 // region. If this assert fails we will just draw as much of the source
234 // into the destination that we can.
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000235 if (dst->width() < w || dst->height() < h) {
236 SkDEBUGF(("SkImageDecoder::cropBitmap does not have a large enough bitmap.\n"));
237 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000238
239 // Set the Src_Mode for the paint to prevent transparency issue in the
240 // dest in the event that the dest was being re-used.
241 SkPaint paint;
242 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
243
244 SkCanvas canvas(*dst);
245 canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
246 (srcY - dstY) / sampleSize,
247 &paint);
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000248 return true;
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000249}
250
reed@android.com8a1c16f2008-12-17 15:59:43 +0000251///////////////////////////////////////////////////////////////////////////////
252
253bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000254 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 SkASSERT(file);
256 SkASSERT(bm);
257
mike@reedtribe.orgf3811622013-03-19 02:18:33 +0000258 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(file));
259 if (stream.get()) {
260 if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000261 bm->pixelRef()->setURI(file);
tomhudson@google.com1a366212012-01-03 14:42:08 +0000262 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000263 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000264 }
265 return false;
266}
267
268bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000269 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000270 if (0 == size) {
271 return false;
272 }
273 SkASSERT(buffer);
274
275 SkMemoryStream stream(buffer, size);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000276 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000277}
278
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000279class TargetAllocator : public SkBitmap::Allocator {
280
281public:
282 TargetAllocator(void* target)
283 : fTarget(target) {}
284
285 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
286 // SkColorTable is not supported by Info/Target model.
287 SkASSERT(NULL == ct);
288 bm->setPixels(fTarget);
289 return true;
290 }
291
292private:
293 void* fTarget;
294};
295
296bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
297 SkImage::Info* info,
298 const SkBitmapFactory::Target* target) {
299 if (NULL == info) {
300 return false;
301 }
302 // FIXME: Just to get this working, implement in terms of existing
303 // ImageDecoder calls.
304 SkBitmap bm;
305 SkMemoryStream stream(buffer, size);
306 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
307 if (decoder.get() != NULL && decoder->decode(&stream, &bm, kDecodeBounds_Mode)) {
308 // Now set info properly
309 if (!SkBitmapToImageInfo(bm, info)) {
310 return false;
311 }
312
313 // SkBitmapToImageInfo will return false if Index8 is used. kIndex8
314 // is not supported by the Info/Target model, since kIndex8 requires
315 // an SkColorTable, which this model does not keep track of.
316 SkASSERT(bm.config() != SkBitmap::kIndex8_Config);
317
318 if (NULL == target) {
319 return true;
320 }
321
322 if (target->fRowBytes != (uint32_t) bm.rowBytes()) {
323 if (target->fRowBytes < SkImageMinRowBytes(*info)) {
324 SkASSERT(!"Desired row bytes is too small");
325 return false;
326 }
327 bm.setConfig(bm.config(), bm.width(), bm.height(), target->fRowBytes);
328 }
329
330 TargetAllocator allocator(target->fAddr);
331 decoder->setAllocator(&allocator);
332 stream.rewind();
333 bool success = decoder->decode(&stream, &bm, kDecodePixels_Mode);
334 // Remove the allocator, since it's on the stack.
335 decoder->setAllocator(NULL);
336 return success;
337 }
338 return false;
339}
340
341
reed@android.com8a1c16f2008-12-17 15:59:43 +0000342bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000343 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000344 SkASSERT(stream);
345 SkASSERT(bm);
346
347 bool success = false;
348 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
349
350 if (NULL != codec) {
351 success = codec->decode(stream, bm, pref, mode);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000352 if (success && format) {
353 *format = codec->getFormat();
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000354 if (kUnknown_Format == *format) {
355 if (stream->rewind()) {
356 *format = GetStreamFormat(stream);
357 }
358 }
reed@android.comb3ade9d2009-06-15 13:04:45 +0000359 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000360 delete codec;
361 }
362 return success;
363}
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000364
scroggo@google.com82449982013-05-01 21:40:58 +0000365/**
366 * This function leaks, but that is okay because it is not intended
367 * to be called. It is only here so that the linker will include the
368 * decoders.
369 * Make sure to keep it in sync with images.gyp, so only the encoders
370 * which are created on a platform are linked.
371 */
372void force_linking();
373void force_linking() {
374 SkASSERT(false);
375 CreateJPEGImageDecoder();
376 CreateWEBPImageDecoder();
377 CreateBMPImageDecoder();
378 CreateICOImageDecoder();
379 CreateWBMPImageDecoder();
380 // Only link GIF and PNG on platforms that build them. See images.gyp
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000381#if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN) && !defined(SK_BUILD_FOR_NACL)
scroggo@google.com82449982013-05-01 21:40:58 +0000382 CreateGIFImageDecoder();
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000383#endif
384#if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN)
scroggo@google.com82449982013-05-01 21:40:58 +0000385 CreatePNGImageDecoder();
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000386#endif
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000387}