blob: 38aa7fda0d143a2134d89253c179f46781bb1f30 [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)
scroggo@google.com2bbc2c92013-06-14 15:33:20 +000043 , fPreferQualityOverSpeed(false)
44 , fRequireUnpremultipliedColors(false) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000045}
46
47SkImageDecoder::~SkImageDecoder() {
reed@google.com82065d62011-02-07 15:30:46 +000048 SkSafeUnref(fPeeker);
49 SkSafeUnref(fChooser);
50 SkSafeUnref(fAllocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +000051}
52
53SkImageDecoder::Format SkImageDecoder::getFormat() const {
54 return kUnknown_Format;
55}
56
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000057const char* SkImageDecoder::getFormatName() const {
scroggo@google.comf98118e2013-05-15 14:53:49 +000058 return GetFormatName(this->getFormat());
59}
60
61const char* SkImageDecoder::GetFormatName(Format format) {
62 switch (format) {
scroggo@google.com39edf4c2013-04-25 17:33:51 +000063 case kUnknown_Format:
64 return "Unknown Format";
65 case kBMP_Format:
66 return "BMP";
67 case kGIF_Format:
68 return "GIF";
69 case kICO_Format:
70 return "ICO";
71 case kJPEG_Format:
72 return "JPEG";
73 case kPNG_Format:
74 return "PNG";
75 case kWBMP_Format:
76 return "WBMP";
77 case kWEBP_Format:
78 return "WEBP";
79 default:
80 SkASSERT(!"Invalid format type!");
81 }
82 return "Unknown Format";
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000083}
84
reed@android.com8a1c16f2008-12-17 15:59:43 +000085SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
86 SkRefCnt_SafeAssign(fPeeker, peeker);
87 return peeker;
88}
89
90SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
91 SkRefCnt_SafeAssign(fChooser, chooser);
92 return chooser;
93}
94
95SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
96 SkRefCnt_SafeAssign(fAllocator, alloc);
97 return alloc;
98}
99
100void SkImageDecoder::setSampleSize(int size) {
101 if (size < 1) {
102 size = 1;
103 }
104 fSampleSize = size;
105}
106
107bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
108 int height) const {
109 Chooser* chooser = fChooser;
110
111 if (NULL == chooser) { // no chooser, we just say YES to decoding :)
112 return true;
113 }
114 chooser->begin(1);
115 chooser->inspect(0, config, width, height);
116 return chooser->choose() == 0;
117}
118
119bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
120 SkColorTable* ctable) const {
121 return bitmap->allocPixels(fAllocator, ctable);
122}
123
124///////////////////////////////////////////////////////////////////////////////
reed@android.comb6137c32009-07-29 20:56:52 +0000125
reed@android.com3f1f06a2010-03-03 21:04:12 +0000126void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
127 if (NULL == pref) {
128 fUsePrefTable = false;
129 } else {
130 fUsePrefTable = true;
131 memcpy(fPrefTable, pref, sizeof(fPrefTable));
132 }
133}
134
135SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
136 bool srcHasAlpha) const {
137 SkBitmap::Config config;
138
139 if (fUsePrefTable) {
140 int index = 0;
141 switch (srcDepth) {
142 case kIndex_SrcDepth:
143 index = 0;
144 break;
145 case k16Bit_SrcDepth:
146 index = 2;
147 break;
148 case k32Bit_SrcDepth:
149 index = 4;
150 break;
151 }
152 if (srcHasAlpha) {
153 index += 1;
154 }
155 config = fPrefTable[index];
156 } else {
157 config = fDefaultPref;
158 }
159
160 if (SkBitmap::kNo_Config == config) {
161 config = SkImageDecoder::GetDeviceConfig();
162 }
163 return config;
164}
165
reed@android.com8a1c16f2008-12-17 15:59:43 +0000166bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000167 SkBitmap::Config pref, Mode mode, bool reuseBitmap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000168 // we reset this to false before calling onDecode
169 fShouldCancelDecode = false;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000170 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
171 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000172
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000173 if (reuseBitmap) {
174 SkAutoLockPixels alp(*bm);
175 if (NULL != bm->getPixels()) {
176 return this->onDecode(stream, bm, mode);
177 }
178 }
179
180 // pass a temporary bitmap, so that if we return false, we are assured of
181 // leaving the caller's bitmap untouched.
182 SkBitmap tmp;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000183 if (!this->onDecode(stream, &tmp, mode)) {
reed@android.com62900b42009-02-11 15:07:19 +0000184 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000185 }
reed@android.com62900b42009-02-11 15:07:19 +0000186 bm->swap(tmp);
187 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000188}
189
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000190bool SkImageDecoder::decodeSubset(SkBitmap* bm, const SkIRect& rect,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000191 SkBitmap::Config pref) {
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000192 // we reset this to false before calling onDecodeSubset
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000193 fShouldCancelDecode = false;
194 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
195 fDefaultPref = pref;
196
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000197 return this->onDecodeSubset(bm, rect);
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000198}
199
200bool SkImageDecoder::buildTileIndex(SkStream* stream,
201 int *width, int *height) {
202 // we reset this to false before calling onBuildTileIndex
203 fShouldCancelDecode = false;
204
205 return this->onBuildTileIndex(stream, width, height);
206}
207
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000208bool SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
209 int dstX, int dstY, int width, int height,
210 int srcX, int srcY) {
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000211 int w = width / sampleSize;
212 int h = height / sampleSize;
scroggo@google.com7e6fcee2013-05-03 20:14:28 +0000213 if (src->getConfig() == SkBitmap::kIndex8_Config) {
214 // kIndex8 does not allow drawing via an SkCanvas, as is done below.
215 // Instead, use extractSubset. Note that this shares the SkPixelRef and
216 // SkColorTable.
217 // FIXME: Since src is discarded in practice, this holds on to more
218 // pixels than is strictly necessary. Switch to a copy if memory
219 // savings are more important than speed here. This also means
220 // that the pixels in dst can not be reused (though there is no
221 // allocation, which was already done on src).
222 int x = (dstX - srcX) / sampleSize;
223 int y = (dstY - srcY) / sampleSize;
224 SkIRect subset = SkIRect::MakeXYWH(x, y, w, h);
225 return src->extractSubset(dst, subset);
226 }
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000227 // if the destination has no pixels then we must allocate them.
228 if (dst->isNull()) {
229 dst->setConfig(src->getConfig(), w, h);
230 dst->setIsOpaque(src->isOpaque());
231
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
mike@reedtribe.orgf3811622013-03-19 02:18:33 +0000263 SkAutoTUnref<SkStream> stream(SkStream::NewFromFile(file));
264 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.com67882dd2013-05-20 16:58:16 +0000284/**
285 * Special allocator used by DecodeMemoryToTarget. Uses preallocated memory
286 * provided if the bm is 8888. Otherwise, uses a heap allocator. The same
287 * allocator will be used again for a copy to 8888, when the preallocated
288 * memory will be used.
289 */
290class TargetAllocator : public SkBitmap::HeapAllocator {
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000291
292public:
293 TargetAllocator(void* target)
294 : fTarget(target) {}
295
296 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
scroggo@google.com67882dd2013-05-20 16:58:16 +0000297 // If the config is not 8888, allocate a pixelref using the heap.
298 // fTarget will be used to store the final pixels when copied to
299 // 8888.
300 if (bm->config() != SkBitmap::kARGB_8888_Config) {
301 return INHERITED::allocPixelRef(bm, ct);
302 }
303 // In kARGB_8888_Config, there is no colortable.
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000304 SkASSERT(NULL == ct);
305 bm->setPixels(fTarget);
306 return true;
307 }
308
309private:
310 void* fTarget;
scroggo@google.com67882dd2013-05-20 16:58:16 +0000311 typedef SkBitmap::HeapAllocator INHERITED;
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000312};
313
scroggo@google.com67882dd2013-05-20 16:58:16 +0000314/**
315 * Helper function for DecodeMemoryToTarget. DecodeMemoryToTarget wants
316 * 8888, so set the config to it. All parameters must not be null.
317 * @param decoder Decoder appropriate for this stream.
318 * @param stream Rewound stream to the encoded data.
319 * @param bitmap On success, will have its bounds set to the bounds of the
320 * encoded data, and its config set to 8888.
321 * @return True if the bounds were decoded and the bitmap is 8888 or can be
322 * copied to 8888.
323 */
324static bool decode_bounds_to_8888(SkImageDecoder* decoder, SkStream* stream,
325 SkBitmap* bitmap) {
326 SkASSERT(decoder != NULL);
327 SkASSERT(stream != NULL);
328 SkASSERT(bitmap != NULL);
329
330 if (!decoder->decode(stream, bitmap, SkImageDecoder::kDecodeBounds_Mode)) {
331 return false;
332 }
333
334 if (bitmap->config() == SkBitmap::kARGB_8888_Config) {
335 return true;
336 }
337
338 if (!bitmap->canCopyTo(SkBitmap::kARGB_8888_Config)) {
339 return false;
340 }
341
342 bitmap->setConfig(SkBitmap::kARGB_8888_Config, bitmap->width(), bitmap->height());
343 return true;
344}
345
346/**
347 * Helper function for DecodeMemoryToTarget. Decodes the stream into bitmap, and if
348 * the bitmap is not 8888, then it is copied to 8888. Either way, the end result has
349 * its pixels stored in target. All parameters must not be null.
350 * @param decoder Decoder appropriate for this stream.
351 * @param stream Rewound stream to the encoded data.
352 * @param bitmap On success, will contain the decoded image, with its pixels stored
353 * at target.
354 * @param target Preallocated memory for storing pixels.
355 * @return bool Whether the decode (and copy, if necessary) succeeded.
356 */
357static bool decode_pixels_to_8888(SkImageDecoder* decoder, SkStream* stream,
358 SkBitmap* bitmap, void* target) {
359 SkASSERT(decoder != NULL);
360 SkASSERT(stream != NULL);
361 SkASSERT(bitmap != NULL);
362 SkASSERT(target != NULL);
363
364 TargetAllocator allocator(target);
365 decoder->setAllocator(&allocator);
366
367 bool success = decoder->decode(stream, bitmap, SkImageDecoder::kDecodePixels_Mode);
368 decoder->setAllocator(NULL);
369
370 if (!success) {
371 return false;
372 }
373
374 if (bitmap->config() == SkBitmap::kARGB_8888_Config) {
375 return true;
376 }
377
378 SkBitmap bm8888;
379 if (!bitmap->copyTo(&bm8888, SkBitmap::kARGB_8888_Config, &allocator)) {
380 return false;
381 }
382
383 bitmap->swap(bm8888);
384 return true;
385}
386
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000387bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
388 SkImage::Info* info,
389 const SkBitmapFactory::Target* target) {
390 if (NULL == info) {
391 return false;
392 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000393
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000394 // FIXME: Just to get this working, implement in terms of existing
395 // ImageDecoder calls.
396 SkBitmap bm;
397 SkMemoryStream stream(buffer, size);
398 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
scroggo@google.com67882dd2013-05-20 16:58:16 +0000399 if (NULL == decoder.get()) {
400 return false;
401 }
402
403 if (!decode_bounds_to_8888(decoder.get(), &stream, &bm)) {
404 return false;
405 }
406
407 SkASSERT(bm.config() == SkBitmap::kARGB_8888_Config);
408
409 // Now set info properly.
410 // Since Config is SkBitmap::kARGB_8888_Config, SkBitmapToImageInfo
411 // will always succeed.
412 SkAssertResult(SkBitmapToImageInfo(bm, info));
413
414 if (NULL == target) {
415 return true;
416 }
417
418 if (target->fRowBytes != SkToU32(bm.rowBytes())) {
419 if (target->fRowBytes < SkImageMinRowBytes(*info)) {
420 SkASSERT(!"Desired row bytes is too small");
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000421 return false;
422 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000423 bm.setConfig(bm.config(), bm.width(), bm.height(), target->fRowBytes);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000424 }
scroggo@google.com67882dd2013-05-20 16:58:16 +0000425
426 // SkMemoryStream.rewind() will always return true.
427 SkAssertResult(stream.rewind());
428 return decode_pixels_to_8888(decoder.get(), &stream, &bm, target->fAddr);
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000429}
430
431
reed@android.com8a1c16f2008-12-17 15:59:43 +0000432bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000433 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434 SkASSERT(stream);
435 SkASSERT(bm);
436
437 bool success = false;
438 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
439
440 if (NULL != codec) {
441 success = codec->decode(stream, bm, pref, mode);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000442 if (success && format) {
443 *format = codec->getFormat();
scroggo@google.com39edf4c2013-04-25 17:33:51 +0000444 if (kUnknown_Format == *format) {
445 if (stream->rewind()) {
446 *format = GetStreamFormat(stream);
447 }
448 }
reed@android.comb3ade9d2009-06-15 13:04:45 +0000449 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 delete codec;
451 }
452 return success;
453}