Home | History | Annotate | Download | only in images
      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:
     20     SkBMPImageDecoder() {}
     21 
     22     virtual Format getFormat() const {
     23         return kBMP_Format;
     24     }
     25 
     26 protected:
     27     virtual bool onDecode(SkStream* stream, SkBitmap* bm, Mode mode);
     28 };
     29 
     30 ///////////////////////////////////////////////////////////////////////////////
     31 DEFINE_DECODER_CREATOR(BMPImageDecoder);
     32 ///////////////////////////////////////////////////////////////////////////////
     33 
     34 static SkImageDecoder* sk_libbmp_dfactory(SkStream* stream) {
     35     static const char kBmpMagic[] = { 'B', 'M' };
     36 
     37     size_t len = stream->getLength();
     38     char buffer[sizeof(kBmpMagic)];
     39 
     40     if (len > sizeof(kBmpMagic) &&
     41             stream->read(buffer, sizeof(kBmpMagic)) == sizeof(kBmpMagic) &&
     42             !memcmp(buffer, kBmpMagic, sizeof(kBmpMagic))) {
     43         return SkNEW(SkBMPImageDecoder);
     44     }
     45     return NULL;
     46 }
     47 
     48 static SkTRegistry<SkImageDecoder*, SkStream*> gReg(sk_libbmp_dfactory);
     49 
     50 ///////////////////////////////////////////////////////////////////////////////
     51 
     52 class SkBmpDecoderCallback : public image_codec::BmpDecoderCallback {
     53 public:
     54     // we don't copy the bitmap, just remember the pointer
     55     SkBmpDecoderCallback(bool justBounds) : fJustBounds(justBounds) {}
     56 
     57     // override from BmpDecoderCallback
     58     virtual uint8* SetSize(int width, int height) {
     59         fWidth = width;
     60         fHeight = height;
     61         if (fJustBounds) {
     62             return NULL;
     63         }
     64 
     65         fRGB.setCount(width * height * 3);  // 3 == r, g, b
     66         return fRGB.begin();
     67     }
     68 
     69     int width() const { return fWidth; }
     70     int height() const { return fHeight; }
     71     uint8_t* rgb() const { return fRGB.begin(); }
     72 
     73 private:
     74     SkTDArray<uint8_t> fRGB;
     75     int fWidth;
     76     int fHeight;
     77     bool fJustBounds;
     78 };
     79 
     80 bool SkBMPImageDecoder::onDecode(SkStream* stream, SkBitmap* bm, Mode mode) {
     81 
     82     size_t length = stream->getLength();
     83     SkAutoMalloc storage(length);
     84 
     85     if (stream->read(storage.get(), length) != length) {
     86         return false;
     87     }
     88 
     89     const bool justBounds = SkImageDecoder::kDecodeBounds_Mode == mode;
     90     SkBmpDecoderCallback callback(justBounds);
     91 
     92     // Now decode the BMP into callback's rgb() array [r,g,b, r,g,b, ...]
     93     {
     94         image_codec::BmpDecoderHelper helper;
     95         const int max_pixels = 16383*16383; // max width*height
     96         if (!helper.DecodeImage((const char*)storage.get(), length,
     97                                 max_pixels, &callback)) {
     98             return false;
     99         }
    100     }
    101 
    102     // we don't need this anymore, so free it now (before we try to allocate
    103     // the bitmap's pixels) rather than waiting for its destructor
    104     storage.free();
    105 
    106     int width = callback.width();
    107     int height = callback.height();
    108     SkBitmap::Config config = this->getPrefConfig(k32Bit_SrcDepth, false);
    109 
    110     // only accept prefConfig if it makes sense for us
    111     if (SkBitmap::kARGB_4444_Config != config &&
    112             SkBitmap::kRGB_565_Config != config) {
    113         config = SkBitmap::kARGB_8888_Config;
    114     }
    115 
    116     SkScaledBitmapSampler sampler(width, height, getSampleSize());
    117 
    118     if (justBounds) {
    119         bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
    120         bm->setIsOpaque(true);
    121         return true;
    122     }
    123 #ifdef SK_BUILD_FOR_ANDROID
    124     // No Bitmap reuse supported for this format
    125     if (!bm->isNull()) {
    126         return false;
    127     }
    128 #endif
    129     bm->setConfig(config, sampler.scaledWidth(), sampler.scaledHeight());
    130     bm->setIsOpaque(true);
    131 
    132     if (!this->allocPixelRef(bm, NULL)) {
    133         return false;
    134     }
    135 
    136     SkAutoLockPixels alp(*bm);
    137 
    138     if (!sampler.begin(bm, SkScaledBitmapSampler::kRGB, getDitherImage())) {
    139         return false;
    140     }
    141 
    142     const int srcRowBytes = width * 3;
    143     const int dstHeight = sampler.scaledHeight();
    144     const uint8_t* srcRow = callback.rgb();
    145 
    146     srcRow += sampler.srcY0() * srcRowBytes;
    147     for (int y = 0; y < dstHeight; y++) {
    148         sampler.next(srcRow);
    149         srcRow += sampler.srcDY() * srcRowBytes;
    150     }
    151     return true;
    152 }
    153