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