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 package androidx.media.filterpacks.image;
18 
19 import android.graphics.Bitmap;
20 import android.graphics.Canvas;
21 import android.graphics.Color;
22 import android.graphics.Paint;
23 import android.graphics.Rect;
24 import android.graphics.RectF;
25 import android.view.Surface;
26 import android.view.SurfaceHolder;
27 import android.view.SurfaceView;
28 import android.view.View;
29 
30 import androidx.media.filterfw.FrameImage2D;
31 import androidx.media.filterfw.FrameType;
32 import androidx.media.filterfw.ImageShader;
33 import androidx.media.filterfw.InputPort;
34 import androidx.media.filterfw.MffContext;
35 import androidx.media.filterfw.RenderTarget;
36 import androidx.media.filterfw.Signature;
37 import androidx.media.filterfw.ViewFilter;
38 
39 public 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 
SurfaceHolderTarget(MffContext context, String name)64     public SurfaceHolderTarget(MffContext context, String name) {
65         super(context, name);
66     }
67 
68     @Override
onBindToView(View view)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 
setSurfaceHolder(SurfaceHolder holder)82     public void setSurfaceHolder(SurfaceHolder holder) {
83         if (isRunning()) {
84             throw new IllegalStateException("Cannot set SurfaceHolder while running!");
85         }
86         mSurfaceHolder = holder;
87     }
88 
onDestroySurface()89     public synchronized void onDestroySurface() {
90         if (mRenderTarget != null) {
91             mRenderTarget.release();
92             mRenderTarget = null;
93         }
94         mHasSurface = false;
95     }
96 
97     @Override
getSignature()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
onInputPortOpen(InputPort port)106     protected void onInputPortOpen(InputPort port) {
107         super.connectViewInputs(port);
108     }
109 
110     @Override
onPrepare()111     protected synchronized void onPrepare() {
112         if (isOpenGLSupported()) {
113             mShader = ImageShader.createIdentity();
114         }
115     }
116 
117     @Override
onOpen()118     protected synchronized void onOpen() {
119         mSurfaceHolder.addCallback(mSurfaceHolderListener);
120         Surface surface = mSurfaceHolder.getSurface();
121         mHasSurface = (surface != null) && surface.isValid();
122     }
123 
124     @Override
onProcess()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      */
renderGL(FrameImage2D image)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      */
renderCanvas(FrameImage2D image)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
onClose()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 
onSurfaceCreated(SurfaceHolder holder)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