GifHeaderParser.java revision e4ac1f593a328f35ca17d8603a54db3826da2475
10f49c87b2f26f3e086f021461b7e5409a7d42be0Sam Juddpackage com.bumptech.glide.gifdecoder; 24f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 34f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Juddimport android.util.Log; 44f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 54f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Juddimport java.nio.BufferUnderflowException; 64f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Juddimport java.nio.ByteBuffer; 74f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Juddimport java.nio.ByteOrder; 85ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Juddimport java.util.Arrays; 94f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 100f49c87b2f26f3e086f021461b7e5409a7d42be0Sam Juddimport static com.bumptech.glide.gifdecoder.GifDecoder.STATUS_FORMAT_ERROR; 114f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 12b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd/** 13b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd * A class responsible for creating {@link com.bumptech.glide.gifdecoder.GifHeader}s from data representing animated 14b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd * gifs. 15b5419dc08eb0a0f82821d774435720e5a31bc936Sam Judd */ 164f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Juddpublic class GifHeaderParser { 174f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd public static final String TAG = "GifHeaderParser"; 184f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 194fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd // The minimum frame delay in hundredths of a second. 204fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd static final int MIN_FRAME_DELAY = 3; 214fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd // The default frame delay in hundredths of a second for GIFs with frame delays less than the minimum. 224fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd static final int DEFAULT_FRAME_DELAY = 10; 234fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd 243640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private static final int MAX_BLOCK_SIZE = 256; 25fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Raw data read working array. 263640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private final byte[] block = new byte[MAX_BLOCK_SIZE]; 27548ff09b7c987156a3f927e8988d798959a0e575Sam Judd 28548ff09b7c987156a3f927e8988d798959a0e575Sam Judd private ByteBuffer rawData; 29548ff09b7c987156a3f927e8988d798959a0e575Sam Judd private GifHeader header; 30fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd private int blockSize = 0; 314f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 325ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd public GifHeaderParser setData(byte[] data) { 335ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd reset(); 344f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (data != null) { 354f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd rawData = ByteBuffer.wrap(data); 364f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd rawData.rewind(); 374f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd rawData.order(ByteOrder.LITTLE_ENDIAN); 384f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } else { 394f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd rawData = null; 404f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.status = GifDecoder.STATUS_OPEN_ERROR; 414f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 425ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd return this; 435ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd } 445ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd 45e4ac1f593a328f35ca17d8603a54db3826da2475Sam Judd public void clear() { 46e4ac1f593a328f35ca17d8603a54db3826da2475Sam Judd rawData = null; 47e4ac1f593a328f35ca17d8603a54db3826da2475Sam Judd header = null; 48e4ac1f593a328f35ca17d8603a54db3826da2475Sam Judd } 49e4ac1f593a328f35ca17d8603a54db3826da2475Sam Judd 505ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd private void reset() { 515ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd rawData = null; 525ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd Arrays.fill(block, (byte) 0); 535ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd header = new GifHeader(); 545ce0c1172e076cd72af8efbde97cffca5f215ba1Sam Judd blockSize = 0; 554f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 564f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 574f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd public GifHeader parseHeader() { 58855776275d8ca409f968c8fceff4d11f51bf8592Sam Judd if (rawData == null) { 59855776275d8ca409f968c8fceff4d11f51bf8592Sam Judd throw new IllegalStateException("You must call setData() before parseHeader()"); 60855776275d8ca409f968c8fceff4d11f51bf8592Sam Judd } 614f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (err()) { 624f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd return header; 634f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 644f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 654f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd readHeader(); 664f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (!err()) { 674f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd readContents(); 684f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (header.frameCount < 0) { 694f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.status = STATUS_FORMAT_ERROR; 704f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 714f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 724f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 734f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd return header; 744f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 754f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 764f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd /** 774f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd * Main file parser. Reads GIF content blocks. 784f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 793640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private void readContents() { 80fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Read GIF file content blocks. 814f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd boolean done = false; 824f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd while (!(done || err())) { 834f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int code = read(); 844f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd switch (code) { 85fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Image separator. 86fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd case 0x2C: 8734cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd // The graphics control extension is optional, but will always come first if it exists. If one did 8834cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd // exist, there will be a non-null current frame which we should use. However if one did not exist, 8934cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd // the current frame will be null and we must create it here. See issue #134. 9034cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd if (header.currentFrame == null) { 9134cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd header.currentFrame = new GifFrame(); 9234cde327bc68d61e7aa64ed122b1d60250bc28d7Sam Judd } 934f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd readBitmap(); 944f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd break; 95fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Extension. 96fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd case 0x21: 974f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd code = read(); 984f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd switch (code) { 99fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Graphics control extension. 100fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd case 0xf9: 101fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Start a new frame. 1024f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.currentFrame = new GifFrame(); 1034f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd readGraphicControlExt(); 1044f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd break; 105fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Application extension. 106fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd case 0xff: 1074f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd readBlock(); 1084f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd String app = ""; 1094f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd for (int i = 0; i < 11; i++) { 1104f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd app += (char) block[i]; 1114f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 1124f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (app.equals("NETSCAPE2.0")) { 1134f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd readNetscapeExt(); 1144f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } else { 115fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Don't care. 116fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd skip(); 1174f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 1184f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd break; 119fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Comment extension. 120fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd case 0xfe: 1214f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd skip(); 1224f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd break; 123fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Plain text extension. 124fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd case 0x01: 1254f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd skip(); 1264f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd break; 127fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Uninteresting extension. 128fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd default: 1294f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd skip(); 1304f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 1314f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd break; 132fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Terminator. 133fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd case 0x3b: 1344f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd done = true; 1354f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd break; 136fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Bad byte, but keep going and see what happens break; 137fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd case 0x00: 1384f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd default: 1394f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.status = STATUS_FORMAT_ERROR; 1404f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 1414f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 1424f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 1434f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 1444f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd /** 145fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd * Reads Graphics Control Extension values. 1464f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 1473640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private void readGraphicControlExt() { 148fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Block size. 149fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd read(); 150fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Packed fields. 151fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd int packed = read(); 152fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Disposal method. 153fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.currentFrame.dispose = (packed & 0x1c) >> 2; 1544f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (header.currentFrame.dispose == 0) { 155fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Elect to keep old image if discretionary. 156fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.currentFrame.dispose = 1; 1574f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 1584f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.currentFrame.transparency = (packed & 1) != 0; 159fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Delay in milliseconds. 1604fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd int delayInHundredthsOfASecond = readShort(); 1614fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd // TODO: consider allowing -1 to indicate show forever. 1624fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd if (delayInHundredthsOfASecond < MIN_FRAME_DELAY) { 1634fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd delayInHundredthsOfASecond = DEFAULT_FRAME_DELAY; 1644fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd } 1654fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16Sam Judd header.currentFrame.delay = delayInHundredthsOfASecond * 10; 166fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Transparent color index 167fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.currentFrame.transIndex = read(); 168fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Block terminator 169fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd read(); 1704f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 1714f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 172fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** 173fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd * Reads next frame image. 1744f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 1753640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private void readBitmap() { 176fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // (sub)image position & size. 177fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.currentFrame.ix = readShort(); 1784f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.currentFrame.iy = readShort(); 1794f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.currentFrame.iw = readShort(); 1804f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.currentFrame.ih = readShort(); 1814f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 1824f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int packed = read(); 183fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // 1 - local color table flag interlace 184fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd boolean lctFlag = (packed & 0x80) != 0; 185fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd int lctSize = (int) Math.pow(2, (packed & 0x07) + 1); 1864f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd // 3 - sort flag 1874f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd // 4-5 - reserved lctSize = 2 << (packed & 7); // 6-8 - local color 1884f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd // table size 1894f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.currentFrame.interlace = (packed & 0x40) != 0; 1904f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (lctFlag) { 191fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Read table. 192fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.currentFrame.lct = readColorTable(lctSize); 1934f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } else { 194fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // No local color table. 195fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.currentFrame.lct = null; 1964f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 1974f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 198fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Save this as the decoding position pointer. 199fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.currentFrame.bufferFrameStart = rawData.position(); 2004f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 201fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // False decode pixel data to advance buffer. 2023640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd skipImageData(); 2034f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 2044f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (err()) { 2054f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd return; 2064f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 2074f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 2084f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.frameCount++; 209fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Add image to frame. 210fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.frames.add(header.currentFrame); 2114f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 212fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** 213fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd * Reads Netscape extension to obtain iteration count. 2144f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 2153640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private void readNetscapeExt() { 2164f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd do { 2174f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd readBlock(); 2184f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (block[0] == 1) { 219fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Loop count sub-block. 2204f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int b1 = ((int) block[1]) & 0xff; 2214f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int b2 = ((int) block[2]) & 0xff; 2224f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.loopCount = (b2 << 8) | b1; 2234f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 2244f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } while ((blockSize > 0) && !err()); 2254f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 2264f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 2274f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 228fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** 2294f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd * Reads GIF file header information. 2304f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 2314f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd private void readHeader() { 2324f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd String id = ""; 2334f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd for (int i = 0; i < 6; i++) { 2344f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd id += (char) read(); 2354f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 2364f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (!id.startsWith("GIF")) { 2374f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.status = STATUS_FORMAT_ERROR; 2384f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd return; 2394f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 2404f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd readLSD(); 2414f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (header.gctFlag && !err()) { 2424f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.gct = readColorTable(header.gctSize); 2434f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.bgColor = header.gct[header.bgIndex]; 2444f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 2454f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 246fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** 247fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd * Reads Logical Screen Descriptor. 2484f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 2493640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private void readLSD() { 250fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Logical screen size. 2514f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.width = readShort(); 2524f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.height = readShort(); 253fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Packed fields 2544f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int packed = read(); 255fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // 1 : global color table flag. 256fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.gctFlag = (packed & 0x80) != 0; 257fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // 2-4 : color resolution. 258fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // 5 : gct sort flag. 259fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // 6-8 : gct size. 260fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.gctSize = 2 << (packed & 7); 261fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Background color index. 262fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.bgIndex = read(); 263fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Pixel aspect ratio 264fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd header.pixelAspect = read(); 2654f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 266fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd 267fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** 268fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd * Reads color table as 256 RGB integer values. 2694f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd * 270fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd * @param ncolors int number of colors to read. 271fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd * @return int array containing 256 colors (packed ARGB with full alpha). 2724f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 2733640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private int[] readColorTable(int ncolors) { 2744f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int nbytes = 3 * ncolors; 2754f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int[] tab = null; 2764f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd byte[] c = new byte[nbytes]; 2774f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 2784f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd try { 2794f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd rawData.get(c); 2804f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 2813640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd // TODO: what bounds checks are we avoiding if we know the number of colors? 282fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Max size to avoid bounds checks. 2833640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd tab = new int[MAX_BLOCK_SIZE]; 2844f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int i = 0; 2854f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int j = 0; 2864f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd while (i < ncolors) { 2874f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int r = ((int) c[j++]) & 0xff; 2884f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int g = ((int) c[j++]) & 0xff; 2894f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int b = ((int) c[j++]) & 0xff; 2904f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd tab[i++] = 0xff000000 | (r << 16) | (g << 8) | b; 2914f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 2924f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } catch (BufferUnderflowException e) { 2934f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd Log.w(TAG, "Format Error Reading Color Table", e); 2944f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.status = STATUS_FORMAT_ERROR; 2954f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 2964f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 2974f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd return tab; 2984f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 2994f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 300fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** 3013640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd * Skips LZW image data for a single frame to advance buffer. 3024f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 3033640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private void skipImageData() { 3043640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd // lzwMinCodeSize 3053640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd read(); 3063640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd // data sub-blocks 3073640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd skip(); 3084f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 3094f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 310fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** 3114f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd * Skips variable length blocks up to and including next zero length block. 3124f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 3133640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private void skip() { 3143640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd int blockSize; 3154f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd do { 3163640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd blockSize = read(); 3173640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd rawData.position(rawData.position() + blockSize); 3183640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd } while (blockSize > 0); 3194f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 3204f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 321fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** 3224f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd * Reads next variable length block from input. 3234f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd * 3244f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd * @return number of bytes stored in "buffer" 3254f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 3263640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private int readBlock() { 3274f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd blockSize = read(); 3284f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int n = 0; 3294f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd if (blockSize > 0) { 33021c2822985058df655ee4874798297f74c5e367eSam Judd int count = 0; 3314f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd try { 3324f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd while (n < blockSize) { 3334f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd count = blockSize - n; 3344f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd rawData.get(block, n, count); 3354f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 3364f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd n += count; 3374f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 3384f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } catch (Exception e) { 33921c2822985058df655ee4874798297f74c5e367eSam Judd Log.w(TAG, "Error Reading Block n: " + n + " count: " + count + " blockSize: " + blockSize, e); 3404f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.status = STATUS_FORMAT_ERROR; 3414f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 3424f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 3434f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd return n; 3444f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 3454f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 346fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd /** 3474f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd * Reads a single byte from the input stream. 3484f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 3494f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd private int read() { 3504f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd int curByte = 0; 3514f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd try { 352fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd curByte = rawData.get() & 0xFF; 3534f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } catch (Exception e) { 3544f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd header.status = STATUS_FORMAT_ERROR; 3554f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 3564f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd return curByte; 3574f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 3584f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 3594f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd /** 360fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd * Reads next 16-bit value, LSB first. 3614f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd */ 3623640e3493c7ea15243d8f0e953b5f0486a40c8a1Sam Judd private int readShort() { 363fe090f50f3040f4d478143a3e0ffa8cdf813fefcSam Judd // Read 16-bit value. 3644f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd return rawData.getShort(); 3654f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 3664f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd 3674f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd private boolean err() { 3684f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd return header.status != GifDecoder.STATUS_OK; 3694f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd } 3704f96c1a82e7d2db4863ac63dd00a261e9f0746b1Sam Judd} 371