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