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; 206a61141137c7a46d747aa611c9caf62436bc119fChris Craikimport java.nio.ByteBuffer; 21a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 22a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikimport java.io.InputStream; 23a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 24a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craikpublic class FrameSequence { 25a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik static { 26a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik System.loadLibrary("framesequence"); 27a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 28a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 29e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private final long mNativeFrameSequence; 30a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final int mWidth; 31a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final int mHeight; 32a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private final boolean mOpaque; 33e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private final int mFrameCount; 34e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private final int mDefaultLoopCount; 35a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 36a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getWidth() { return mWidth; } 37a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public int getHeight() { return mHeight; } 38a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public boolean isOpaque() { return mOpaque; } 39e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public int getFrameCount() { return mFrameCount; } 40e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public int getDefaultLoopCount() { return mDefaultLoopCount; } 41a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 42a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static native FrameSequence nativeDecodeByteArray(byte[] data, int offset, int length); 43a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik private static native FrameSequence nativeDecodeStream(InputStream is, byte[] tempStorage); 446a61141137c7a46d747aa611c9caf62436bc119fChris Craik private static native FrameSequence nativeDecodeByteBuffer(ByteBuffer buffer, int offset, int capacity); 45e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private static native void nativeDestroyFrameSequence(long nativeFrameSequence); 46e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private static native long nativeCreateState(long nativeFrameSequence); 47e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private static native void nativeDestroyState(long nativeState); 48e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private static native long nativeGetFrame(long nativeState, int frameNr, 49a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik Bitmap output, int previousFrameNr); 50a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 51a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @SuppressWarnings("unused") // called by native 52e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private FrameSequence(long nativeFrameSequence, int width, int height, 53e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik boolean opaque, int frameCount, int defaultLoopCount) { 54a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNativeFrameSequence = nativeFrameSequence; 55a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mWidth = width; 56a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mHeight = height; 57a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mOpaque = opaque; 58e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mFrameCount = frameCount; 59e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik mDefaultLoopCount = defaultLoopCount; 60a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 61a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 62a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public static FrameSequence decodeByteArray(byte[] data) { 63a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return decodeByteArray(data, 0, data.length); 64a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 65a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 66a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public static FrameSequence decodeByteArray(byte[] data, int offset, int length) { 67a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (data == null) throw new IllegalArgumentException(); 68a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (offset < 0 || length < 0 || (offset + length > data.length)) { 69a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik throw new IllegalArgumentException("invalid offset/length parameters"); 70a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 71a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return nativeDecodeByteArray(data, offset, length); 72a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 73a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 746a61141137c7a46d747aa611c9caf62436bc119fChris Craik public static FrameSequence decodeByteBuffer(ByteBuffer buffer) { 756a61141137c7a46d747aa611c9caf62436bc119fChris Craik if (buffer == null) throw new IllegalArgumentException(); 766a61141137c7a46d747aa611c9caf62436bc119fChris Craik if (!buffer.isDirect()) { 776a61141137c7a46d747aa611c9caf62436bc119fChris Craik if (buffer.hasArray()) { 786a61141137c7a46d747aa611c9caf62436bc119fChris Craik byte[] byteArray = buffer.array(); 796a61141137c7a46d747aa611c9caf62436bc119fChris Craik return decodeByteArray(byteArray, buffer.position(), buffer.remaining()); 806a61141137c7a46d747aa611c9caf62436bc119fChris Craik } else { 816a61141137c7a46d747aa611c9caf62436bc119fChris Craik throw new IllegalArgumentException("Cannot have non-direct ByteBuffer with no byte array"); 826a61141137c7a46d747aa611c9caf62436bc119fChris Craik } 836a61141137c7a46d747aa611c9caf62436bc119fChris Craik } 846a61141137c7a46d747aa611c9caf62436bc119fChris Craik return nativeDecodeByteBuffer(buffer, buffer.position(), buffer.remaining()); 856a61141137c7a46d747aa611c9caf62436bc119fChris Craik } 866a61141137c7a46d747aa611c9caf62436bc119fChris Craik 87a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public static FrameSequence decodeStream(InputStream stream) { 88a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (stream == null) throw new IllegalArgumentException(); 89a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik byte[] tempStorage = new byte[16 * 1024]; // TODO: use buffer pool 90a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return nativeDecodeStream(stream, tempStorage); 91a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 92a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 93a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik State createState() { 94a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mNativeFrameSequence == 0) { 95a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik throw new IllegalStateException("attempted to use incorrectly built FrameSequence"); 96a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 97a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 98e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik long nativeState = nativeCreateState(mNativeFrameSequence); 99a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (nativeState == 0) { 100a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return null; 101a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 102a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return new State(nativeState); 103a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 104a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 105a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik @Override 106a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik protected void finalize() throws Throwable { 107a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik try { 108a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mNativeFrameSequence != 0) nativeDestroyFrameSequence(mNativeFrameSequence); 109a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } finally { 110a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik super.finalize(); 111a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 112a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 113a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 114a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik /** 115a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * Playback state used when moving frames forward in a frame sequence. 116a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * 117a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * Note that this doesn't require contiguous frames to be rendered, it just stores 118a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * information (in the case of gif, a recall buffer) that will be used to construct 119a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * frames based upon data recorded before previousFrameNr. 120a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * 1214eb541aff092a057b27b917f09d33aba226dffedChris Craik * Note: {@link #destroy()} *must* be called before the object is GC'd to free native resources 122a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * 123a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * Note: State holds a native ref to its FrameSequence instance, so its FrameSequence should 124a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik * remain ref'd while it is in use 125a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik */ 126a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik static class State { 127e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik private long mNativeState; 128a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 129e36c5d675c8c2f900ef186a55edf71ce36ca9fa0Chris Craik public State(long nativeState) { 130a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNativeState = nativeState; 131a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 132a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 1334eb541aff092a057b27b917f09d33aba226dffedChris Craik public void destroy() { 134a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mNativeState != 0) { 135a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik nativeDestroyState(mNativeState); 136a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik mNativeState = 0; 137a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 138a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 139a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik 140a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik // TODO: consider adding alternate API for drawing into a SurfaceTexture 141a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik public long getFrame(int frameNr, Bitmap output, int previousFrameNr) { 142a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (output == null || output.getConfig() != Bitmap.Config.ARGB_8888) { 143a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik throw new IllegalArgumentException("Bitmap passed must be non-null and ARGB_8888"); 144a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 145a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik if (mNativeState == 0) { 1464eb541aff092a057b27b917f09d33aba226dffedChris Craik throw new IllegalStateException("attempted to draw destroyed FrameSequenceState"); 147a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 148a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik return nativeGetFrame(mNativeState, frameNr, output, previousFrameNr); 149a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 150a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik } 151a3ac0a2df64dcfb8b0b01f1cf05e9afd1439e1f4Chris Craik} 152