Home | History | Annotate | Download | only in gif
      1 package com.bumptech.glide.load.resource.gif;
      2 
      3 import android.graphics.Bitmap;
      4 import android.util.Log;
      5 
      6 import com.bumptech.glide.gifdecoder.GifDecoder;
      7 import com.bumptech.glide.gifdecoder.GifHeader;
      8 import com.bumptech.glide.gifdecoder.GifHeaderParser;
      9 import com.bumptech.glide.gifencoder.AnimatedGifEncoder;
     10 import com.bumptech.glide.load.ResourceEncoder;
     11 import com.bumptech.glide.load.Transformation;
     12 import com.bumptech.glide.load.engine.Resource;
     13 import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool;
     14 import com.bumptech.glide.load.resource.UnitTransformation;
     15 import com.bumptech.glide.load.resource.bitmap.BitmapResource;
     16 import com.bumptech.glide.util.LogTime;
     17 
     18 import java.io.IOException;
     19 import java.io.OutputStream;
     20 
     21 /**
     22  * An {@link com.bumptech.glide.load.ResourceEncoder} that can write
     23  * {@link com.bumptech.glide.load.resource.gif.GifDrawable} to cache.
     24  */
     25 public class GifResourceEncoder implements ResourceEncoder<GifDrawable> {
     26     private static final Factory FACTORY = new Factory();
     27     private static final String TAG = "GifEncoder";
     28     private final GifDecoder.BitmapProvider provider;
     29     private final BitmapPool bitmapPool;
     30     private final Factory factory;
     31 
     32     public GifResourceEncoder(BitmapPool bitmapPool) {
     33         this(bitmapPool, FACTORY);
     34     }
     35 
     36     // Visible for testing.
     37     GifResourceEncoder(BitmapPool bitmapPool, Factory factory) {
     38         this.bitmapPool = bitmapPool;
     39         provider = new GifBitmapProvider(bitmapPool);
     40         this.factory = factory;
     41     }
     42 
     43     @Override
     44     public boolean encode(Resource<GifDrawable> resource, OutputStream os) {
     45         long startTime = LogTime.getLogTime();
     46 
     47         GifDrawable drawable = resource.get();
     48         Transformation<Bitmap> transformation = drawable.getFrameTransformation();
     49         if (transformation instanceof UnitTransformation) {
     50             return writeDataDirect(drawable.getData(), os);
     51         }
     52 
     53         GifDecoder decoder = decodeHeaders(drawable.getData());
     54 
     55         AnimatedGifEncoder encoder = factory.buildEncoder();
     56         if (!encoder.start(os)) {
     57             return false;
     58         }
     59 
     60         for (int i = 0; i < decoder.getFrameCount(); i++) {
     61             Bitmap currentFrame = decoder.getNextFrame();
     62             Resource<Bitmap> transformedResource = getTransformedFrame(currentFrame, transformation, drawable);
     63             try {
     64                 if (!encoder.addFrame(transformedResource.get())) {
     65                     return false;
     66                 }
     67                 int currentFrameIndex = decoder.getCurrentFrameIndex();
     68                 int delay = decoder.getDelay(currentFrameIndex);
     69                 encoder.setDelay(delay);
     70 
     71                 decoder.advance();
     72             } finally {
     73                 transformedResource.recycle();
     74             }
     75         }
     76 
     77         boolean result = encoder.finish();
     78 
     79         if (Log.isLoggable(TAG, Log.VERBOSE)) {
     80             Log.v(TAG, "Encoded gif with " + decoder.getFrameCount() + " frames and " + drawable.getData().length
     81                     + " bytes in " + LogTime.getElapsedMillis(startTime) + " ms");
     82         }
     83 
     84         return result;
     85     }
     86 
     87     private boolean writeDataDirect(byte[] data, OutputStream os) {
     88         boolean success = true;
     89         try {
     90             os.write(data);
     91         } catch (IOException e) {
     92             if (Log.isLoggable(TAG, Log.DEBUG)) {
     93                 Log.d(TAG, "Failed to write data to output stream in GifResourceEncoder", e);
     94             }
     95             success = false;
     96         }
     97         return success;
     98     }
     99 
    100     private GifDecoder decodeHeaders(byte[] data) {
    101         GifHeaderParser parser = factory.buildParser();
    102         parser.setData(data);
    103         GifHeader header = parser.parseHeader();
    104 
    105         GifDecoder decoder = factory.buildDecoder(provider);
    106         decoder.setData(header, data);
    107         decoder.advance();
    108 
    109         return decoder;
    110     }
    111 
    112     private Resource<Bitmap> getTransformedFrame(Bitmap currentFrame, Transformation<Bitmap> transformation,
    113             GifDrawable drawable) {
    114         // TODO: what if current frame is null?
    115         Resource<Bitmap> bitmapResource = factory.buildFrameResource(currentFrame, bitmapPool);
    116         Resource<Bitmap> transformedResource = transformation.transform(bitmapResource,
    117                 drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
    118         if (!bitmapResource.equals(transformedResource)) {
    119             bitmapResource.recycle();
    120         }
    121         return transformedResource;
    122     }
    123 
    124     @Override
    125     public String getId() {
    126         return "";
    127     }
    128 
    129     // Visible for testing.
    130     static class Factory {
    131 
    132         public GifDecoder buildDecoder(GifDecoder.BitmapProvider bitmapProvider) {
    133             return new GifDecoder(bitmapProvider);
    134         }
    135 
    136         public GifHeaderParser buildParser() {
    137             return new GifHeaderParser();
    138         }
    139 
    140         public AnimatedGifEncoder buildEncoder() {
    141             return new AnimatedGifEncoder();
    142         }
    143 
    144         public Resource<Bitmap> buildFrameResource(Bitmap bitmap, BitmapPool bitmapPool) {
    145             return new BitmapResource(bitmap, bitmapPool);
    146         }
    147     }
    148 }
    149