1package com.bumptech.glide.load.resource.gif; 2 3import android.content.Context; 4import android.graphics.Bitmap; 5import android.util.Log; 6 7import com.bumptech.glide.Glide; 8import com.bumptech.glide.gifdecoder.GifDecoder; 9import com.bumptech.glide.gifdecoder.GifHeader; 10import com.bumptech.glide.gifdecoder.GifHeaderParser; 11import com.bumptech.glide.load.ResourceDecoder; 12import com.bumptech.glide.load.Transformation; 13import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool; 14import com.bumptech.glide.load.resource.UnitTransformation; 15import com.bumptech.glide.util.Util; 16 17import java.io.ByteArrayOutputStream; 18import java.io.IOException; 19import java.io.InputStream; 20import java.util.Queue; 21 22/** 23 * An {@link com.bumptech.glide.load.ResourceDecoder} that decodes 24 * {@link com.bumptech.glide.load.resource.gif.GifDrawable} from {@link java.io.InputStream} data. 25 */ 26public class GifResourceDecoder implements ResourceDecoder<InputStream, GifDrawable> { 27 private static final String TAG = "GifResourceDecoder"; 28 private static final GifHeaderParserPool PARSER_POOL = new GifHeaderParserPool(); 29 private static final GifDecoderPool DECODER_POOL = new GifDecoderPool(); 30 31 private final Context context; 32 private final GifHeaderParserPool parserPool; 33 private final BitmapPool bitmapPool; 34 private final GifDecoderPool decoderPool; 35 private final GifBitmapProvider provider; 36 37 public GifResourceDecoder(Context context) { 38 this(context, Glide.get(context).getBitmapPool()); 39 } 40 41 public GifResourceDecoder(Context context, BitmapPool bitmapPool) { 42 this(context, bitmapPool, PARSER_POOL, DECODER_POOL); 43 } 44 45 // Visible for testing. 46 GifResourceDecoder(Context context, BitmapPool bitmapPool, GifHeaderParserPool parserPool, 47 GifDecoderPool decoderPool) { 48 this.context = context; 49 this.bitmapPool = bitmapPool; 50 this.decoderPool = decoderPool; 51 this.provider = new GifBitmapProvider(bitmapPool); 52 this.parserPool = parserPool; 53 } 54 55 @Override 56 public GifDrawableResource decode(InputStream source, int width, int height) { 57 byte[] data = inputStreamToBytes(source); 58 final GifHeaderParser parser = parserPool.obtain(data); 59 final GifDecoder decoder = decoderPool.obtain(provider); 60 try { 61 return decode(data, width, height, parser, decoder); 62 } finally { 63 parserPool.release(parser); 64 decoderPool.release(decoder); 65 } 66 } 67 68 private GifDrawableResource decode(byte[] data, int width, int height, GifHeaderParser parser, GifDecoder decoder) { 69 final GifHeader header = parser.parseHeader(); 70 if (header.getNumFrames() <= 0 || header.getStatus() != GifDecoder.STATUS_OK) { 71 // If we couldn't decode the GIF, we will end up with a frame count of 0. 72 return null; 73 } 74 75 Bitmap firstFrame = decodeFirstFrame(decoder, header, data); 76 if (firstFrame == null) { 77 return null; 78 } 79 80 Transformation<Bitmap> unitTransformation = UnitTransformation.get(); 81 82 GifDrawable gifDrawable = new GifDrawable(context, provider, bitmapPool, unitTransformation, width, height, 83 header, data, firstFrame); 84 85 return new GifDrawableResource(gifDrawable); 86 } 87 88 private Bitmap decodeFirstFrame(GifDecoder decoder, GifHeader header, byte[] data) { 89 decoder.setData(header, data); 90 decoder.advance(); 91 return decoder.getNextFrame(); 92 } 93 94 @Override 95 public String getId() { 96 return ""; 97 } 98 99 private static byte[] inputStreamToBytes(InputStream is) { 100 final int bufferSize = 16384; 101 ByteArrayOutputStream buffer = new ByteArrayOutputStream(bufferSize); 102 try { 103 int nRead; 104 byte[] data = new byte[bufferSize]; 105 while ((nRead = is.read(data)) != -1) { 106 buffer.write(data, 0, nRead); 107 } 108 buffer.flush(); 109 } catch (IOException e) { 110 Log.w(TAG, "Error reading data from stream", e); 111 } 112 //TODO the returned byte[] may be partial if an IOException was thrown from read 113 return buffer.toByteArray(); 114 } 115 116 // Visible for testing. 117 static class GifDecoderPool { 118 private final Queue<GifDecoder> pool = Util.createQueue(0); 119 120 public synchronized GifDecoder obtain(GifDecoder.BitmapProvider bitmapProvider) { 121 GifDecoder result = pool.poll(); 122 if (result == null) { 123 result = new GifDecoder(bitmapProvider); 124 } 125 return result; 126 } 127 128 public synchronized void release(GifDecoder decoder) { 129 decoder.clear(); 130 pool.offer(decoder); 131 } 132 } 133 134 // Visible for testing. 135 static class GifHeaderParserPool { 136 private final Queue<GifHeaderParser> pool = Util.createQueue(0); 137 138 public synchronized GifHeaderParser obtain(byte[] data) { 139 GifHeaderParser result = pool.poll(); 140 if (result == null) { 141 result = new GifHeaderParser(); 142 } 143 return result.setData(data); 144 } 145 146 public synchronized void release(GifHeaderParser parser) { 147 parser.clear(); 148 pool.offer(parser); 149 } 150 } 151} 152