aboutsummaryrefslogtreecommitdiff
path: root/third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifDecoder.java
diff options
context:
space:
mode:
authorSam Judd <judds@google.com>2014-10-19 13:13:28 -0700
committerSam Judd <judds@google.com>2014-10-19 13:30:55 -0700
commit855776275d8ca409f968c8fceff4d11f51bf8592 (patch)
treec715c2b06dd4fa84f37e76ae34beafaded9c42c9 /third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifDecoder.java
parent2c259f532bee14a4f3f6be419bcfb58ef5e22ff5 (diff)
downloadglide-855776275d8ca409f968c8fceff4d11f51bf8592.tar.gz
Decode GIFs with more codes than can fit in table.
Fixes #203
Diffstat (limited to 'third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifDecoder.java')
-rw-r--r--third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifDecoder.java124
1 files changed, 79 insertions, 45 deletions
diff --git a/third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifDecoder.java b/third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifDecoder.java
index e1f05455..4023eb14 100644
--- a/third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifDecoder.java
+++ b/third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifDecoder.java
@@ -69,6 +69,10 @@ public class GifDecoder {
*/
public static final int STATUS_OPEN_ERROR = 2;
/**
+ * Unable to fully decode the current frame.
+ */
+ public static final int STATUS_PARTIAL_DECODE = 3;
+ /**
* max decoder pixel stack size.
*/
private static final int MAX_STACK_SIZE = 4096;
@@ -90,6 +94,8 @@ public class GifDecoder {
*/
private static final int DISPOSAL_PREVIOUS = 3;
+ private static final int NULL_CODE = -1;
+
// Global File Header values and parsing flags.
// Active color table.
private int[] act;
@@ -115,6 +121,7 @@ public class GifDecoder {
private Bitmap previousImage;
private boolean savePrevious;
private Bitmap.Config config;
+ private int status = STATUS_OK;
/**
* An interface that can be used to provide reused {@link android.graphics.Bitmap}s to avoid GCs from constantly
@@ -154,6 +161,18 @@ public class GifDecoder {
}
/**
+ * Returns the current status of the decoder.
+ *
+ * <p>
+ * Status will update per frame to allow the caller to tell whether or not the current frame was decoded
+ * successfully and/or completely. Format and open failures persist across frames.
+ * </p>
+ */
+ public int getStatus() {
+ return status;
+ }
+
+ /**
* Move the animation frame counter forward.
*/
public void advance() {
@@ -223,8 +242,12 @@ public class GifDecoder {
*/
public Bitmap getNextFrame() {
if (header.frameCount <= 0 || framePointer < 0) {
+ status = STATUS_FORMAT_ERROR;
+ }
+ if (status == STATUS_FORMAT_ERROR || status == STATUS_OPEN_ERROR) {
return null;
}
+ status = STATUS_OK;
GifFrame frame = header.frames.get(framePointer);
@@ -247,7 +270,7 @@ public class GifDecoder {
if (act == null) {
Log.w(TAG, "No Valid Color Table");
// No color table defined.
- header.status = STATUS_FORMAT_ERROR;
+ status = STATUS_FORMAT_ERROR;
return null;
}
@@ -285,7 +308,7 @@ public class GifDecoder {
Log.w(TAG, "Error reading data from stream", e);
}
} else {
- header.status = STATUS_OPEN_ERROR;
+ status = STATUS_OPEN_ERROR;
}
try {
@@ -296,7 +319,7 @@ public class GifDecoder {
Log.w(TAG, "Error closing stream", e);
}
- return header.status;
+ return status;
}
public void clear() {
@@ -315,6 +338,7 @@ public class GifDecoder {
this.id = id;
this.header = header;
this.data = data;
+ this.status = STATUS_OK;
// Initialize the raw data buffer.
rawData = ByteBuffer.wrap(data);
rawData.rewind();
@@ -364,7 +388,7 @@ public class GifDecoder {
}
}
- return header.status;
+ return status;
}
/**
@@ -479,7 +503,6 @@ public class GifDecoder {
rawData.position(frame.bufferFrameStart);
}
- int nullCode = -1;
int npix = (frame == null) ? header.width * header.height : frame.iw * frame.ih;
int available, clear, codeMask, codeSize, endOfInformation, inCode, oldCode, bits, code, count, i, datum,
dataSize, first, top, bi, pi;
@@ -503,7 +526,7 @@ public class GifDecoder {
clear = 1 << dataSize;
endOfInformation = clear + 1;
available = clear + 2;
- oldCode = nullCode;
+ oldCode = NULL_CODE;
codeSize = dataSize + 1;
codeMask = (1 << codeSize) - 1;
for (code = 0; code < clear; code++) {
@@ -515,73 +538,84 @@ public class GifDecoder {
// Decode GIF pixel stream.
datum = bits = count = first = top = pi = bi = 0;
for (i = 0; i < npix; ) {
- if (top == 0) {
- if (bits < codeSize) {
- // Load bytes until there are enough bits for a code.
- if (count == 0) {
- // Read a new data block.
- count = readBlock();
- if (count <= 0) {
- break;
- }
- bi = 0;
- }
- datum += (((int) block[bi]) & 0xff) << bits;
- bits += 8;
- bi++;
- count--;
- continue;
+ // Load bytes until there are enough bits for a code.
+ if (count == 0) {
+ // Read a new data block.
+ count = readBlock();
+ if (count <= 0) {
+ status = STATUS_PARTIAL_DECODE;
+ break;
}
+ bi = 0;
+ }
+
+ datum += (((int) block[bi]) & 0xff) << bits;
+ bits += 8;
+ bi++;
+ count--;
+
+ while (bits >= codeSize) {
// Get the next code.
code = datum & codeMask;
datum >>= codeSize;
bits -= codeSize;
+
// Interpret the code.
- if ((code > available) || (code == endOfInformation)) {
- break;
- }
if (code == clear) {
// Reset decoder.
codeSize = dataSize + 1;
codeMask = (1 << codeSize) - 1;
available = clear + 2;
- oldCode = nullCode;
+ oldCode = NULL_CODE;
continue;
}
- if (oldCode == nullCode) {
+
+ if (code > available) {
+ status = STATUS_PARTIAL_DECODE;
+ break;
+ }
+
+ if (code == endOfInformation) {
+ break;
+ }
+
+ if (oldCode == NULL_CODE) {
pixelStack[top++] = suffix[code];
oldCode = code;
first = code;
continue;
}
inCode = code;
- if (code == available) {
+ if (code >= available) {
pixelStack[top++] = (byte) first;
code = oldCode;
}
- while (code > clear) {
+ while (code >= clear) {
pixelStack[top++] = suffix[code];
code = prefix[code];
}
first = ((int) suffix[code]) & 0xff;
- // Add a new string to the string table.
- if (available >= MAX_STACK_SIZE) {
- break;
- }
pixelStack[top++] = (byte) first;
- prefix[available] = (short) oldCode;
- suffix[available] = (byte) first;
- available++;
- if (((available & codeMask) == 0) && (available < MAX_STACK_SIZE)) {
- codeSize++;
- codeMask += available;
+
+ // Add a new string to the string table.
+ if (available < MAX_STACK_SIZE) {
+ prefix[available] = (short) oldCode;
+ suffix[available] = (byte) first;
+ available++;
+ if (((available & codeMask) == 0) && (available < MAX_STACK_SIZE)) {
+ codeSize++;
+ codeMask += available;
+ }
}
oldCode = inCode;
+
+ while (top > 0) {
+ // Pop a pixel off the pixel stack.
+ top--;
+ mainPixels[pi++] = pixelStack[top];
+ i++;
+ }
}
- // Pop a pixel off the pixel stack.
- top--;
- mainPixels[pi++] = pixelStack[top];
- i++;
}
// Clear missing pixels.
@@ -598,7 +632,7 @@ public class GifDecoder {
try {
curByte = rawData.get() & 0xFF;
} catch (Exception e) {
- header.status = STATUS_FORMAT_ERROR;
+ status = STATUS_FORMAT_ERROR;
}
return curByte;
}
@@ -622,7 +656,7 @@ public class GifDecoder {
}
} catch (Exception e) {
Log.w(TAG, "Error Reading Block", e);
- header.status = STATUS_FORMAT_ERROR;
+ status = STATUS_FORMAT_ERROR;
}
}
return n;