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 // Author: cevans (at) google.com (Chris Evans)
     10 
     11 #include "bmpdecoderhelper.h"
     12 
     13 namespace image_codec {
     14 
     15 static const int kBmpHeaderSize = 14;
     16 static const int kBmpInfoSize = 40;
     17 static const int kBmpOS2InfoSize = 12;
     18 static const int kMaxDim = SHRT_MAX / 2;
     19 
     20 bool BmpDecoderHelper::DecodeImage(const char* p,
     21                                    int len,
     22                                    int max_pixels,
     23                                    BmpDecoderCallback* callback) {
     24   data_ = reinterpret_cast<const uint8*>(p);
     25   pos_ = 0;
     26   len_ = len;
     27   inverted_ = true;
     28   // Parse the header structure.
     29   if (len < kBmpHeaderSize + 4) {
     30     return false;
     31   }
     32   GetShort();  // Signature.
     33   GetInt();  // Size.
     34   GetInt();  // Reserved.
     35   int offset = GetInt();
     36   // Parse the info structure.
     37   int infoSize = GetInt();
     38   if (infoSize != kBmpOS2InfoSize && infoSize < kBmpInfoSize) {
     39     return false;
     40   }
     41   int cols = 0;
     42   int comp = 0;
     43   int colLen = 4;
     44   if (infoSize >= kBmpInfoSize) {
     45     if (len < kBmpHeaderSize + kBmpInfoSize) {
     46       return false;
     47     }
     48     width_ = GetInt();
     49     height_ = GetInt();
     50     GetShort();  // Planes.
     51     bpp_ = GetShort();
     52     comp = GetInt();
     53     GetInt();  // Size.
     54     GetInt();  // XPPM.
     55     GetInt();  // YPPM.
     56     cols = GetInt();
     57     GetInt();  // Important colours.
     58   } else {
     59     if (len < kBmpHeaderSize + kBmpOS2InfoSize) {
     60       return false;
     61     }
     62     colLen = 3;
     63     width_ = GetShort();
     64     height_ = GetShort();
     65     GetShort();  // Planes.
     66     bpp_ = GetShort();
     67   }
     68   if (height_ < 0) {
     69     height_ = -height_;
     70     inverted_ = false;
     71   }
     72   if (width_ <= 0 || width_ > kMaxDim || height_ <= 0 || height_ > kMaxDim) {
     73     return false;
     74   }
     75   if (width_ * height_ > max_pixels) {
     76     return false;
     77   }
     78   if (cols < 0 || cols > 256) {
     79     return false;
     80   }
     81   // Allocate then read in the colour map.
     82   if (cols == 0 && bpp_ <= 8) {
     83     cols = 1 << bpp_;
     84   }
     85   if (bpp_ <= 8 || cols > 0) {
     86     uint8* colBuf = new uint8[256 * 3];
     87     memset(colBuf, '\0', 256 * 3);
     88     colTab_.reset(colBuf);
     89   }
     90   if (cols > 0) {
     91     if (pos_ + (cols * colLen) > len_) {
     92       return false;
     93     }
     94     for (int i = 0; i < cols; ++i) {
     95       int base = i * 3;
     96       colTab_[base + 2] = GetByte();
     97       colTab_[base + 1] = GetByte();
     98       colTab_[base] = GetByte();
     99       if (colLen == 4) {
    100         GetByte();
    101       }
    102     }
    103   }
    104   // Read in the compression data if necessary.
    105   redBits_ = 0x7c00;
    106   greenBits_ = 0x03e0;
    107   blueBits_ = 0x001f;
    108   bool rle = false;
    109   if (comp == 1 || comp == 2) {
    110     rle = true;
    111   } else if (comp == 3) {
    112     if (pos_ + 12 > len_) {
    113       return false;
    114     }
    115     redBits_ = GetInt() & 0xffff;
    116     greenBits_ = GetInt() & 0xffff;
    117     blueBits_ = GetInt() & 0xffff;
    118   }
    119   redShiftRight_ = CalcShiftRight(redBits_);
    120   greenShiftRight_ = CalcShiftRight(greenBits_);
    121   blueShiftRight_ = CalcShiftRight(blueBits_);
    122   redShiftLeft_ = CalcShiftLeft(redBits_);
    123   greenShiftLeft_ = CalcShiftLeft(greenBits_);
    124   blueShiftLeft_ = CalcShiftLeft(blueBits_);
    125   rowPad_ = 0;
    126   pixelPad_ = 0;
    127   int rowLen;
    128   if (bpp_ == 32) {
    129     rowLen = width_ * 4;
    130     pixelPad_ = 1;
    131   } else if (bpp_ == 24) {
    132     rowLen = width_ * 3;
    133   } else if (bpp_ == 16) {
    134     rowLen = width_ * 2;
    135   } else if (bpp_ == 8) {
    136     rowLen = width_;
    137   } else if (bpp_ == 4) {
    138     rowLen = width_ / 2;
    139     if (width_ & 1) {
    140       rowLen++;
    141     }
    142   } else if (bpp_ == 1) {
    143     rowLen = width_ / 8;
    144     if (width_ & 7) {
    145       rowLen++;
    146     }
    147   } else {
    148     return false;
    149   }
    150   // Round the rowLen up to a multiple of 4.
    151   if (rowLen % 4 != 0) {
    152     rowPad_ = 4 - (rowLen % 4);
    153     rowLen += rowPad_;
    154   }
    155 
    156   if (offset > 0 && offset > pos_ && offset < len_) {
    157     pos_ = offset;
    158   }
    159   // Deliberately off-by-one; a load of BMPs seem to have their last byte
    160   // missing.
    161   if (!rle && (pos_ + (rowLen * height_) > len_ + 1)) {
    162     return false;
    163   }
    164 
    165   output_ = callback->SetSize(width_, height_);
    166   if (NULL == output_) {
    167     return true;  // meaning we succeeded, but they want us to stop now
    168   }
    169 
    170   if (rle && (bpp_ == 4 || bpp_ == 8)) {
    171     DoRLEDecode();
    172   } else {
    173     DoStandardDecode();
    174   }
    175   return true;
    176 }
    177 
    178 void BmpDecoderHelper::DoRLEDecode() {
    179   static const uint8 RLE_ESCAPE = 0;
    180   static const uint8 RLE_EOL = 0;
    181   static const uint8 RLE_EOF = 1;
    182   static const uint8 RLE_DELTA = 2;
    183   int x = 0;
    184   int y = height_ - 1;
    185   while (pos_ < len_ - 1) {
    186     uint8 cmd = GetByte();
    187     if (cmd != RLE_ESCAPE) {
    188       uint8 pixels = GetByte();
    189       int num = 0;
    190       uint8 col = pixels;
    191       while (cmd-- && x < width_) {
    192         if (bpp_ == 4) {
    193           if (num & 1) {
    194             col = pixels & 0xf;
    195           } else {
    196             col = pixels >> 4;
    197           }
    198         }
    199         PutPixel(x++, y, col);
    200         num++;
    201       }
    202     } else {
    203       cmd = GetByte();
    204       if (cmd == RLE_EOF) {
    205         return;
    206       } else if (cmd == RLE_EOL) {
    207         x = 0;
    208         y--;
    209         if (y < 0) {
    210           return;
    211         }
    212       } else if (cmd == RLE_DELTA) {
    213         if (pos_ < len_ - 1) {
    214           uint8 dx = GetByte();
    215           uint8 dy = GetByte();
    216           x += dx;
    217           if (x > width_) {
    218             x = width_;
    219           }
    220           y -= dy;
    221           if (y < 0) {
    222             return;
    223           }
    224         }
    225       } else {
    226         int num = 0;
    227         int bytesRead = 0;
    228         uint8 val = 0;
    229         while (cmd-- && pos_ < len_) {
    230           if (bpp_ == 8 || !(num & 1)) {
    231             val = GetByte();
    232             bytesRead++;
    233           }
    234           uint8 col = val;
    235           if (bpp_ == 4) {
    236             if (num & 1) {
    237               col = col & 0xf;
    238             } else {
    239               col >>= 4;
    240             }
    241           }
    242           if (x < width_) {
    243             PutPixel(x++, y, col);
    244           }
    245           num++;
    246         }
    247         // All pixel runs must be an even number of bytes - skip a byte if we
    248         // read an odd number.
    249         if ((bytesRead & 1) && pos_ < len_) {
    250           GetByte();
    251         }
    252       }
    253     }
    254   }
    255 }
    256 
    257 void BmpDecoderHelper::PutPixel(int x, int y, uint8 col) {
    258   CHECK(x >= 0 && x < width_);
    259   CHECK(y >= 0 && y < height_);
    260   if (!inverted_) {
    261     y = height_ - (y + 1);
    262   }
    263 
    264   int base = ((y * width_) + x) * 3;
    265   int colBase = col * 3;
    266   output_[base] = colTab_[colBase];
    267   output_[base + 1] = colTab_[colBase + 1];
    268   output_[base + 2] = colTab_[colBase + 2];
    269 }
    270 
    271 void BmpDecoderHelper::DoStandardDecode() {
    272   int row = 0;
    273   uint8 currVal = 0;
    274   for (int h = height_ - 1; h >= 0; h--, row++) {
    275     int realH = h;
    276     if (!inverted_) {
    277       realH = height_ - (h + 1);
    278     }
    279     uint8* line = output_ + (3 * width_ * realH);
    280     for (int w = 0; w < width_; w++) {
    281       if (bpp_ >= 24) {
    282         line[2] = GetByte();
    283         line[1] = GetByte();
    284         line[0] = GetByte();
    285       } else if (bpp_ == 16) {
    286         uint32 val = GetShort();
    287         line[0] = ((val & redBits_) >> redShiftRight_) << redShiftLeft_;
    288         line[1] = ((val & greenBits_) >> greenShiftRight_) << greenShiftLeft_;
    289         line[2] = ((val & blueBits_) >> blueShiftRight_) << blueShiftLeft_;
    290       } else if (bpp_ <= 8) {
    291         uint8 col;
    292         if (bpp_ == 8) {
    293           col = GetByte();
    294         } else if (bpp_ == 4) {
    295           if ((w % 2) == 0) {
    296             currVal = GetByte();
    297             col = currVal >> 4;
    298           } else {
    299             col = currVal & 0xf;
    300           }
    301         } else {
    302           if ((w % 8) == 0) {
    303             currVal = GetByte();
    304           }
    305           int bit = w & 7;
    306           col = ((currVal >> (7 - bit)) & 1);
    307         }
    308         int base = col * 3;
    309         line[0] = colTab_[base];
    310         line[1] = colTab_[base + 1];
    311         line[2] = colTab_[base + 2];
    312       }
    313       line += 3;
    314       for (int i = 0; i < pixelPad_; ++i) {
    315         GetByte();
    316       }
    317     }
    318     for (int i = 0; i < rowPad_; ++i) {
    319       GetByte();
    320     }
    321   }
    322 }
    323 
    324 int BmpDecoderHelper::GetInt() {
    325   uint8 b1 = GetByte();
    326   uint8 b2 = GetByte();
    327   uint8 b3 = GetByte();
    328   uint8 b4 = GetByte();
    329   return b1 | (b2 << 8) | (b3 << 16) | (b4 << 24);
    330 }
    331 
    332 int BmpDecoderHelper::GetShort() {
    333   uint8 b1 = GetByte();
    334   uint8 b2 = GetByte();
    335   return b1 | (b2 << 8);
    336 }
    337 
    338 uint8 BmpDecoderHelper::GetByte() {
    339   CHECK(pos_ >= 0 && pos_ <= len_);
    340   // We deliberately allow this off-by-one access to cater for BMPs with their
    341   // last byte missing.
    342   if (pos_ == len_) {
    343     return 0;
    344   }
    345   return data_[pos_++];
    346 }
    347 
    348 int BmpDecoderHelper::CalcShiftRight(uint32 mask) {
    349   int ret = 0;
    350   while (mask != 0 && !(mask & 1)) {
    351     mask >>= 1;
    352     ret++;
    353   }
    354   return ret;
    355 }
    356 
    357 int BmpDecoderHelper::CalcShiftLeft(uint32 mask) {
    358   int ret = 0;
    359   while (mask != 0 && !(mask & 1)) {
    360     mask >>= 1;
    361   }
    362   while (mask != 0 && !(mask & 0x80)) {
    363     mask <<= 1;
    364     ret++;
    365   }
    366   return ret;
    367 }
    368 
    369 }  // namespace image_codec
    370