Home | History | Annotate | Download | only in gifdecoder
      1 package com.bumptech.glide.gifdecoder;
      2 
      3 import android.util.Log;
      4 
      5 import java.nio.BufferUnderflowException;
      6 import java.nio.ByteBuffer;
      7 import java.nio.ByteOrder;
      8 
      9 import static com.bumptech.glide.gifdecoder.GifDecoder.STATUS_FORMAT_ERROR;
     10 
     11 public class GifHeaderParser {
     12     public static final String TAG = "GifHeaderParser";
     13     /**
     14      * max decoder pixel stack size
     15      */
     16     private static final int MAX_STACK_SIZE = 4096;
     17 
     18     private final ByteBuffer rawData;
     19     private GifHeader header = new GifHeader();
     20 
     21     // Raw data read working array
     22     protected byte[] block = new byte[256]; // current data block
     23     protected int blockSize = 0; // block size last graphic control extension info
     24     protected boolean lctFlag; // local color table flag
     25     protected int lctSize; // local color table size
     26     private short[] prefix;
     27     private byte[] suffix;
     28     private byte[] pixelStack;
     29 
     30     public GifHeaderParser(byte[] data) {
     31         if (data != null) {
     32             rawData = ByteBuffer.wrap(data);
     33             rawData.rewind();
     34             rawData.order(ByteOrder.LITTLE_ENDIAN);
     35         } else {
     36             rawData = null;
     37             header.status = GifDecoder.STATUS_OPEN_ERROR;
     38         }
     39     }
     40 
     41     public GifHeader parseHeader() {
     42         if (err()) {
     43             return header;
     44         }
     45 
     46         readHeader();
     47         if (!err()) {
     48             readContents();
     49             if (header.frameCount < 0) {
     50                 header.status = STATUS_FORMAT_ERROR;
     51             }
     52         }
     53 
     54         return header;
     55     }
     56 
     57     /**
     58      * Main file parser. Reads GIF content blocks.
     59      */
     60     protected void readContents() {
     61         // read GIF file content blocks
     62         boolean done = false;
     63         while (!(done || err())) {
     64             int code = read();
     65             switch (code) {
     66                 case 0x2C: // image separator
     67                     readBitmap();
     68                     break;
     69                 case 0x21: // extension
     70                     code = read();
     71                     switch (code) {
     72                         case 0xf9: // graphics control extension
     73                             //Start a new frame
     74                             header.currentFrame = new GifFrame();
     75                             readGraphicControlExt();
     76                             break;
     77                         case 0xff: // application extension
     78                             readBlock();
     79                             String app = "";
     80                             for (int i = 0; i < 11; i++) {
     81                                 app += (char) block[i];
     82                             }
     83                             if (app.equals("NETSCAPE2.0")) {
     84                                 readNetscapeExt();
     85                             } else {
     86                                 skip(); // don't care
     87                             }
     88                             break;
     89                         case 0xfe:// comment extension
     90                             skip();
     91                             break;
     92                         case 0x01:// plain text extension
     93                             skip();
     94                             break;
     95                         default: // uninteresting extension
     96                             skip();
     97                     }
     98                     break;
     99                 case 0x3b: // terminator
    100                     done = true;
    101                     break;
    102                 case 0x00: // bad byte, but keep going and see what happens break;
    103                 default:
    104                     header.status = STATUS_FORMAT_ERROR;
    105             }
    106         }
    107     }
    108 
    109     /**
    110      * Reads Graphics Control Extension values
    111      */
    112     protected void readGraphicControlExt() {
    113         read(); // block size
    114         int packed = read(); // packed fields
    115         header.currentFrame.dispose = (packed & 0x1c) >> 2; // disposal method
    116         if (header.currentFrame.dispose == 0) {
    117             header.currentFrame.dispose = 1; // elect to keep old image if discretionary
    118         }
    119         header.currentFrame.transparency = (packed & 1) != 0;
    120         header.isTransparent |= header.currentFrame.transparency;
    121         header.currentFrame.delay = readShort() * 10; // delay in milliseconds
    122         header.currentFrame.transIndex = read(); // transparent color index
    123         read(); // block terminator
    124     }
    125 
    126      /**
    127      * Reads next frame image
    128      */
    129     protected void readBitmap() {
    130         header.currentFrame.ix = readShort(); // (sub)image position & size
    131         header.currentFrame.iy = readShort();
    132         header.currentFrame.iw = readShort();
    133         header.currentFrame.ih = readShort();
    134 
    135         int packed = read();
    136         lctFlag = (packed & 0x80) != 0; // 1 - local color table flag interlace
    137         lctSize = (int) Math.pow(2, (packed & 0x07) + 1);
    138         // 3 - sort flag
    139         // 4-5 - reserved lctSize = 2 << (packed & 7); // 6-8 - local color
    140         // table size
    141         header.currentFrame.interlace = (packed & 0x40) != 0;
    142         if (lctFlag) {
    143             header.currentFrame.lct = readColorTable(lctSize); // read table
    144         } else {
    145             header.currentFrame.lct = null; //No local color table
    146         }
    147 
    148         header.currentFrame.bufferFrameStart = rawData.position(); //Save this as the decoding position pointer
    149 
    150         skipBitmapData();  // false decode pixel data to advance buffer.
    151 
    152         skip();
    153         if (err()) {
    154             return;
    155         }
    156 
    157         header.frameCount++;
    158         header.frames.add(header.currentFrame); // add image to frame
    159     }
    160        /**
    161      * Reads Netscape extenstion to obtain iteration count
    162      */
    163     protected void readNetscapeExt() {
    164         do {
    165             readBlock();
    166             if (block[0] == 1) {
    167                 // loop count sub-block
    168                 int b1 = ((int) block[1]) & 0xff;
    169                 int b2 = ((int) block[2]) & 0xff;
    170                 header.loopCount = (b2 << 8) | b1;
    171             }
    172         } while ((blockSize > 0) && !err());
    173     }
    174 
    175 
    176        /**
    177      * Reads GIF file header information.
    178      */
    179     private void readHeader() {
    180         String id = "";
    181         for (int i = 0; i < 6; i++) {
    182             id += (char) read();
    183         }
    184         if (!id.startsWith("GIF")) {
    185             header.status = STATUS_FORMAT_ERROR;
    186             return;
    187         }
    188         readLSD();
    189         if (header.gctFlag && !err()) {
    190             header.gct = readColorTable(header.gctSize);
    191             header.bgColor = header.gct[header.bgIndex];
    192         }
    193     }
    194      /**
    195      * Reads Logical Screen Descriptor
    196      */
    197     protected void readLSD() {
    198         // logical screen size
    199         header.width = readShort();
    200         header.height = readShort();
    201         // packed fields
    202         int packed = read();
    203         header.gctFlag = (packed & 0x80) != 0; // 1 : global color table flag
    204         // 2-4 : color resolution
    205         // 5 : gct sort flag
    206         header.gctSize = 2 << (packed & 7); // 6-8 : gct size
    207         header.bgIndex = read(); // background color index
    208         header.pixelAspect = read(); // pixel aspect ratio
    209 
    210         //Now that we know the size, init scratch arrays
    211         //TODO: these shouldn't go here.
    212 //        mainPixels = new byte[header.width * header.height];
    213 //        mainScratch = new int[header.width * header.height];
    214     }
    215      /**
    216      * Reads color table as 256 RGB integer values
    217      *
    218      * @param ncolors int number of colors to read
    219      * @return int array containing 256 colors (packed ARGB with full alpha)
    220      */
    221     protected int[] readColorTable(int ncolors) {
    222         int nbytes = 3 * ncolors;
    223         int[] tab = null;
    224         byte[] c = new byte[nbytes];
    225 
    226         try {
    227             rawData.get(c);
    228 
    229             tab = new int[256]; // max size to avoid bounds checks
    230             int i = 0;
    231             int j = 0;
    232             while (i < ncolors) {
    233                 int r = ((int) c[j++]) & 0xff;
    234                 int g = ((int) c[j++]) & 0xff;
    235                 int b = ((int) c[j++]) & 0xff;
    236                 tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
    237             }
    238         } catch (BufferUnderflowException e) {
    239             Log.w(TAG, "Format Error Reading Color Table", e);
    240             header.status = STATUS_FORMAT_ERROR;
    241         }
    242 
    243         return tab;
    244     }
    245 
    246       /**
    247      * Decodes LZW image data into pixel array. Adapted from John Cristy's BitmapMagick.
    248      */
    249     protected void skipBitmapData() {
    250         int nullCode = -1;
    251         int npix = header.width * header.height;
    252         int available, clear, code_mask, code_size, end_of_information, in_code, old_code, bits, code, count, i, datum, data_size, first, top, bi, pi;
    253 
    254         if (prefix == null) {
    255             prefix = new short[MAX_STACK_SIZE];
    256         }
    257         if (suffix == null) {
    258             suffix = new byte[MAX_STACK_SIZE];
    259         }
    260         if (pixelStack == null) {
    261             pixelStack = new byte[MAX_STACK_SIZE + 1];
    262         }
    263 
    264         // Initialize GIF data stream decoder.
    265         data_size = read();
    266         clear = 1 << data_size;
    267         end_of_information = clear + 1;
    268         available = clear + 2;
    269         old_code = nullCode;
    270         code_size = data_size + 1;
    271         code_mask = (1 << code_size) - 1;
    272         long start = System.currentTimeMillis();
    273         for (code = 0; code < clear; code++) {
    274             prefix[code] = 0; // XXX ArrayIndexOutOfBoundsException
    275             suffix[code] = (byte) code;
    276         }
    277 
    278         start = System.currentTimeMillis();
    279         // Decode GIF pixel stream.
    280         datum = bits = count = first = top = pi = bi = 0;
    281         int iterations = 0;
    282         for (i = 0; i < npix; ) {
    283             iterations++;
    284             if (top == 0) {
    285                 if (bits < code_size) {
    286                     // Load bytes until there are enough bits for a code.
    287                     if (count == 0) {
    288                         // Read a new data block.
    289                         count = readBlock();
    290                         if (count <= 0) {
    291                             break;
    292                         }
    293                         bi = 0;
    294                     }
    295                     datum += (((int) block[bi]) & 0xff) << bits;
    296                     bits += 8;
    297                     bi++;
    298                     count--;
    299                     continue;
    300                 }
    301                 // Get the next code.
    302                 code = datum & code_mask;
    303                 datum >>= code_size;
    304                 bits -= code_size;
    305                 // Interpret the code
    306                 if ((code > available) || (code == end_of_information)) {
    307                     break;
    308                 }
    309                 if (code == clear) {
    310                     // Reset decoder.
    311                     code_size = data_size + 1;
    312                     code_mask = (1 << code_size) - 1;
    313                     available = clear + 2;
    314                     old_code = nullCode;
    315                     continue;
    316                 }
    317                 if (old_code == nullCode) {
    318                     pixelStack[top++] = suffix[code];
    319                     old_code = code;
    320                     first = code;
    321                     continue;
    322                 }
    323                 in_code = code;
    324                 if (code == available) {
    325                     pixelStack[top++] = (byte) first;
    326                     code = old_code;
    327                 }
    328                 while (code > clear) {
    329                     pixelStack[top++] = suffix[code];
    330                     code = prefix[code];
    331                 }
    332                 first = ((int) suffix[code]) & 0xff;
    333                 // Add a new string to the string table,
    334                 if (available >= MAX_STACK_SIZE) {
    335                     break;
    336                 }
    337                 pixelStack[top++] = (byte) first;
    338                 prefix[available] = (short) old_code;
    339                 suffix[available] = (byte) first;
    340                 available++;
    341                 if (((available & code_mask) == 0) && (available < MAX_STACK_SIZE)) {
    342                     code_size++;
    343                     code_mask += available;
    344                 }
    345                 old_code = in_code;
    346             }
    347             // Pop a pixel off the pixel stack.
    348             top--;
    349             i++;
    350         }
    351     }
    352 
    353       /**
    354      * Skips variable length blocks up to and including next zero length block.
    355      */
    356     protected void skip() {
    357         do {
    358             readBlock();
    359         } while ((blockSize > 0) && !err());
    360     }
    361 
    362      /**
    363      * Reads next variable length block from input.
    364      *
    365      * @return number of bytes stored in "buffer"
    366      */
    367     protected int readBlock() {
    368         blockSize = read();
    369         int n = 0;
    370         if (blockSize > 0) {
    371             try {
    372                 int count;
    373                 while (n < blockSize) {
    374                     count = blockSize - n;
    375                     rawData.get(block, n, count);
    376 
    377                     n += count;
    378                 }
    379             } catch (Exception e) {
    380                 Log.w(TAG, "Error Reading Block", e);
    381                 header.status = STATUS_FORMAT_ERROR;
    382             }
    383         }
    384         return n;
    385     }
    386 
    387       /**
    388      * Reads a single byte from the input stream.
    389      */
    390     private int read() {
    391         int curByte = 0;
    392         try {
    393             curByte = (rawData.get() & 0xFF);
    394         } catch (Exception e) {
    395             header.status = STATUS_FORMAT_ERROR;
    396         }
    397         return curByte;
    398     }
    399 
    400     /**
    401      * Reads next 16-bit value, LSB first
    402      */
    403     protected int readShort() {
    404         // read 16-bit value
    405         return rawData.getShort();
    406     }
    407 
    408     private boolean err() {
    409         return header.status != GifDecoder.STATUS_OK;
    410     }
    411 }
    412