blob: b5e49e8cda3d38a093df2d88604e6c1e570fab56 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
reed@android.com8a1c16f2008-12-17 15:59:43 +00002/*
epoger@google.comec3ed6a2011-07-28 14:26:00 +00003 * Copyright 2007 The Android Open Source Project
reed@android.com8a1c16f2008-12-17 15:59:43 +00004 *
epoger@google.comec3ed6a2011-07-28 14:26:00 +00005 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
reed@android.com8a1c16f2008-12-17 15:59:43 +00007 */
epoger@google.comec3ed6a2011-07-28 14:26:00 +00008
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "bmpdecoderhelper.h"
11#include "SkImageDecoder.h"
12#include "SkScaledBitmapSampler.h"
13#include "SkStream.h"
14#include "SkColorPriv.h"
15#include "SkTDArray.h"
reed@android.com00bf85a2009-01-22 13:04:56 +000016#include "SkTRegistry.h"
reed@android.com8a1c16f2008-12-17 15:59:43 +000017
18class SkBMPImageDecoder : public SkImageDecoder {
19public:
20 SkBMPImageDecoder() {}
21
22 virtual Format getFormat() const {
23 return kBMP_Format;
24 }
25
26protected:
reed@android.com3f1f06a2010-03-03 21:04:12 +000027 virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
reed@android.com8a1c16f2008-12-17 15:59:43 +000028};
29
reed@android.com00bf85a2009-01-22 13:04:56 +000030static SkImageDecoder* Factory(SkStream* stream) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000031 static const char kBmpMagic[] = { 'B', 'M' };
32
33 size_t len = stream->getLength();
34 char buffer[sizeof(kBmpMagic)];
35
36 if (len > sizeof(kBmpMagic) &&
37 stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
38 !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) {
39 return SkNEW(SkBMPImageDecoder);
40 }
41 return NULL;
42}
43
reed@android.com00bf85a2009-01-22 13:04:56 +000044static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
45
reed@android.com8a1c16f2008-12-17 15:59:43 +000046///////////////////////////////////////////////////////////////////////////////
47
48class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
49public:
50 // we don't copy the bitmap, just remember the pointer
51 SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
52
53 // override from BmpDecoderCallback
54 virtual uint8* SetSize(int width, int height) {
55 fWidth = width;
56 fHeight = height;
57 if (fJustBounds) {
58 return NULL;
59 }
60
61 fRGB.setCount(width * height * 3); // 3 == r, g, b
62 return fRGB.begin();
63 }
64
65 int width() const { return fWidth; }
66 int height() const { return fHeight; }
67 uint8_t* rgb() const { return fRGB.begin(); }
68
69private:
70 SkTDArray<uint8_t> fRGB;
71 int fWidth;
72 int fHeight;
73 bool fJustBounds;
74};
75
reed@android.com3f1f06a2010-03-03 21:04:12 +000076bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
reed@android.com8a1c16f2008-12-17 15:59:43 +000077
78 size_t length = stream->getLength();
79 SkAutoMalloc storage(length);
80
81 if (stream->read(storage.get(), length) != length) {
82 return false;
83 }
84
85 const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
86 SkBmpDecoderCallback callback(justBounds);
87
88 // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]
89 {
90 image_codec::BmpDecoderHelper helper;
91 const int max_pixels = 16383*16383; // max width*height
92 if (!helper.DecodeImage((const char*)storage.get(), length,
93 max_pixels, &callback)) {
94 return false;
95 }
96 }
97
98 // we don't need this anymore, so free it now (before we try to allocate
99 // the bitmap's pixels) rather than waiting for its destructor
100 storage.free();
101
102 int width = callback.width();
103 int height = callback.height();
reed@android.com3f1f06a2010-03-03 21:04:12 +0000104 SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
105
reed@android.com8a1c16f2008-12-17 15:59:43 +0000106 // only accept prefConfig if it makes sense for us
reed@android.com3f1f06a2010-03-03 21:04:12 +0000107 if (SkBitmap::kARGB_4444_Config != config &&
108 SkBitmap::kRGB_565_Config != config) {
109 config = SkBitmap::kARGB_8888_Config;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000110 }
111
112 SkScaledBitmapSampler sampler(width, height, getSampleSize());
113
114 bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
115 bm->setIsOpaque(true);
116 if (justBounds) {
117 return true;
118 }
119
120 if (!this->allocPixelRef(bm, NULL)) {
121 return false;
122 }
123
124 SkAutoLockPixels alp(*bm);
125
126 if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) {
127 return false;
128 }
129
130 const int srcRowBytes = width * 3;
131 const int dstHeight = sampler.scaledHeight();
132 const uint8_t* srcRow = callback.rgb();
133
134 srcRow += sampler.srcY0() * srcRowBytes;
135 for (int y = 0; y < dstHeight; y++) {
136 sampler.next(srcRow);
137 srcRow += sampler.srcDY() * srcRowBytes;
138 }
139 return true;
140}