aboutsummaryrefslogtreecommitdiff
path: root/third_party
diff options
context:
space:
mode:
authorSam Judd <judds@google.com>2014-10-21 19:31:46 -0700
committerSam Judd <judds@google.com>2014-10-21 19:31:46 -0700
commit4fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16 (patch)
tree4b4fca82227416189be4f66af5b9c01d420eb5de /third_party
parent39f12e09c7273b13604184e523846c1ef18ff311 (diff)
downloadglide-4fcb6cd5b3dbd7ecc58d3e3dcb7a3d8304e54a16.tar.gz
Set minimum and default frame delays.
Fixes #205.
Diffstat (limited to 'third_party')
-rw-r--r--third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/GifHeaderParserTest.java66
-rw-r--r--third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtil.java25
-rw-r--r--third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtilTest.java44
-rw-r--r--third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifDecoder.java2
-rw-r--r--third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifHeaderParser.java12
5 files changed, 140 insertions, 9 deletions
diff --git a/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/GifHeaderParserTest.java b/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/GifHeaderParserTest.java
index 0cd0a7af..654b8cef 100644
--- a/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/GifHeaderParserTest.java
+++ b/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/GifHeaderParserTest.java
@@ -79,6 +79,72 @@ public class GifHeaderParserTest {
assertNotNull(header.frames.get(0));
}
+ private static ByteBuffer writeHeaderWithGceAndFrameDelay(short frameDelay) {
+ final int lzwMinCodeSize = 2;
+ ByteBuffer buffer = ByteBuffer.allocate(
+ GifBytesTestUtil.HEADER_LENGTH
+ + GifBytesTestUtil.GRAPHICS_CONTROL_EXTENSION_LENGTH
+ + GifBytesTestUtil.IMAGE_DESCRIPTOR_LENGTH
+ + GifBytesTestUtil.getImageDataSize(lzwMinCodeSize)
+ ).order(ByteOrder.LITTLE_ENDIAN);
+ GifBytesTestUtil.writeHeaderAndLsd(buffer, 1, 1, false, 0);
+ GifBytesTestUtil.writeGraphicsControlExtension(buffer, frameDelay);
+ GifBytesTestUtil.writeImageDescriptor(buffer, 0, 0, 1, 1, false /*hasLct*/, 0);
+ GifBytesTestUtil.writeFakeImageData(buffer, lzwMinCodeSize);
+ return buffer;
+ }
+
+ @Test
+ public void testCanParseFrameDelay() {
+ final short frameDelay = 50;
+ ByteBuffer buffer = writeHeaderWithGceAndFrameDelay(frameDelay);
+
+ parser.setData(buffer.array());
+ GifHeader header = parser.parseHeader();
+ GifFrame frame = header.frames.get(0);
+
+ // Convert delay in 100ths of a second to ms.
+ assertEquals(frameDelay * 10, frame.delay);
+ }
+
+ @Test
+ public void testSetsDefaultFrameDelayIfFrameDelayIsZero() {
+ ByteBuffer buffer = writeHeaderWithGceAndFrameDelay((short) 0);
+
+ parser.setData(buffer.array());
+ GifHeader header = parser.parseHeader();
+ GifFrame frame = header.frames.get(0);
+
+ // Convert delay in 100ths of a second to ms.
+ assertEquals(GifHeaderParser.DEFAULT_FRAME_DELAY * 10, frame.delay);
+ }
+
+ @Test
+ public void testSetsDefaultFrameDelayIfFrameDelayIsLessThanMinimum() {
+ final short frameDelay = GifHeaderParser.MIN_FRAME_DELAY - 1;
+ ByteBuffer buffer = writeHeaderWithGceAndFrameDelay(frameDelay);
+
+ parser.setData(buffer.array());
+ GifHeader header = parser.parseHeader();
+ GifFrame frame = header.frames.get(0);
+
+ // Convert delay in 100ths of a second to ms.
+ assertEquals(GifHeaderParser.DEFAULT_FRAME_DELAY * 10, frame.delay);
+ }
+
+ @Test
+ public void testObeysFrameDelayIfFrameDelayIsAtMinimum() {
+ final short frameDelay = GifHeaderParser.MIN_FRAME_DELAY;
+ ByteBuffer buffer = writeHeaderWithGceAndFrameDelay(frameDelay);
+
+ parser.setData(buffer.array());
+ GifHeader header = parser.parseHeader();
+ GifFrame frame = header.frames.get(0);
+
+ // Convert delay in 100ths of a second to ms.
+ assertEquals(frameDelay * 10, frame.delay);
+ }
+
@Test
public void testSetsFrameLocalColorTableToNullIfNoColorTable() {
final int lzwMinCodeSize = 2;
diff --git a/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtil.java b/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtil.java
index e389deed..b6f3140e 100644
--- a/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtil.java
+++ b/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtil.java
@@ -10,6 +10,8 @@ public class GifBytesTestUtil {
public static final int HEADER_LENGTH = 13;
// Length in bytes.
public static final int IMAGE_DESCRIPTOR_LENGTH = 10;
+ // Length in bytes.
+ public static final int GRAPHICS_CONTROL_EXTENSION_LENGTH = 8;
public static int getColorTableLength(int numColors) {
return 3 * numColors;
@@ -104,6 +106,29 @@ public class GifBytesTestUtil {
out.put((byte) 0);
}
+ public static void writeGraphicsControlExtension(ByteBuffer out, int delayTime) {
+ verifyRemaining(out, GRAPHICS_CONTROL_EXTENSION_LENGTH);
+ verifyShortValues(delayTime);
+
+ // Extension inducer (constant).
+ out.put((byte) 0x21);
+ // Graphic control label (constant).
+ out.put((byte) 0xF9);
+ // Block size (constant).
+ out.put((byte) 0x04);
+ // Packed (disposal method, user input, transparent color flag)
+ out.put((byte) 0x00);
+
+ // Frame delay in 100ths of a second.
+ out.putShort((short) delayTime);
+
+ // Transparent color index.
+ out.put((byte) 0x00);
+
+ // Block terminator (constant).
+ out.put((byte) 0x00);
+ }
+
private static void verifyRemaining(ByteBuffer buffer, int expected) {
if (buffer.remaining() < expected) {
throw new IllegalArgumentException("Must have at least " + expected + " bytes to write");
diff --git a/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtilTest.java b/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtilTest.java
index 88106ea6..b393265f 100644
--- a/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtilTest.java
+++ b/third_party/gif_decoder/src/androidTest/java/com/bumptech/glide/gifdecoder/test/GifBytesTestUtilTest.java
@@ -19,7 +19,7 @@ public class GifBytesTestUtilTest {
byte[] expected = new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x00, 0x08, 0x00, 0x10, 0x20, 0x00, 0x00};
- assertArrayEquals(expected, buffer.array());
+ assertEquals(expected, buffer);
}
@Test
@@ -30,7 +30,7 @@ public class GifBytesTestUtilTest {
byte[] expected = new byte[] { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61, 0x00, 0x08, 0x00, 0x10, (byte) 0xA4, 0x00,
0x00};
- assertArrayEquals(expected, buffer.array());
+ assertEquals(expected, buffer);
}
@Test
@@ -53,7 +53,7 @@ public class GifBytesTestUtilTest {
0x00
};
- assertArrayEquals(expected, buffer.array());
+ assertEquals(expected, buffer);
}
@Test
@@ -81,7 +81,7 @@ public class GifBytesTestUtilTest {
packedField
};
- assertArrayEquals(expected, buffer.array());
+ assertEquals(expected, buffer);
}
@Test
@@ -102,8 +102,7 @@ public class GifBytesTestUtilTest {
};
- assertArrayEquals("expected=" + Arrays.toString(expected) + " received=" + Arrays.toString(buffer.array()),
- expected, buffer.array());
+ assertEquals(expected, buffer);
}
@Test
@@ -113,6 +112,37 @@ public class GifBytesTestUtilTest {
byte[] expected = new byte[] { 0x02, 0x01, 0x01, 0x00 };
- assertArrayEquals(expected, buffer.array());
+ assertEquals(expected, buffer);
+ }
+
+ @Test
+ public void testWritesGraphicsControlExtension() {
+ short delay = 20;
+ ByteBuffer buffer = ByteBuffer.allocate(GifBytesTestUtil.GRAPHICS_CONTROL_EXTENSION_LENGTH);
+ byte[] expected = new byte[] {
+ // Extension inducer.
+ 0x21,
+ // Graphic control label.
+ (byte) 0xF9,
+ // Block size.
+ 0x04,
+ // Packed byte.
+ 0x00,
+ // Frame delay.
+ 0x00,
+ 0x14,
+ // Transparent color index.
+ 0x00,
+ // block terminator.
+ 0x00
+ };
+
+ GifBytesTestUtil.writeGraphicsControlExtension(buffer, delay);
+ assertEquals(expected, buffer);
+ }
+
+ private static void assertEquals(byte[] expected, ByteBuffer buffer) {
+ assertArrayEquals("expected=" + Arrays.toString(expected) + " received=" + Arrays.toString(buffer.array()),
+ expected, buffer.array());
}
}
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 92dc3bfc..ac634ca6 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
@@ -202,7 +202,7 @@ public class GifDecoder {
}
/**
- * Gets display duration for the upcoming frame.
+ * Gets display duration for the upcoming frame in ms.
*/
public int getNextDelay() {
if (header.frameCount <= 0 || framePointer < 0) {
diff --git a/third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifHeaderParser.java b/third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifHeaderParser.java
index 286a5602..126a799c 100644
--- a/third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifHeaderParser.java
+++ b/third_party/gif_decoder/src/main/java/com/bumptech/glide/gifdecoder/GifHeaderParser.java
@@ -16,6 +16,11 @@ import static com.bumptech.glide.gifdecoder.GifDecoder.STATUS_FORMAT_ERROR;
public class GifHeaderParser {
public static final String TAG = "GifHeaderParser";
+ // The minimum frame delay in hundredths of a second.
+ static final int MIN_FRAME_DELAY = 3;
+ // The default frame delay in hundredths of a second for GIFs with frame delays less than the minimum.
+ static final int DEFAULT_FRAME_DELAY = 10;
+
private static final int MAX_BLOCK_SIZE = 256;
// Raw data read working array.
private final byte[] block = new byte[MAX_BLOCK_SIZE];
@@ -147,7 +152,12 @@ public class GifHeaderParser {
}
header.currentFrame.transparency = (packed & 1) != 0;
// Delay in milliseconds.
- header.currentFrame.delay = readShort() * 10;
+ int delayInHundredthsOfASecond = readShort();
+ // TODO: consider allowing -1 to indicate show forever.
+ if (delayInHundredthsOfASecond < MIN_FRAME_DELAY) {
+ delayInHundredthsOfASecond = DEFAULT_FRAME_DELAY;
+ }
+ header.currentFrame.delay = delayInHundredthsOfASecond * 10;
// Transparent color index
header.currentFrame.transIndex = read();
// Block terminator