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