blob: de0bc14c90e40cd54b49a1985fe535a5c86b0a7b [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()
36 : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1),
reed@android.com3f1f06a2010-03-03 21:04:12 +000037 fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true),
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000038 fUsePrefTable(false),fPreferQualityOverSpeed(false) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000039}
40
41SkImageDecoder::~SkImageDecoder() {
reed@google.com82065d62011-02-07 15:30:46 +000042 SkSafeUnref(fPeeker);
43 SkSafeUnref(fChooser);
44 SkSafeUnref(fAllocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +000045}
46
47SkImageDecoder::Format SkImageDecoder::getFormat() const {
48 return kUnknown_Format;
49}
50
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000051const char* SkImageDecoder::getFormatName() const {
scroggo@google.com39edf4c2013-04-25 17:33:51 +000052 switch (this->getFormat()) {
53 case kUnknown_Format:
54 return "Unknown Format";
55 case kBMP_Format:
56 return "BMP";
57 case kGIF_Format:
58 return "GIF";
59 case kICO_Format:
60 return "ICO";
61 case kJPEG_Format:
62 return "JPEG";
63 case kPNG_Format:
64 return "PNG";
65 case kWBMP_Format:
66 return "WBMP";
67 case kWEBP_Format:
68 return "WEBP";
69 default:
70 SkASSERT(!"Invalid format type!");
71 }
72 return "Unknown Format";
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000073}
74
reed@android.com8a1c16f2008-12-17 15:59:43 +000075SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
76 SkRefCnt_SafeAssign(fPeeker, peeker);
77 return peeker;
78}
79
80SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
81 SkRefCnt_SafeAssign(fChooser, chooser);
82 return chooser;
83}
84
85SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
86 SkRefCnt_SafeAssign(fAllocator, alloc);
87 return alloc;
88}
89
90void SkImageDecoder::setSampleSize(int size) {
91 if (size < 1) {
92 size = 1;
93 }
94 fSampleSize = size;
95}
96
97bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
98 int height) const {
99 Chooser* chooser = fChooser;
100
101 if (NULL == chooser) { // no chooser, we just say YES to decoding :)
102 return true;
103 }
104 chooser->begin(1);
105 chooser->inspect(0, config, width, height);
106 return chooser->choose() == 0;
107}
108
109bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
110 SkColorTable* ctable) const {
111 return bitmap->allocPixels(fAllocator, ctable);
112}
113
114///////////////////////////////////////////////////////////////////////////////
reed@android.comb6137c32009-07-29 20:56:52 +0000115
reed@android.com3f1f06a2010-03-03 21:04:12 +0000116void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
117 if (NULL == pref) {
118 fUsePrefTable = false;
119 } else {
120 fUsePrefTable = true;
121 memcpy(fPrefTable, pref, sizeof(fPrefTable));
122 }
123}
124
125SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
126 bool srcHasAlpha) const {
127 SkBitmap::Config config;
128
129 if (fUsePrefTable) {
130 int index = 0;
131 switch (srcDepth) {
132 case kIndex_SrcDepth:
133 index = 0;
134 break;
135 case k16Bit_SrcDepth:
136 index = 2;
137 break;
138 case k32Bit_SrcDepth:
139 index = 4;
140 break;
141 }
142 if (srcHasAlpha) {
143 index += 1;
144 }
145 config = fPrefTable[index];
146 } else {
147 config = fDefaultPref;
148 }
149
150 if (SkBitmap::kNo_Config == config) {
151 config = SkImageDecoder::GetDeviceConfig();
152 }
153 return config;
154}
155
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000157 SkBitmap::Config pref, Mode mode, bool reuseBitmap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000158 // we reset this to false before calling onDecode
159 fShouldCancelDecode = false;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000160 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
161 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000162
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000163 if (reuseBitmap) {
164 SkAutoLockPixels alp(*bm);
165 if (NULL != bm->getPixels()) {
166 return this->onDecode(stream, bm, mode);
167 }
168 }
169
170 // pass a temporary bitmap, so that if we return false, we are assured of
171 // leaving the caller's bitmap untouched.
172 SkBitmap tmp;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000173 if (!this->onDecode(stream, &tmp, mode)) {
reed@android.com62900b42009-02-11 15:07:19 +0000174 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000175 }
reed@android.com62900b42009-02-11 15:07:19 +0000176 bm->swap(tmp);
177 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000178}
179
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000180bool SkImageDecoder::decodeRegion(SkBitmap* bm, const SkIRect& rect,
181 SkBitmap::Config pref) {
182 // we reset this to false before calling onDecodeRegion
183 fShouldCancelDecode = false;
184 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
185 fDefaultPref = pref;
186
187 return this->onDecodeRegion(bm, rect);
188}
189
190bool SkImageDecoder::buildTileIndex(SkStream* stream,
191 int *width, int *height) {
192 // we reset this to false before calling onBuildTileIndex
193 fShouldCancelDecode = false;
194
195 return this->onBuildTileIndex(stream, width, height);
196}
197
198void SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
skia.committer@gmail.comc49cabf2013-03-15 07:05:19 +0000199 int dstX, int dstY, int width, int height,
200 int srcX, int srcY) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000201 int w = width / sampleSize;
202 int h = height / sampleSize;
203 // if the destination has no pixels then we must allocate them.
204 if (dst->isNull()) {
205 dst->setConfig(src->getConfig(), w, h);
206 dst->setIsOpaque(src->isOpaque());
207
208 if (!this->allocPixelRef(dst, NULL)) {
209 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
210 return;
211 }
212 }
213 // check to see if the destination is large enough to decode the desired
214 // region. If this assert fails we will just draw as much of the source
215 // into the destination that we can.
216 SkASSERT(dst->width() >= w && dst->height() >= h);
217
218 // Set the Src_Mode for the paint to prevent transparency issue in the
219 // dest in the event that the dest was being re-used.
220 SkPaint paint;
221 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
222
223 SkCanvas canvas(*dst);
224 canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
225 (srcY - dstY) / sampleSize,
226 &paint);
227}
228
reed@android.com8a1c16f2008-12-17 15:59:43 +0000229///////////////////////////////////////////////////////////////////////////////
230
231bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000232 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 SkASSERT(file);
234 SkASSERT(bm);
235
mike@reedtribe.orgf3811622013-03-19 02:18:33 +0000236 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(file));
237 if (stream.get()) {
238 if (SkImageDecoder::DecodeStream(stream, bm, pref, mode, format)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239 bm->pixelRef()->setURI(file);
tomhudson@google.com1a366212012-01-03 14:42:08 +0000240 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000241 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000242 }
243 return false;
244}
245
246bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000247 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000248 if (0 == size) {
249 return false;
250 }
251 SkASSERT(buffer);
252
253 SkMemoryStream stream(buffer, size);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000254 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255}
256
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000257class TargetAllocator : public SkBitmap::Allocator {
258
259public:
260 TargetAllocator(void* target)
261 : fTarget(target) {}
262
263 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
264 // SkColorTable is not supported by Info/Target model.
265 SkASSERT(NULL == ct);
266 bm->setPixels(fTarget);
267 return true;
268 }
269
270private:
271 void* fTarget;
272};
273
274bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
275 SkImage::Info* info,
276 const SkBitmapFactory::Target* target) {
277 if (NULL == info) {
278 return false;
279 }
280 // FIXME: Just to get this working, implement in terms of existing
281 // ImageDecoder calls.
282 SkBitmap bm;
283 SkMemoryStream stream(buffer, size);
284 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
285 if (decoder.get() != NULL && decoder->decode(&stream, &bm, kDecodeBounds_Mode)) {
286 // Now set info properly
287 if (!SkBitmapToImageInfo(bm, info)) {
288 return false;
289 }
290
291 // SkBitmapToImageInfo will return false if Index8 is used. kIndex8
292 // is not supported by the Info/Target model, since kIndex8 requires
293 // an SkColorTable, which this model does not keep track of.
294 SkASSERT(bm.config() != SkBitmap::kIndex8_Config);
295
296 if (NULL == target) {
297 return true;
298 }
299
300 if (target->fRowBytes != (uint32_t) bm.rowBytes()) {
301 if (target->fRowBytes < SkImageMinRowBytes(*info)) {
302 SkASSERT(!"Desired row bytes is too small");
303 return false;
304 }
305 bm.setConfig(bm.config(), bm.width(), bm.height(), target->fRowBytes);
306 }
307
308 TargetAllocator allocator(target->fAddr);
309 decoder->setAllocator(&allocator);
310 stream.rewind();
311 bool success = decoder->decode(&stream, &bm, kDecodePixels_Mode);
312 // Remove the allocator, since it's on the stack.
313 decoder->setAllocator(NULL);
314 return success;
315 }
316 return false;
317}
318
319
reed@android.com8a1c16f2008-12-17 15:59:43 +0000320bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000321 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000322 SkASSERT(stream);
323 SkASSERT(bm);
324
325 bool success = false;
326 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
327
328 if (NULL != codec) {
329 success = codec->decode(stream, bm, pref, mode);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000330 if (success && format) {
331 *format = codec->getFormat();
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000332 if (kUnknown_Format == *format) {
333 if (stream->rewind()) {
334 *format = GetStreamFormat(stream);
335 }
336 }
reed@android.comb3ade9d2009-06-15 13:04:45 +0000337 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000338 delete codec;
339 }
340 return success;
341}
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000342
scroggo@google.com82449982013-05-01 21:40:58 +0000343/**
344 * This function leaks, but that is okay because it is not intended
345 * to be called. It is only here so that the linker will include the
346 * decoders.
347 * Make sure to keep it in sync with images.gyp, so only the encoders
348 * which are created on a platform are linked.
349 */
350void force_linking();
351void force_linking() {
352 SkASSERT(false);
353 CreateJPEGImageDecoder();
354 CreateWEBPImageDecoder();
355 CreateBMPImageDecoder();
356 CreateICOImageDecoder();
357 CreateWBMPImageDecoder();
358 // Only link GIF and PNG on platforms that build them. See images.gyp
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000359#if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN) && !defined(SK_BUILD_FOR_NACL)
scroggo@google.com82449982013-05-01 21:40:58 +0000360 CreateGIFImageDecoder();
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000361#endif
362#if !defined(SK_BUILD_FOR_MAC) && !defined(SK_BUILD_FOR_WIN)
scroggo@google.com82449982013-05-01 21:40:58 +0000363 CreatePNGImageDecoder();
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000364#endif
scroggo@google.comc4ebdb02013-05-01 21:17:27 +0000365}