1 /* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.support.rastermill; 18 19 import android.graphics.Bitmap; 20 21 import java.io.InputStream; 22 23 public class FrameSequence { 24 static { 25 System.loadLibrary("framesequence"); 26 } 27 28 private final long mNativeFrameSequence; 29 private final int mWidth; 30 private final int mHeight; 31 private final boolean mOpaque; 32 private final int mFrameCount; 33 private final int mDefaultLoopCount; 34 getWidth()35 public int getWidth() { return mWidth; } getHeight()36 public int getHeight() { return mHeight; } isOpaque()37 public boolean isOpaque() { return mOpaque; } getFrameCount()38 public int getFrameCount() { return mFrameCount; } getDefaultLoopCount()39 public int getDefaultLoopCount() { return mDefaultLoopCount; } 40 nativeDecodeByteArray(byte[] data, int offset, int length)41 private static native FrameSequence nativeDecodeByteArray(byte[] data, int offset, int length); nativeDecodeStream(InputStream is, byte[] tempStorage)42 private static native FrameSequence nativeDecodeStream(InputStream is, byte[] tempStorage); nativeDestroyFrameSequence(long nativeFrameSequence)43 private static native void nativeDestroyFrameSequence(long nativeFrameSequence); nativeCreateState(long nativeFrameSequence)44 private static native long nativeCreateState(long nativeFrameSequence); nativeDestroyState(long nativeState)45 private static native void nativeDestroyState(long nativeState); nativeGetFrame(long nativeState, int frameNr, Bitmap output, int previousFrameNr)46 private static native long nativeGetFrame(long nativeState, int frameNr, 47 Bitmap output, int previousFrameNr); 48 49 @SuppressWarnings("unused") // called by native FrameSequence(long nativeFrameSequence, int width, int height, boolean opaque, int frameCount, int defaultLoopCount)50 private FrameSequence(long nativeFrameSequence, int width, int height, 51 boolean opaque, int frameCount, int defaultLoopCount) { 52 mNativeFrameSequence = nativeFrameSequence; 53 mWidth = width; 54 mHeight = height; 55 mOpaque = opaque; 56 mFrameCount = frameCount; 57 mDefaultLoopCount = defaultLoopCount; 58 } 59 decodeByteArray(byte[] data)60 public static FrameSequence decodeByteArray(byte[] data) { 61 return decodeByteArray(data, 0, data.length); 62 } 63 decodeByteArray(byte[] data, int offset, int length)64 public static FrameSequence decodeByteArray(byte[] data, int offset, int length) { 65 if (data == null) throw new IllegalArgumentException(); 66 if (offset < 0 || length < 0 || (offset + length > data.length)) { 67 throw new IllegalArgumentException("invalid offset/length parameters"); 68 } 69 return nativeDecodeByteArray(data, offset, length); 70 } 71 decodeStream(InputStream stream)72 public static FrameSequence decodeStream(InputStream stream) { 73 if (stream == null) throw new IllegalArgumentException(); 74 byte[] tempStorage = new byte[16 * 1024]; // TODO: use buffer pool 75 return nativeDecodeStream(stream, tempStorage); 76 } 77 createState()78 State createState() { 79 if (mNativeFrameSequence == 0) { 80 throw new IllegalStateException("attempted to use incorrectly built FrameSequence"); 81 } 82 83 long nativeState = nativeCreateState(mNativeFrameSequence); 84 if (nativeState == 0) { 85 return null; 86 } 87 return new State(nativeState); 88 } 89 90 @Override finalize()91 protected void finalize() throws Throwable { 92 try { 93 if (mNativeFrameSequence != 0) nativeDestroyFrameSequence(mNativeFrameSequence); 94 } finally { 95 super.finalize(); 96 } 97 } 98 99 /** 100 * Playback state used when moving frames forward in a frame sequence. 101 * 102 * Note that this doesn't require contiguous frames to be rendered, it just stores 103 * information (in the case of gif, a recall buffer) that will be used to construct 104 * frames based upon data recorded before previousFrameNr. 105 * 106 * Note: {@link #destroy()} *must* be called before the object is GC'd to free native resources 107 * 108 * Note: State holds a native ref to its FrameSequence instance, so its FrameSequence should 109 * remain ref'd while it is in use 110 */ 111 static class State { 112 private long mNativeState; 113 State(long nativeState)114 public State(long nativeState) { 115 mNativeState = nativeState; 116 } 117 destroy()118 public void destroy() { 119 if (mNativeState != 0) { 120 nativeDestroyState(mNativeState); 121 mNativeState = 0; 122 } 123 } 124 125 // TODO: consider adding alternate API for drawing into a SurfaceTexture getFrame(int frameNr, Bitmap output, int previousFrameNr)126 public long getFrame(int frameNr, Bitmap output, int previousFrameNr) { 127 if (output == null || output.getConfig() != Bitmap.Config.ARGB_8888) { 128 throw new IllegalArgumentException("Bitmap passed must be non-null and ARGB_8888"); 129 } 130 if (mNativeState == 0) { 131 throw new IllegalStateException("attempted to draw destroyed FrameSequenceState"); 132 } 133 return nativeGetFrame(mNativeState, frameNr, output, previousFrameNr); 134 } 135 } 136 } 137