package pl.droidsonroids.gif; import android.graphics.Bitmap; import android.support.annotation.IntRange; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.io.IOException; /** * GifDecoder allows lightweight access to GIF frames, without wrappers like Drawable or View. * {@link Bitmap} with size equal to or greater than size of the GIF is needed. * For access only metadata (size, number of frames etc.) without pixels see {@link GifAnimationMetaData}. */ public class GifDecoder { //TODO extract common container private final GifInfoHandle mGifInfoHandle; /** * Constructs new GifDecoder. * Equivalent of {@link #GifDecoder(InputSource, GifOptions)} with null {@code options} * @param inputSource source * @throws IOException when creation fails */ public GifDecoder(@NonNull final InputSource inputSource) throws IOException { this(inputSource, null); } /** * Constructs new GifDecoder * * @param inputSource source * @param options null-ok; options controlling subsampling and opacity * @throws IOException when creation fails */ public GifDecoder(@NonNull final InputSource inputSource, @Nullable final GifOptions options) throws IOException { mGifInfoHandle = inputSource.open(); if (options != null) { mGifInfoHandle.setOptions(options.inSampleSize, options.inIsOpaque); } } /** * See {@link GifDrawable#getComment()} * * @return GIF comment */ public String getComment() { return mGifInfoHandle.getComment(); } /** * See {@link GifDrawable#getLoopCount()} * * @return loop count, 0 means that animation is infinite */ public int getLoopCount() { return mGifInfoHandle.getLoopCount(); } /** * See {@link GifDrawable#getInputSourceByteCount()} * * @return number of bytes backed by input source or -1 if it is unknown */ public long getSourceLength() { return mGifInfoHandle.getSourceLength(); } /** * See {@link GifDrawable#seekTo(int)} * * @param position position to seek to in milliseconds * @param buffer the frame buffer * @throws IllegalArgumentException if {@code position < 0 }or {@code buffer} is recycled */ public void seekToTime(@IntRange(from = 0, to = Integer.MAX_VALUE) final int position, @NonNull final Bitmap buffer) { checkBuffer(buffer); mGifInfoHandle.seekToTime(position, buffer); } /** * See {@link GifDrawable#seekToFrame(int)} * * @param frameIndex position to seek to in milliseconds * @param buffer the frame buffer * @throws IllegalArgumentException if {@code frameIndex < 0} or {@code buffer} is recycled */ public void seekToFrame(@IntRange(from = 0, to = Integer.MAX_VALUE) final int frameIndex, @NonNull final Bitmap buffer) { checkBuffer(buffer); mGifInfoHandle.seekToFrame(frameIndex, buffer); } /** * See {@link GifDrawable#getAllocationByteCount()} * * @return possible size of the memory needed to store pixels of this object */ public long getAllocationByteCount() { return mGifInfoHandle.getAllocationByteCount(); } /** * See {@link GifDrawable#getFrameDuration(int)} * * @param index index of the frame * @return duration of the given frame in milliseconds * @throws IndexOutOfBoundsException if {@code index < 0 || index >= <number of frames>} */ public int getFrameDuration(@IntRange(from = 0) int index) { return mGifInfoHandle.getFrameDuration(index); } /** * See {@link GifDrawable#getDuration()} * * @return duration of of one loop the animation in milliseconds. Result is always multiple of 10. */ public int getDuration() { return mGifInfoHandle.getDuration(); } /** * @return width od the GIF canvas in pixels */ public int getWidth() { return mGifInfoHandle.getWidth(); } /** * @return height od the GIF canvas in pixels */ public int getHeight() { return mGifInfoHandle.getHeight(); } /** * @return number of frames in GIF, at least one */ public int getNumberOfFrames() { return mGifInfoHandle.getNumberOfFrames(); } /** * @return true if GIF is animated (has at least 2 frames and positive duration), false otherwise */ public boolean isAnimated() { return mGifInfoHandle.getNumberOfFrames() > 1 && getDuration() > 0; } /** * See {@link GifDrawable#recycle()} */ public void recycle() { mGifInfoHandle.recycle(); } private void checkBuffer(final Bitmap buffer) { if (buffer.isRecycled()) { throw new IllegalArgumentException("Bitmap is recycled"); } if (buffer.getWidth() < mGifInfoHandle.getWidth() || buffer.getHeight() < mGifInfoHandle.getHeight()) { throw new IllegalArgumentException("Bitmap ia too small, size must be greater than or equal to GIF size"); } } }