130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni/*
230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * Copyright (C) 2011 The Android Open Source Project
330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *
430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * Licensed under the Apache License, Version 2.0 (the "License");
530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * you may not use this file except in compliance with the License.
630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * You may obtain a copy of the License at
730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *
830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *      http://www.apache.org/licenses/LICENSE-2.0
930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *
1030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * Unless required by applicable law or agreed to in writing, software
1130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * distributed under the License is distributed on an "AS IS" BASIS,
1230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * See the License for the specific language governing permissions and
1430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * limitations under the License.
1530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni */
1630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
1730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
1830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceronipackage android.filterpacks.ui;
1930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
2030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.Filter;
21692c3bf7a1c7e2d5b1066cf7a1b057e993742c87Marius Rennimport android.filterfw.core.FilterContext;
2230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.FilterSurfaceView;
2330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.Frame;
2430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.FrameFormat;
2521d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Rennimport android.filterfw.core.GenerateFieldPort;
2621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Rennimport android.filterfw.core.GenerateFinalPort;
2730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.GLEnvironment;
2830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.GLFrame;
2930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.KeyValueMap;
3030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.MutableFrameFormat;
3130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.NativeProgram;
3230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.NativeFrame;
3330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.Program;
3430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.ShaderProgram;
3521d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Rennimport android.filterfw.format.ImageFormat;
3630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
3730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.view.Surface;
3830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.view.SurfaceHolder;
3930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.view.SurfaceView;
4030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
4130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.graphics.Rect;
4230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
4330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.util.Log;
4430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
45a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala/**
46a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala * @hide
47a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala */
488bdaff4e029bfac893ea652702e4e685130f28e9Marius Rennpublic class SurfaceRenderFilter extends Filter implements SurfaceHolder.Callback {
4930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
5030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private final int RENDERMODE_STRETCH   = 0;
5130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private final int RENDERMODE_FIT       = 1;
5230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private final int RENDERMODE_FILL_CROP = 2;
5330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
5430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    /** Required. Sets the destination filter surface view for this
5530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni     * node.
5630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni     */
5721d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFinalPort(name = "surfaceView")
5830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private FilterSurfaceView mSurfaceView;
5930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
6030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    /** Optional. Control how the incoming frames are rendered onto the
6130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni     * output. Default is FIT.
6230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni     * RENDERMODE_STRETCH: Just fill the output surfaceView.
6330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni     * RENDERMODE_FIT: Keep aspect ratio and fit without cropping. May
6430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni     * have black bars.
6530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni     * RENDERMODE_FILL_CROP: Keep aspect ratio and fit without black
6630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni     * bars. May crop.
6730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni     */
6821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFieldPort(name = "renderMode", hasDefault = true)
6930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private String mRenderModeString;
7030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
718bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn    private boolean mIsBound = false;
728bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn
7330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private ShaderProgram mProgram;
7430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private GLFrame mScreen;
7530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private int mRenderMode = RENDERMODE_FIT;
7621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    private float mAspectRatio = 1.f;
7730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
7830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private int mScreenWidth;
7930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private int mScreenHeight;
8030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
8157774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala    private boolean mLogVerbose;
8230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private static final String TAG = "SurfaceRenderFilter";
8330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
8430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    public SurfaceRenderFilter(String name) {
8530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        super(name);
8657774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala
8757774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala        mLogVerbose = Log.isLoggable(TAG, Log.VERBOSE);
8830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
8930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
9030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    @Override
9121d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void setupPorts() {
9221d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        // Make sure we have a SurfaceView
9330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        if (mSurfaceView == null) {
9430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            throw new RuntimeException("NULL SurfaceView passed to SurfaceRenderFilter");
9530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
9630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
9721d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        // Add input port
9821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        addMaskedInputPort("frame", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
9921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    }
10021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn
10121d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void updateRenderMode() {
10230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        if (mRenderModeString != null) {
103427833087f412e3bcbe4e31945ea1461f3c4824aRuei-sung Lin            if (mRenderModeString.equals("stretch")) {
10430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                mRenderMode = RENDERMODE_STRETCH;
105427833087f412e3bcbe4e31945ea1461f3c4824aRuei-sung Lin            } else if (mRenderModeString.equals("fit")) {
10630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                mRenderMode = RENDERMODE_FIT;
107427833087f412e3bcbe4e31945ea1461f3c4824aRuei-sung Lin            } else if (mRenderModeString.equals("fill_crop")) {
10830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                mRenderMode = RENDERMODE_FILL_CROP;
10930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            } else {
11030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                throw new RuntimeException("Unknown render mode '" + mRenderModeString + "'!");
11130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            }
11230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
11321d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        updateTargetRect();
11430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
11530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
11630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    @Override
117a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala    public void prepare(FilterContext context) {
11830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // Create identity shader to render, and make sure to render upside-down, as textures
11930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // are stored internally bottom-to-top.
120511360e61650864ea22a171159efe073c80d0cdbMarius Renn        mProgram = ShaderProgram.createIdentity(context);
12130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        mProgram.setSourceRect(0, 1, 1, -1);
12230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        mProgram.setClearsOutput(true);
12330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        mProgram.setClearColor(0.0f, 0.0f, 0.0f);
12430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
12521d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        updateRenderMode();
12630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
12730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // Create a frame representing the screen
128e4c7423f1bbcb79ba9317f09d0da7038ea6bb0b5Marius Renn        MutableFrameFormat screenFormat = ImageFormat.create(mSurfaceView.getWidth(),
129e4c7423f1bbcb79ba9317f09d0da7038ea6bb0b5Marius Renn                                                             mSurfaceView.getHeight(),
130e4c7423f1bbcb79ba9317f09d0da7038ea6bb0b5Marius Renn                                                             ImageFormat.COLORSPACE_RGBA,
131e4c7423f1bbcb79ba9317f09d0da7038ea6bb0b5Marius Renn                                                             FrameFormat.TARGET_GPU);
132a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        mScreen = (GLFrame)context.getFrameManager().newBoundFrame(screenFormat,
13321d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn                                                                   GLFrame.EXISTING_FBO_BINDING,
13421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn                                                                   0);
135a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala    }
136a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala
137a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala    @Override
13821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void open(FilterContext context) {
1398bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn        // Bind surface view to us. This will emit a surfaceCreated and surfaceChanged call that
1408bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn        // will update our screen width and height.
141a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        mSurfaceView.unbind();
1428bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn        mSurfaceView.bindToListener(this, context.getGLEnvironment());
14330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
14430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
14530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    @Override
14621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void process(FilterContext context) {
1478bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn        // Make sure we are bound to a surface before rendering
1488bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn        if (!mIsBound) {
1498bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn            Log.w("SurfaceRenderFilter",
1508bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn                  this + ": Ignoring frame as there is no surface to render to!");
1518bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn            return;
1528bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn        }
1538bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn
15457774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala        if (mLogVerbose) Log.v(TAG, "Starting frame processing");
15530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
156a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        GLEnvironment glEnv = mSurfaceView.getGLEnv();
157a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        if (glEnv != context.getGLEnvironment()) {
158a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala            throw new RuntimeException("Surface created under different GLEnvironment!");
159a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        }
16030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
16130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
162a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        // Get input frame
16321d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        Frame input = pullInput("frame");
164a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        boolean createdFrame = false;
16530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
166a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        float currentAspectRatio = (float)input.getFormat().getWidth() / input.getFormat().getHeight();
167a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        if (currentAspectRatio != mAspectRatio) {
16857774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala            if (mLogVerbose) Log.v(TAG, "New aspect ratio: " + currentAspectRatio +", previously: " + mAspectRatio);
169a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala            mAspectRatio = currentAspectRatio;
170a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala            updateTargetRect();
171a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        }
172a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala
173a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        // See if we need to copy to GPU
174a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        Frame gpuFrame = null;
17557774b9e8ea96fa5088e68bac7a21465fa429b15Eino-Ville Talvala        if (mLogVerbose) Log.v("SurfaceRenderFilter", "Got input format: " + input.getFormat());
176262184714f14ddbff3b8d418091c687416d2a14aMarius Renn        int target = input.getFormat().getTarget();
177262184714f14ddbff3b8d418091c687416d2a14aMarius Renn        if (target != FrameFormat.TARGET_GPU) {
178262184714f14ddbff3b8d418091c687416d2a14aMarius Renn            gpuFrame = context.getFrameManager().duplicateFrameToTarget(input,
179262184714f14ddbff3b8d418091c687416d2a14aMarius Renn                                                                        FrameFormat.TARGET_GPU);
180a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala            createdFrame = true;
181a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        } else {
182a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala            gpuFrame = input;
183a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        }
18430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
185a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        // Activate our surface
186a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        glEnv.activateSurfaceWithId(mSurfaceView.getSurfaceId());
18730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
188a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        // Process
189a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        mProgram.process(gpuFrame, mScreen);
19030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
191a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        // And swap buffers
192a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        glEnv.swapBuffers();
19330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
194a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala        if (createdFrame) {
195a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala            gpuFrame.release();
19630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
19721d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    }
19830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
19921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @Override
20021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void fieldPortValueUpdated(String name, FilterContext context) {
20121d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        updateTargetRect();
20230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
20330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
20430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    @Override
2058bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn    public void close(FilterContext context) {
2068bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn        mSurfaceView.unbind();
2078bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn    }
2088bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn
2098bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn    @Override
210a9ccdef84f787fde226b6b49b6a29db9dc099a4bEino-Ville Talvala    public void tearDown(FilterContext context) {
21130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        if (mScreen != null) {
21230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            mScreen.release();
21330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
21430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
21530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
21630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    @Override
2178bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn    public synchronized void surfaceCreated(SurfaceHolder holder) {
2188bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn        mIsBound = true;
2198bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn    }
2208bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn
2218bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn    @Override
2228bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn    public synchronized void surfaceChanged(SurfaceHolder holder,
2238bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn                                            int format,
2248bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn                                            int width,
2258bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn                                            int height) {
22630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // If the screen is null, we do not care about surface changes (yet). Once we have a
22730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // screen object, we need to keep track of these changes.
22830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        if (mScreen != null) {
22930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            mScreenWidth = width;
23030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            mScreenHeight = height;
23130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            mScreen.setViewport(0, 0, mScreenWidth, mScreenHeight);
23230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            updateTargetRect();
23330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
23430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
23530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
23633d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
2378bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn    public synchronized void surfaceDestroyed(SurfaceHolder holder) {
2388bdaff4e029bfac893ea652702e4e685130f28e9Marius Renn        mIsBound = false;
23930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
24030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
24130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    private void updateTargetRect() {
24230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        if (mScreenWidth > 0 && mScreenHeight > 0 && mProgram != null) {
24330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            float screenAspectRatio = (float)mScreenWidth / mScreenHeight;
24430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            float relativeAspectRatio = screenAspectRatio / mAspectRatio;
24530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
24630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni            switch (mRenderMode) {
24730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                case RENDERMODE_STRETCH:
24830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    mProgram.setTargetRect(0, 0, 1, 1);
24930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    break;
25030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                case RENDERMODE_FIT:
25130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    if (relativeAspectRatio > 1.0f) {
25230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        // Screen is wider than the camera, scale down X
25330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        mProgram.setTargetRect(0.5f - 0.5f / relativeAspectRatio, 0.0f,
25430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                                               1.0f / relativeAspectRatio, 1.0f);
25530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    } else {
25630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        // Screen is taller than the camera, scale down Y
25730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        mProgram.setTargetRect(0.0f, 0.5f - 0.5f * relativeAspectRatio,
25830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                                               1.0f, relativeAspectRatio);
25930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    }
26030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    break;
26130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                case RENDERMODE_FILL_CROP:
26230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    if (relativeAspectRatio > 1) {
26330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        // Screen is wider than the camera, crop in Y
26430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        mProgram.setTargetRect(0.0f, 0.5f - 0.5f * relativeAspectRatio,
26530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                                               1.0f, relativeAspectRatio);
26630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    } else {
26730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        // Screen is taller than the camera, crop in X
26830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                        mProgram.setTargetRect(0.5f - 0.5f / relativeAspectRatio, 0.0f,
26930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                                               1.0f / relativeAspectRatio, 1.0f);
27030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    }
27130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni                    break;
27233d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua            }
27330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        }
27430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
27530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni}
276