1/*
2 * Copyright (C) 2011 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
18package android.filterfw.core;
19
20import android.filterfw.core.Frame;
21import android.filterfw.core.FrameFormat;
22import android.filterfw.core.FrameManager;
23import android.filterfw.core.NativeFrame;
24import android.filterfw.core.StopWatchMap;
25import android.graphics.Bitmap;
26import android.opengl.GLES20;
27import android.graphics.Rect;
28
29import java.nio.ByteBuffer;
30
31class GLFrameTimer {
32
33    private static StopWatchMap mTimer = null;
34
35    public static StopWatchMap get() {
36        if (mTimer == null) {
37            mTimer = new StopWatchMap();
38        }
39        return mTimer;
40    }
41
42}
43
44/**
45 * @hide
46 */
47public class GLFrame extends Frame {
48
49    // GL-related binding types
50    public final static int EXISTING_TEXTURE_BINDING = 100;
51    public final static int EXISTING_FBO_BINDING     = 101;
52    public final static int NEW_TEXTURE_BINDING      = 102; // TODO: REMOVE THIS
53    public final static int NEW_FBO_BINDING          = 103; // TODO: REMOVE THIS
54    public final static int EXTERNAL_TEXTURE         = 104;
55
56    private int glFrameId = -1;
57
58    /**
59     * Flag whether we own the texture or not. If we do not, we must be careful when caching or
60     * storing the frame, as the user may delete, and regenerate it.
61     */
62    private boolean mOwnsTexture = true;
63
64    /**
65     * Keep a reference to the GL environment, so that it does not get deallocated while there
66     * are still frames living in it.
67     */
68    private GLEnvironment mGLEnvironment;
69
70    GLFrame(FrameFormat format, FrameManager frameManager) {
71        super(format, frameManager);
72    }
73
74    GLFrame(FrameFormat format, FrameManager frameManager, int bindingType, long bindingId) {
75        super(format, frameManager, bindingType, bindingId);
76    }
77
78    void init(GLEnvironment glEnv) {
79        FrameFormat format = getFormat();
80        mGLEnvironment = glEnv;
81
82        // Check that we have a valid format
83        if (format.getBytesPerSample() != 4) {
84            throw new IllegalArgumentException("GL frames must have 4 bytes per sample!");
85        } else if (format.getDimensionCount() != 2) {
86            throw new IllegalArgumentException("GL frames must be 2-dimensional!");
87        } else if (getFormat().getSize() < 0) {
88            throw new IllegalArgumentException("Initializing GL frame with zero size!");
89        }
90
91        // Create correct frame
92        int bindingType = getBindingType();
93        boolean reusable = true;
94        if (bindingType == Frame.NO_BINDING) {
95            initNew(false);
96        } else if (bindingType == EXTERNAL_TEXTURE) {
97            initNew(true);
98            reusable = false;
99        } else if (bindingType == EXISTING_TEXTURE_BINDING) {
100            initWithTexture((int)getBindingId());
101        } else if (bindingType == EXISTING_FBO_BINDING) {
102            initWithFbo((int)getBindingId());
103        } else if (bindingType == NEW_TEXTURE_BINDING) {
104            initWithTexture((int)getBindingId());
105        } else if (bindingType == NEW_FBO_BINDING) {
106            initWithFbo((int)getBindingId());
107        } else {
108            throw new RuntimeException("Attempting to create GL frame with unknown binding type "
109                + bindingType + "!");
110        }
111        setReusable(reusable);
112    }
113
114    private void initNew(boolean isExternal) {
115        if (isExternal) {
116            if (!nativeAllocateExternal(mGLEnvironment)) {
117                throw new RuntimeException("Could not allocate external GL frame!");
118            }
119        } else {
120            if (!nativeAllocate(mGLEnvironment, getFormat().getWidth(), getFormat().getHeight())) {
121                throw new RuntimeException("Could not allocate GL frame!");
122            }
123        }
124    }
125
126    private void initWithTexture(int texId) {
127        int width = getFormat().getWidth();
128        int height = getFormat().getHeight();
129        if (!nativeAllocateWithTexture(mGLEnvironment, texId, width, height)) {
130            throw new RuntimeException("Could not allocate texture backed GL frame!");
131        }
132        mOwnsTexture = false;
133        markReadOnly();
134    }
135
136    private void initWithFbo(int fboId) {
137        int width = getFormat().getWidth();
138        int height = getFormat().getHeight();
139        if (!nativeAllocateWithFbo(mGLEnvironment, fboId, width, height)) {
140            throw new RuntimeException("Could not allocate FBO backed GL frame!");
141        }
142    }
143
144    void flushGPU(String message) {
145        StopWatchMap timer = GLFrameTimer.get();
146        if (timer.LOG_MFF_RUNNING_TIMES) {
147          timer.start("glFinish " + message);
148          GLES20.glFinish();
149          timer.stop("glFinish " + message);
150        }
151    }
152
153    @Override
154    protected synchronized boolean hasNativeAllocation() {
155        return glFrameId != -1;
156    }
157
158    @Override
159    protected synchronized void releaseNativeAllocation() {
160        nativeDeallocate();
161        glFrameId = -1;
162    }
163
164    public GLEnvironment getGLEnvironment() {
165        return mGLEnvironment;
166    }
167
168    @Override
169    public Object getObjectValue() {
170        assertGLEnvValid();
171        return ByteBuffer.wrap(getNativeData());
172    }
173
174    @Override
175    public void setInts(int[] ints) {
176        assertFrameMutable();
177        assertGLEnvValid();
178        if (!setNativeInts(ints)) {
179            throw new RuntimeException("Could not set int values for GL frame!");
180        }
181    }
182
183    @Override
184    public int[] getInts() {
185        assertGLEnvValid();
186        flushGPU("getInts");
187        return getNativeInts();
188    }
189
190    @Override
191    public void setFloats(float[] floats) {
192        assertFrameMutable();
193        assertGLEnvValid();
194        if (!setNativeFloats(floats)) {
195            throw new RuntimeException("Could not set int values for GL frame!");
196        }
197    }
198
199    @Override
200    public float[] getFloats() {
201        assertGLEnvValid();
202        flushGPU("getFloats");
203        return getNativeFloats();
204    }
205
206    @Override
207    public void setData(ByteBuffer buffer, int offset, int length) {
208        assertFrameMutable();
209        assertGLEnvValid();
210        byte[] bytes = buffer.array();
211        if (getFormat().getSize() != bytes.length) {
212            throw new RuntimeException("Data size in setData does not match GL frame size!");
213        } else if (!setNativeData(bytes, offset, length)) {
214            throw new RuntimeException("Could not set GL frame data!");
215        }
216    }
217
218    @Override
219    public ByteBuffer getData() {
220        assertGLEnvValid();
221        flushGPU("getData");
222        return ByteBuffer.wrap(getNativeData());
223    }
224
225    @Override
226    public void setBitmap(Bitmap bitmap) {
227        assertFrameMutable();
228        assertGLEnvValid();
229        if (getFormat().getWidth()  != bitmap.getWidth() ||
230            getFormat().getHeight() != bitmap.getHeight()) {
231            throw new RuntimeException("Bitmap dimensions do not match GL frame dimensions!");
232        } else {
233            Bitmap rgbaBitmap = convertBitmapToRGBA(bitmap);
234            if (!setNativeBitmap(rgbaBitmap, rgbaBitmap.getByteCount())) {
235                throw new RuntimeException("Could not set GL frame bitmap data!");
236            }
237        }
238    }
239
240    @Override
241    public Bitmap getBitmap() {
242        assertGLEnvValid();
243        flushGPU("getBitmap");
244        Bitmap result = Bitmap.createBitmap(getFormat().getWidth(),
245                                            getFormat().getHeight(),
246                                            Bitmap.Config.ARGB_8888);
247        if (!getNativeBitmap(result)) {
248            throw new RuntimeException("Could not get bitmap data from GL frame!");
249        }
250        return result;
251    }
252
253    @Override
254    public void setDataFromFrame(Frame frame) {
255        assertGLEnvValid();
256
257        // Make sure frame fits
258        if (getFormat().getSize() < frame.getFormat().getSize()) {
259            throw new RuntimeException(
260                "Attempting to assign frame of size " + frame.getFormat().getSize() + " to " +
261                "smaller GL frame of size " + getFormat().getSize() + "!");
262        }
263
264        // Invoke optimized implementations if possible
265        if (frame instanceof NativeFrame) {
266            nativeCopyFromNative((NativeFrame)frame);
267        } else if (frame instanceof GLFrame) {
268            nativeCopyFromGL((GLFrame)frame);
269        } else if (frame instanceof SimpleFrame) {
270            setObjectValue(frame.getObjectValue());
271        } else {
272            super.setDataFromFrame(frame);
273        }
274    }
275
276    public void setViewport(int x, int y, int width, int height) {
277        assertFrameMutable();
278        setNativeViewport(x, y, width, height);
279    }
280
281    public void setViewport(Rect rect) {
282        assertFrameMutable();
283        setNativeViewport(rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top);
284    }
285
286    public void generateMipMap() {
287        assertFrameMutable();
288        assertGLEnvValid();
289        if (!generateNativeMipMap()) {
290            throw new RuntimeException("Could not generate mip-map for GL frame!");
291        }
292    }
293
294    public void setTextureParameter(int param, int value) {
295        assertFrameMutable();
296        assertGLEnvValid();
297        if (!setNativeTextureParam(param, value)) {
298            throw new RuntimeException("Could not set texture value " + param + " = " + value + " " +
299                                       "for GLFrame!");
300        }
301    }
302
303    public int getTextureId() {
304        return getNativeTextureId();
305    }
306
307    public int getFboId() {
308        return getNativeFboId();
309    }
310
311    public void focus() {
312        if (!nativeFocus()) {
313            throw new RuntimeException("Could not focus on GLFrame for drawing!");
314        }
315    }
316
317    @Override
318    public String toString() {
319        return "GLFrame id: " + glFrameId + " (" + getFormat() + ") with texture ID "
320            + getTextureId() + ", FBO ID " + getFboId();
321    }
322
323    @Override
324    protected void reset(FrameFormat newFormat) {
325        if (!nativeResetParams()) {
326            throw new RuntimeException("Could not reset GLFrame texture parameters!");
327        }
328        super.reset(newFormat);
329    }
330
331    @Override
332    protected void onFrameStore() {
333        if (!mOwnsTexture) {
334            // Detach texture from FBO in case user manipulates it.
335            nativeDetachTexFromFbo();
336        }
337    }
338
339    @Override
340    protected void onFrameFetch() {
341        if (!mOwnsTexture) {
342            // Reattach texture to FBO when using frame again. This may reallocate the texture
343            // in case it has become invalid.
344            nativeReattachTexToFbo();
345        }
346    }
347
348    private void assertGLEnvValid() {
349        if (!mGLEnvironment.isContextActive()) {
350            if (GLEnvironment.isAnyContextActive()) {
351                throw new RuntimeException("Attempting to access " + this + " with foreign GL " +
352                    "context active!");
353            } else {
354                throw new RuntimeException("Attempting to access " + this + " with no GL context " +
355                    " active!");
356            }
357        }
358    }
359
360    static {
361        System.loadLibrary("filterfw");
362    }
363
364    private native boolean nativeAllocate(GLEnvironment env, int width, int height);
365
366    private native boolean nativeAllocateWithTexture(GLEnvironment env,
367                                               int textureId,
368                                               int width,
369                                               int height);
370
371    private native boolean nativeAllocateWithFbo(GLEnvironment env,
372                                           int fboId,
373                                           int width,
374                                           int height);
375
376    private native boolean nativeAllocateExternal(GLEnvironment env);
377
378    private native boolean nativeDeallocate();
379
380    private native boolean setNativeData(byte[] data, int offset, int length);
381
382    private native byte[] getNativeData();
383
384    private native boolean setNativeInts(int[] ints);
385
386    private native boolean setNativeFloats(float[] floats);
387
388    private native int[] getNativeInts();
389
390    private native float[] getNativeFloats();
391
392    private native boolean setNativeBitmap(Bitmap bitmap, int size);
393
394    private native boolean getNativeBitmap(Bitmap bitmap);
395
396    private native boolean setNativeViewport(int x, int y, int width, int height);
397
398    private native int getNativeTextureId();
399
400    private native int getNativeFboId();
401
402    private native boolean generateNativeMipMap();
403
404    private native boolean setNativeTextureParam(int param, int value);
405
406    private native boolean nativeResetParams();
407
408    private native boolean nativeCopyFromNative(NativeFrame frame);
409
410    private native boolean nativeCopyFromGL(GLFrame frame);
411
412    private native boolean nativeFocus();
413
414    private native boolean nativeReattachTexToFbo();
415
416    private native boolean nativeDetachTexFromFbo();
417}
418