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