blob: 1e3b03929fd21d9b67051f22be6f448d77fd3dea [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
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000021const char *SkImageDecoder::sFormatName[] = {
22 "Unknown Format",
23 "BMP",
24 "GIF",
25 "ICO",
26 "JPEG",
27 "PNG",
28 "WBMP",
29 "WEBP",
30};
31
reed@android.com8a1c16f2008-12-17 15:59:43 +000032static SkBitmap::Config gDeviceConfig = SkBitmap::kNo_Config;
33
34SkBitmap::Config SkImageDecoder::GetDeviceConfig()
35{
36 return gDeviceConfig;
37}
38
39void SkImageDecoder::SetDeviceConfig(SkBitmap::Config config)
40{
41 gDeviceConfig = config;
42}
43
44///////////////////////////////////////////////////////////////////////////////
45
46SkImageDecoder::SkImageDecoder()
47 : fPeeker(NULL), fChooser(NULL), fAllocator(NULL), fSampleSize(1),
reed@android.com3f1f06a2010-03-03 21:04:12 +000048 fDefaultPref(SkBitmap::kNo_Config), fDitherImage(true),
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000049 fUsePrefTable(false),fPreferQualityOverSpeed(false) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000050}
51
52SkImageDecoder::~SkImageDecoder() {
reed@google.com82065d62011-02-07 15:30:46 +000053 SkSafeUnref(fPeeker);
54 SkSafeUnref(fChooser);
55 SkSafeUnref(fAllocator);
reed@android.com8a1c16f2008-12-17 15:59:43 +000056}
57
58SkImageDecoder::Format SkImageDecoder::getFormat() const {
59 return kUnknown_Format;
60}
61
commit-bot@chromium.orga936e372013-03-14 14:42:18 +000062const char* SkImageDecoder::getFormatName() const {
63 SkASSERT(SK_ARRAY_COUNT(sFormatName) == kLastKnownFormat);
64 return sFormatName[this->getFormat()];
65}
66
reed@android.com8a1c16f2008-12-17 15:59:43 +000067SkImageDecoder::Peeker* SkImageDecoder::setPeeker(Peeker* peeker) {
68 SkRefCnt_SafeAssign(fPeeker, peeker);
69 return peeker;
70}
71
72SkImageDecoder::Chooser* SkImageDecoder::setChooser(Chooser* chooser) {
73 SkRefCnt_SafeAssign(fChooser, chooser);
74 return chooser;
75}
76
77SkBitmap::Allocator* SkImageDecoder::setAllocator(SkBitmap::Allocator* alloc) {
78 SkRefCnt_SafeAssign(fAllocator, alloc);
79 return alloc;
80}
81
82void SkImageDecoder::setSampleSize(int size) {
83 if (size < 1) {
84 size = 1;
85 }
86 fSampleSize = size;
87}
88
89bool SkImageDecoder::chooseFromOneChoice(SkBitmap::Config config, int width,
90 int height) const {
91 Chooser* chooser = fChooser;
92
93 if (NULL == chooser) { // no chooser, we just say YES to decoding :)
94 return true;
95 }
96 chooser->begin(1);
97 chooser->inspect(0, config, width, height);
98 return chooser->choose() == 0;
99}
100
101bool SkImageDecoder::allocPixelRef(SkBitmap* bitmap,
102 SkColorTable* ctable) const {
103 return bitmap->allocPixels(fAllocator, ctable);
104}
105
106///////////////////////////////////////////////////////////////////////////////
reed@android.comb6137c32009-07-29 20:56:52 +0000107
reed@android.com3f1f06a2010-03-03 21:04:12 +0000108void SkImageDecoder::setPrefConfigTable(const SkBitmap::Config pref[6]) {
109 if (NULL == pref) {
110 fUsePrefTable = false;
111 } else {
112 fUsePrefTable = true;
113 memcpy(fPrefTable, pref, sizeof(fPrefTable));
114 }
115}
116
117SkBitmap::Config SkImageDecoder::getPrefConfig(SrcDepth srcDepth,
118 bool srcHasAlpha) const {
119 SkBitmap::Config config;
120
121 if (fUsePrefTable) {
122 int index = 0;
123 switch (srcDepth) {
124 case kIndex_SrcDepth:
125 index = 0;
126 break;
127 case k16Bit_SrcDepth:
128 index = 2;
129 break;
130 case k32Bit_SrcDepth:
131 index = 4;
132 break;
133 }
134 if (srcHasAlpha) {
135 index += 1;
136 }
137 config = fPrefTable[index];
138 } else {
139 config = fDefaultPref;
140 }
141
142 if (SkBitmap::kNo_Config == config) {
143 config = SkImageDecoder::GetDeviceConfig();
144 }
145 return config;
146}
147
reed@android.com8a1c16f2008-12-17 15:59:43 +0000148bool SkImageDecoder::decode(SkStream* stream, SkBitmap* bm,
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000149 SkBitmap::Config pref, Mode mode, bool reuseBitmap) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000150 // we reset this to false before calling onDecode
151 fShouldCancelDecode = false;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000152 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
153 fDefaultPref = pref;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000154
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000155 if (reuseBitmap) {
156 SkAutoLockPixels alp(*bm);
157 if (NULL != bm->getPixels()) {
158 return this->onDecode(stream, bm, mode);
159 }
160 }
161
162 // pass a temporary bitmap, so that if we return false, we are assured of
163 // leaving the caller's bitmap untouched.
164 SkBitmap tmp;
reed@android.com3f1f06a2010-03-03 21:04:12 +0000165 if (!this->onDecode(stream, &tmp, mode)) {
reed@android.com62900b42009-02-11 15:07:19 +0000166 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000167 }
reed@android.com62900b42009-02-11 15:07:19 +0000168 bm->swap(tmp);
169 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000170}
171
commit-bot@chromium.orga936e372013-03-14 14:42:18 +0000172bool SkImageDecoder::decodeRegion(SkBitmap* bm, const SkIRect& rect,
173 SkBitmap::Config pref) {
174 // we reset this to false before calling onDecodeRegion
175 fShouldCancelDecode = false;
176 // assign this, for use by getPrefConfig(), in case fUsePrefTable is false
177 fDefaultPref = pref;
178
179 return this->onDecodeRegion(bm, rect);
180}
181
182bool SkImageDecoder::buildTileIndex(SkStream* stream,
183 int *width, int *height) {
184 // we reset this to false before calling onBuildTileIndex
185 fShouldCancelDecode = false;
186
187 return this->onBuildTileIndex(stream, width, height);
188}
189
190void SkImageDecoder::cropBitmap(SkBitmap *dst, SkBitmap *src, int sampleSize,
191 int dstX, int dstY, int width, int height,
192 int srcX, int srcY) {
193 int w = width / sampleSize;
194 int h = height / sampleSize;
195 // if the destination has no pixels then we must allocate them.
196 if (dst->isNull()) {
197 dst->setConfig(src->getConfig(), w, h);
198 dst->setIsOpaque(src->isOpaque());
199
200 if (!this->allocPixelRef(dst, NULL)) {
201 SkDEBUGF(("failed to allocate pixels needed to crop the bitmap"));
202 return;
203 }
204 }
205 // check to see if the destination is large enough to decode the desired
206 // region. If this assert fails we will just draw as much of the source
207 // into the destination that we can.
208 SkASSERT(dst->width() >= w && dst->height() >= h);
209
210 // Set the Src_Mode for the paint to prevent transparency issue in the
211 // dest in the event that the dest was being re-used.
212 SkPaint paint;
213 paint.setXfermodeMode(SkXfermode::kSrc_Mode);
214
215 SkCanvas canvas(*dst);
216 canvas.drawSprite(*src, (srcX - dstX) / sampleSize,
217 (srcY - dstY) / sampleSize,
218 &paint);
219}
220
reed@android.com8a1c16f2008-12-17 15:59:43 +0000221///////////////////////////////////////////////////////////////////////////////
222
223bool SkImageDecoder::DecodeFile(const char file[], SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000224 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225 SkASSERT(file);
226 SkASSERT(bm);
227
228 SkFILEStream stream(file);
229 if (stream.isValid()) {
reed@android.comb3ade9d2009-06-15 13:04:45 +0000230 if (SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format)) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000231 bm->pixelRef()->setURI(file);
tomhudson@google.com1a366212012-01-03 14:42:08 +0000232 return true;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000234 }
235 return false;
236}
237
238bool SkImageDecoder::DecodeMemory(const void* buffer, size_t size, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000239 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000240 if (0 == size) {
241 return false;
242 }
243 SkASSERT(buffer);
244
245 SkMemoryStream stream(buffer, size);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000246 return SkImageDecoder::DecodeStream(&stream, bm, pref, mode, format);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000247}
248
scroggo@google.comf8d7d272013-02-22 21:38:35 +0000249class TargetAllocator : public SkBitmap::Allocator {
250
251public:
252 TargetAllocator(void* target)
253 : fTarget(target) {}
254
255 virtual bool allocPixelRef(SkBitmap* bm, SkColorTable* ct) SK_OVERRIDE {
256 // SkColorTable is not supported by Info/Target model.
257 SkASSERT(NULL == ct);
258 bm->setPixels(fTarget);
259 return true;
260 }
261
262private:
263 void* fTarget;
264};
265
266bool SkImageDecoder::DecodeMemoryToTarget(const void* buffer, size_t size,
267 SkImage::Info* info,
268 const SkBitmapFactory::Target* target) {
269 if (NULL == info) {
270 return false;
271 }
272 // FIXME: Just to get this working, implement in terms of existing
273 // ImageDecoder calls.
274 SkBitmap bm;
275 SkMemoryStream stream(buffer, size);
276 SkAutoTDelete<SkImageDecoder> decoder(SkImageDecoder::Factory(&stream));
277 if (decoder.get() != NULL && decoder->decode(&stream, &bm, kDecodeBounds_Mode)) {
278 // Now set info properly
279 if (!SkBitmapToImageInfo(bm, info)) {
280 return false;
281 }
282
283 // SkBitmapToImageInfo will return false if Index8 is used. kIndex8
284 // is not supported by the Info/Target model, since kIndex8 requires
285 // an SkColorTable, which this model does not keep track of.
286 SkASSERT(bm.config() != SkBitmap::kIndex8_Config);
287
288 if (NULL == target) {
289 return true;
290 }
291
292 if (target->fRowBytes != (uint32_t) bm.rowBytes()) {
293 if (target->fRowBytes < SkImageMinRowBytes(*info)) {
294 SkASSERT(!"Desired row bytes is too small");
295 return false;
296 }
297 bm.setConfig(bm.config(), bm.width(), bm.height(), target->fRowBytes);
298 }
299
300 TargetAllocator allocator(target->fAddr);
301 decoder->setAllocator(&allocator);
302 stream.rewind();
303 bool success = decoder->decode(&stream, &bm, kDecodePixels_Mode);
304 // Remove the allocator, since it's on the stack.
305 decoder->setAllocator(NULL);
306 return success;
307 }
308 return false;
309}
310
311
reed@android.com8a1c16f2008-12-17 15:59:43 +0000312bool SkImageDecoder::DecodeStream(SkStream* stream, SkBitmap* bm,
reed@android.comb3ade9d2009-06-15 13:04:45 +0000313 SkBitmap::Config pref, Mode mode, Format* format) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000314 SkASSERT(stream);
315 SkASSERT(bm);
316
317 bool success = false;
318 SkImageDecoder* codec = SkImageDecoder::Factory(stream);
319
320 if (NULL != codec) {
321 success = codec->decode(stream, bm, pref, mode);
reed@android.comb3ade9d2009-06-15 13:04:45 +0000322 if (success && format) {
323 *format = codec->getFormat();
324 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000325 delete codec;
326 }
327 return success;
328}