• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2007, The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "bmpdecoderhelper.h"
18 #include "SkImageDecoder.h"
19 #include "SkScaledBitmapSampler.h"
20 #include "SkStream.h"
21 #include "SkColorPriv.h"
22 #include "SkTDArray.h"
23 #include "SkTRegistry.h"
24 
25 class SkBMPImageDecoder : public SkImageDecoder {
26 public:
SkBMPImageDecoder()27     SkBMPImageDecoder() {}
28 
getFormat() const29     virtual Format getFormat() const {
30         return kBMP_Format;
31     }
32 
33 protected:
34     virtual bool onDecode(SkStream* stream, SkBitmap* bm,
35                           SkBitmap::Config pref, Mode mode);
36 };
37 
Factory(SkStream * stream)38 static SkImageDecoder* Factory(SkStream* stream) {
39     static const char kBmpMagic[] = { 'B', 'M' };
40 
41     size_t len = stream->getLength();
42     char buffer[sizeof(kBmpMagic)];
43 
44     if (len > sizeof(kBmpMagic) &&
45             stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
46             !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) {
47         return SkNEW(SkBMPImageDecoder);
48     }
49     return NULL;
50 }
51 
52 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(Factory);
53 
54 ///////////////////////////////////////////////////////////////////////////////
55 
56 class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
57 public:
58     // we don't copy the bitmap, just remember the pointer
SkBmpDecoderCallback(bool justBounds)59     SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
60 
61     // override from BmpDecoderCallback
SetSize(int width,int height)62     virtual uint8* SetSize(int width, int height) {
63         fWidth = width;
64         fHeight = height;
65         if (fJustBounds) {
66             return NULL;
67         }
68 
69         fRGB.setCount(width * height * 3);  // 3 == r, g, b
70         return fRGB.begin();
71     }
72 
width() const73     int width() const { return fWidth; }
height() const74     int height() const { return fHeight; }
rgb() const75     uint8_t* rgb() const { return fRGB.begin(); }
76 
77 private:
78     SkTDArray<uint8_t> fRGB;
79     int fWidth;
80     int fHeight;
81     bool fJustBounds;
82 };
83 
onDecode(SkStream * stream,SkBitmap * bm,SkBitmap::Config prefConfig,Mode mode)84 bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm,
85                                  SkBitmap::Config prefConfig, Mode mode) {
86 
87     size_t length = stream->getLength();
88     SkAutoMalloc storage(length);
89 
90     if (stream->read(storage.get(), length) != length) {
91         return false;
92     }
93 
94     const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
95     SkBmpDecoderCallback callback(justBounds);
96 
97     // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]
98     {
99         image_codec::BmpDecoderHelper helper;
100         const int max_pixels = 16383*16383; // max width*height
101         if (!helper.DecodeImage((const char*)storage.get(), length,
102                                 max_pixels, &callback)) {
103             return false;
104         }
105     }
106 
107     // we don't need this anymore, so free it now (before we try to allocate
108     // the bitmap's pixels) rather than waiting for its destructor
109     storage.free();
110 
111     int width = callback.width();
112     int height = callback.height();
113     SkBitmap::Config config = SkBitmap::kARGB_8888_Config;
114 
115     // only accept prefConfig if it makes sense for us
116     if (SkBitmap::kARGB_4444_Config == prefConfig ||
117             SkBitmap::kRGB_565_Config == config) {
118         config = prefConfig;
119     }
120 
121     SkScaledBitmapSampler sampler(width, height, getSampleSize());
122 
123     bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
124     bm->setIsOpaque(true);
125     if (justBounds) {
126         return true;
127     }
128 
129     if (!this->allocPixelRef(bm, NULL)) {
130         return false;
131     }
132 
133     SkAutoLockPixels alp(*bm);
134 
135     if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) {
136         return false;
137     }
138 
139     const int srcRowBytes = width * 3;
140     const int dstHeight = sampler.scaledHeight();
141     const uint8_t* srcRow = callback.rgb();
142 
143     srcRow += sampler.srcY0() * srcRowBytes;
144     for (int y = 0; y < dstHeight; y++) {
145         sampler.next(srcRow);
146         srcRow += sampler.srcDY() * srcRowBytes;
147     }
148     return true;
149 }
150