• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 
2 /*
3  * Copyright 2007 The Android Open Source Project
4  *
5  * Use of this source code is governed by a BSD-style license that can be
6  * found in the LICENSE file.
7  */
8 
9 
10 #include "bmpdecoderhelper.h"
11 #include "SkImageDecoder.h"
12 #include "SkScaledBitmapSampler.h"
13 #include "SkStream.h"
14 #include "SkColorPriv.h"
15 #include "SkTDArray.h"
16 #include "SkTRegistry.h"
17 
18 class SkBMPImageDecoder : public SkImageDecoder {
19 public:
SkBMPImageDecoder()20     SkBMPImageDecoder() {}
21 
getFormat() const22     virtual Format getFormat() const {
23         return kBMP_Format;
24     }
25 
26 protected:
27     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
28 };
29 
Factory(SkStream * stream)30 static SkImageDecoder* Factory(SkStream* stream) {
31     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 
44 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
45 
46 ///////////////////////////////////////////////////////////////////////////////
47 
48 class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
49 public:
50     // we don't copy the bitmap, just remember the pointer
SkBmpDecoderCallback(bool justBounds)51     SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
52 
53     // override from BmpDecoderCallback
SetSize(int width,int height)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 
width() const65     int width() const { return fWidth; }
height() const66     int height() const { return fHeight; }
rgb() const67     uint8_t* rgb() const { return fRGB.begin(); }
68 
69 private:
70     SkTDArray<uint8_t> fRGB;
71     int fWidth;
72     int fHeight;
73     bool fJustBounds;
74 };
75 
onDecode(SkStream * stream,SkBitmap * bm,Mode mode)76 bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
77 
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();
104     SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
105 
106     // only accept prefConfig if it makes sense for us
107     if (SkBitmap::kARGB_4444_Config != config &&
108             SkBitmap::kRGB_565_Config != config) {
109         config = SkBitmap::kARGB_8888_Config;
110     }
111 
112     SkScaledBitmapSampler sampler(width, height, getSampleSize());
113 
114     if (justBounds) {
115         bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
116         bm->setIsOpaque(true);
117         return true;
118     }
119 #ifdef SK_BUILD_FOR_ANDROID
120     // No Bitmap reuse supported for this format
121     if (!bm->isNull()) {
122         return false;
123     }
124 #endif
125     bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
126     bm->setIsOpaque(true);
127 
128     if (!this->allocPixelRef(bm, NULL)) {
129         return false;
130     }
131 
132     SkAutoLockPixels alp(*bm);
133 
134     if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) {
135         return false;
136     }
137 
138     const int srcRowBytes = width * 3;
139     const int dstHeight = sampler.scaledHeight();
140     const uint8_t* srcRow = callback.rgb();
141 
142     srcRow += sampler.srcY0() * srcRowBytes;
143     for (int y = 0; y < dstHeight; y++) {
144         sampler.next(srcRow);
145         srcRow += sampler.srcDY() * srcRowBytes;
146     }
147     return true;
148 }
149