| Index: third_party/gif_player/src/jp/tomorrowkey/android/gifplayer/BaseGifImage.java
|
| diff --git a/third_party/gif_player/src/jp/tomorrowkey/android/gifplayer/BaseGifImage.java b/third_party/gif_player/src/jp/tomorrowkey/android/gifplayer/BaseGifImage.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3c4a1f6d8d4922ae78b82a135934f2e7f9b05e98
|
| --- /dev/null
|
| +++ b/third_party/gif_player/src/jp/tomorrowkey/android/gifplayer/BaseGifImage.java
|
| @@ -0,0 +1,188 @@
|
| +package jp.tomorrowkey.android.gifplayer;
|
| +
|
| +import java.io.ByteArrayInputStream;
|
| +import java.io.IOException;
|
| +import java.io.InputStream;
|
| +import java.nio.ByteBuffer;
|
| +
|
| +/**
|
| + * A base wrapper for GIF image data.
|
| + */
|
| +public class BaseGifImage {
|
| + private final byte[] mData;
|
| + private final int mOffset;
|
| + private int mWidth;
|
| + private int mHeight;
|
| +
|
| + private static final byte[] sColorTableBuffer = new byte[256 * 3];
|
| +
|
| + int mHeaderSize;
|
| + boolean mGlobalColorTableUsed;
|
| + boolean mError;
|
| + int[] mGlobalColorTable = new int[256];
|
| + int mGlobalColorTableSize;
|
| + int mBackgroundColor;
|
| + int mBackgroundIndex;
|
| +
|
| + public BaseGifImage(byte[] data) {
|
| + this(data, 0);
|
| + }
|
| +
|
| + /**
|
| + * Unlike the desktop JVM, ByteBuffers created with allocateDirect() can (and since froyo, do)
|
| + * provide a backing array, enabling zero-copy interop with native code. However, they are
|
| + * aligned on a byte boundary, meaning that they often have an arrayOffset as well - in those
|
| + * cases, we can avoid allocating large byte arrays and a copy.
|
| + */
|
| + public BaseGifImage(ByteBuffer data) {
|
| + this(bufferToArray(data), bufferToOffset(data));
|
| + }
|
| +
|
| + private static int bufferToOffset(ByteBuffer buffer) {
|
| + return buffer.hasArray() ? buffer.arrayOffset() : 0;
|
| + }
|
| +
|
| + private static byte[] bufferToArray(ByteBuffer buffer) {
|
| + if (buffer.hasArray()) {
|
| + return buffer.array();
|
| + } else {
|
| + int position = buffer.position();
|
| + try {
|
| + byte[] newData = new byte[buffer.capacity()];
|
| + buffer.get(newData);
|
| + return newData;
|
| + } finally {
|
| + buffer.position(position);
|
| + }
|
| + }
|
| + }
|
| +
|
| + public BaseGifImage(byte[] data, int offset) {
|
| + mData = data;
|
| + mOffset = offset;
|
| +
|
| + GifHeaderStream stream = new GifHeaderStream(data);
|
| + stream.skip(offset);
|
| + try {
|
| + readHeader(stream);
|
| + mHeaderSize = stream.getPosition();
|
| + } catch (IOException e) {
|
| + mError = true;
|
| + }
|
| +
|
| + try {
|
| + stream.close();
|
| + } catch (IOException e) {
|
| + // Ignore
|
| + }
|
| + }
|
| +
|
| + public byte[] getData() {
|
| + return mData;
|
| + }
|
| +
|
| + public int getDataOffset() {
|
| + return mOffset;
|
| + }
|
| +
|
| + public int getWidth() {
|
| + return mWidth;
|
| + }
|
| +
|
| + public int getHeight() {
|
| + return mHeight;
|
| + }
|
| +
|
| + /**
|
| + * Returns an estimate of the size of the object in bytes.
|
| + */
|
| + public int getSizeEstimate() {
|
| + return mData.length + mGlobalColorTable.length * 4;
|
| + }
|
| +
|
| + /**
|
| + * Reads GIF file header information.
|
| + */
|
| + private void readHeader(InputStream stream) throws IOException {
|
| + boolean valid = stream.read() == 'G';
|
| + valid = valid && stream.read() == 'I';
|
| + valid = valid && stream.read() == 'F';
|
| + if (!valid) {
|
| + mError = true;
|
| + return;
|
| + }
|
| +
|
| + // Skip the next three letter, which represent the variation of the GIF standard.
|
| + stream.skip(3);
|
| +
|
| + readLogicalScreenDescriptor(stream);
|
| +
|
| + if (mGlobalColorTableUsed && !mError) {
|
| + readColorTable(stream, mGlobalColorTable, mGlobalColorTableSize);
|
| + mBackgroundColor = mGlobalColorTable[mBackgroundIndex];
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * Reads Logical Screen Descriptor
|
| + */
|
| + private void readLogicalScreenDescriptor(InputStream stream) throws IOException {
|
| + // logical screen size
|
| + mWidth = readShort(stream);
|
| + mHeight = readShort(stream);
|
| + // packed fields
|
| + int packed = stream.read();
|
| + mGlobalColorTableUsed = (packed & 0x80) != 0; // 1 : global color table flag
|
| + // 2-4 : color resolution - ignore
|
| + // 5 : gct sort flag - ignore
|
| + mGlobalColorTableSize = 2 << (packed & 7); // 6-8 : gct size
|
| + mBackgroundIndex = stream.read();
|
| + stream.skip(1); // pixel aspect ratio - ignore
|
| + }
|
| +
|
| + /**
|
| + * Reads color table as 256 RGB integer values
|
| + *
|
| + * @param ncolors int number of colors to read
|
| + */
|
| + static boolean readColorTable(InputStream stream, int[] colorTable, int ncolors)
|
| + throws IOException {
|
| + synchronized (sColorTableBuffer) {
|
| + int nbytes = 3 * ncolors;
|
| + int n = stream.read(sColorTableBuffer, 0, nbytes);
|
| + if (n < nbytes) {
|
| + return false;
|
| + } else {
|
| + int i = 0;
|
| + int j = 0;
|
| + while (i < ncolors) {
|
| + int r = sColorTableBuffer[j++] & 0xff;
|
| + int g = sColorTableBuffer[j++] & 0xff;
|
| + int b = sColorTableBuffer[j++] & 0xff;
|
| + colorTable[i++] = 0xff000000 | (r << 16) | (g << 8) | b;
|
| + }
|
| + }
|
| + }
|
| +
|
| + return true;
|
| + }
|
| +
|
| + /**
|
| + * Reads next 16-bit value, LSB first
|
| + */
|
| + private int readShort(InputStream stream) throws IOException {
|
| + // read 16-bit value, LSB first
|
| + return stream.read() | (stream.read() << 8);
|
| + }
|
| +
|
| + private final class GifHeaderStream extends ByteArrayInputStream {
|
| +
|
| + private GifHeaderStream(byte[] buf) {
|
| + super(buf);
|
| + }
|
| +
|
| + public int getPosition() {
|
| + return pos;
|
| + }
|
| + }
|
| +}
|
|
|