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
17package androidx.media.filterpacks.image;
18
19import android.graphics.Bitmap;
20import android.graphics.Canvas;
21import android.graphics.Color;
22import android.graphics.Paint;
23import android.graphics.Rect;
24import android.graphics.RectF;
25import android.view.Surface;
26import android.view.SurfaceHolder;
27import android.view.SurfaceView;
28import android.view.View;
29
30import androidx.media.filterfw.FrameImage2D;
31import androidx.media.filterfw.FrameType;
32import androidx.media.filterfw.ImageShader;
33import androidx.media.filterfw.InputPort;
34import androidx.media.filterfw.MffContext;
35import androidx.media.filterfw.RenderTarget;
36import androidx.media.filterfw.Signature;
37import androidx.media.filterfw.ViewFilter;
38
39public class SurfaceHolderTarget extends ViewFilter {
40
41    private SurfaceHolder mSurfaceHolder = null;
42    private RenderTarget mRenderTarget = null;
43    private ImageShader mShader = null;
44    private boolean mHasSurface = false;
45
46    private SurfaceHolder.Callback mSurfaceHolderListener = new SurfaceHolder.Callback() {
47        @Override
48        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
49            // This just makes sure the holder is still the one we expect.
50            onSurfaceCreated(holder);
51        }
52
53        @Override
54        public void surfaceCreated (SurfaceHolder holder) {
55            onSurfaceCreated(holder);
56        }
57
58        @Override
59        public void surfaceDestroyed (SurfaceHolder holder) {
60            onDestroySurface();
61        }
62    };
63
64    public SurfaceHolderTarget(MffContext context, String name) {
65        super(context, name);
66    }
67
68    @Override
69    public void onBindToView(View view) {
70        if (view instanceof SurfaceView) {
71            SurfaceHolder holder = ((SurfaceView)view).getHolder();
72            if (holder == null) {
73                throw new RuntimeException("Could not get SurfaceHolder from SurfaceView "
74                    + view + "!");
75            }
76            setSurfaceHolder(holder);
77        } else {
78            throw new IllegalArgumentException("View must be a SurfaceView!");
79        }
80    }
81
82    public void setSurfaceHolder(SurfaceHolder holder) {
83        if (isRunning()) {
84            throw new IllegalStateException("Cannot set SurfaceHolder while running!");
85        }
86        mSurfaceHolder = holder;
87    }
88
89    public synchronized void onDestroySurface() {
90        if (mRenderTarget != null) {
91            mRenderTarget.release();
92            mRenderTarget = null;
93        }
94        mHasSurface = false;
95    }
96
97    @Override
98    public Signature getSignature() {
99        FrameType imageType = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
100        return super.getSignature()
101            .addInputPort("image", Signature.PORT_REQUIRED, imageType)
102            .disallowOtherPorts();
103    }
104
105    @Override
106    protected void onInputPortOpen(InputPort port) {
107        super.connectViewInputs(port);
108    }
109
110    @Override
111    protected synchronized void onPrepare() {
112        if (isOpenGLSupported()) {
113            mShader = ImageShader.createIdentity();
114        }
115    }
116
117    @Override
118    protected synchronized void onOpen() {
119        mSurfaceHolder.addCallback(mSurfaceHolderListener);
120        Surface surface = mSurfaceHolder.getSurface();
121        mHasSurface = (surface != null) && surface.isValid();
122    }
123
124    @Override
125    protected synchronized void onProcess() {
126        FrameImage2D image = getConnectedInputPort("image").pullFrame().asFrameImage2D();
127        if (mHasSurface) {
128            // Synchronize the surface holder in case another filter is accessing this surface.
129            synchronized (mSurfaceHolder) {
130                if (isOpenGLSupported()) {
131                    renderGL(image);
132                } else {
133                    renderCanvas(image);
134                }
135            }
136        }
137    }
138
139    /**
140     * Renders the given frame to the screen using GLES2.
141     * @param image the image to render
142     */
143    private void renderGL(FrameImage2D image) {
144        if (mRenderTarget == null) {
145            mRenderTarget = RenderTarget.currentTarget().forSurfaceHolder(mSurfaceHolder);
146            mRenderTarget.registerAsDisplaySurface();
147        }
148        Rect frameRect = new Rect(0, 0, image.getWidth(), image.getHeight());
149        Rect surfRect = mSurfaceHolder.getSurfaceFrame();
150        setupShader(mShader, frameRect, surfRect);
151        mShader.process(image.lockTextureSource(),
152                        mRenderTarget,
153                        surfRect.width(),
154                        surfRect.height());
155        image.unlock();
156        mRenderTarget.swapBuffers();
157    }
158
159    /**
160     * Renders the given frame to the screen using a Canvas.
161     * @param image the image to render
162     */
163    private void renderCanvas(FrameImage2D image) {
164        Canvas canvas = mSurfaceHolder.lockCanvas();
165        Bitmap bitmap = image.toBitmap();
166        Rect sourceRect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
167        Rect surfaceRect = mSurfaceHolder.getSurfaceFrame();
168        RectF targetRect = getTargetRect(sourceRect, surfaceRect);
169        canvas.drawColor(Color.BLACK);
170        if (targetRect.width() > 0 && targetRect.height() > 0) {
171            canvas.scale(surfaceRect.width(), surfaceRect.height());
172            canvas.drawBitmap(bitmap, sourceRect, targetRect, new Paint());
173        }
174        mSurfaceHolder.unlockCanvasAndPost(canvas);
175    }
176
177    @Override
178    protected synchronized void onClose() {
179        if (mRenderTarget != null) {
180            mRenderTarget.unregisterAsDisplaySurface();
181            mRenderTarget.release();
182            mRenderTarget = null;
183        }
184        if (mSurfaceHolder != null) {
185            mSurfaceHolder.removeCallback(mSurfaceHolderListener);
186        }
187    }
188
189    private synchronized void onSurfaceCreated(SurfaceHolder holder) {
190        if (mSurfaceHolder != holder) {
191            throw new RuntimeException("Unexpected Holder!");
192        }
193        mHasSurface = true;
194    }
195
196}
197
198