/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package androidx.media.filterpacks.image; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.view.Surface; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import androidx.media.filterfw.FrameImage2D; import androidx.media.filterfw.FrameType; import androidx.media.filterfw.ImageShader; import androidx.media.filterfw.InputPort; import androidx.media.filterfw.MffContext; import androidx.media.filterfw.RenderTarget; import androidx.media.filterfw.Signature; import androidx.media.filterfw.ViewFilter; public class SurfaceHolderTarget extends ViewFilter { private SurfaceHolder mSurfaceHolder = null; private RenderTarget mRenderTarget = null; private ImageShader mShader = null; private boolean mHasSurface = false; private SurfaceHolder.Callback mSurfaceHolderListener = new SurfaceHolder.Callback() { @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { // This just makes sure the holder is still the one we expect. onSurfaceCreated(holder); } @Override public void surfaceCreated (SurfaceHolder holder) { onSurfaceCreated(holder); } @Override public void surfaceDestroyed (SurfaceHolder holder) { onDestroySurface(); } }; public SurfaceHolderTarget(MffContext context, String name) { super(context, name); } @Override public void onBindToView(View view) { if (view instanceof SurfaceView) { SurfaceHolder holder = ((SurfaceView)view).getHolder(); if (holder == null) { throw new RuntimeException("Could not get SurfaceHolder from SurfaceView " + view + "!"); } setSurfaceHolder(holder); } else { throw new IllegalArgumentException("View must be a SurfaceView!"); } } public void setSurfaceHolder(SurfaceHolder holder) { if (isRunning()) { throw new IllegalStateException("Cannot set SurfaceHolder while running!"); } mSurfaceHolder = holder; } public synchronized void onDestroySurface() { if (mRenderTarget != null) { mRenderTarget.release(); mRenderTarget = null; } mHasSurface = false; } @Override public Signature getSignature() { FrameType imageType = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU); return super.getSignature() .addInputPort("image", Signature.PORT_REQUIRED, imageType) .disallowOtherPorts(); } @Override protected void onInputPortOpen(InputPort port) { super.connectViewInputs(port); } @Override protected synchronized void onPrepare() { if (isOpenGLSupported()) { mShader = ImageShader.createIdentity(); } } @Override protected synchronized void onOpen() { mSurfaceHolder.addCallback(mSurfaceHolderListener); Surface surface = mSurfaceHolder.getSurface(); mHasSurface = (surface != null) && surface.isValid(); } @Override protected synchronized void onProcess() { FrameImage2D image = getConnectedInputPort("image").pullFrame().asFrameImage2D(); if (mHasSurface) { // Synchronize the surface holder in case another filter is accessing this surface. synchronized (mSurfaceHolder) { if (isOpenGLSupported()) { renderGL(image); } else { renderCanvas(image); } } } } /** * Renders the given frame to the screen using GLES2. * @param image the image to render */ private void renderGL(FrameImage2D image) { if (mRenderTarget == null) { mRenderTarget = RenderTarget.currentTarget().forSurfaceHolder(mSurfaceHolder); mRenderTarget.registerAsDisplaySurface(); } Rect frameRect = new Rect(0, 0, image.getWidth(), image.getHeight()); Rect surfRect = mSurfaceHolder.getSurfaceFrame(); setupShader(mShader, frameRect, surfRect); mShader.process(image.lockTextureSource(), mRenderTarget, surfRect.width(), surfRect.height()); image.unlock(); mRenderTarget.swapBuffers(); } /** * Renders the given frame to the screen using a Canvas. * @param image the image to render */ private void renderCanvas(FrameImage2D image) { Canvas canvas = mSurfaceHolder.lockCanvas(); Bitmap bitmap = image.toBitmap(); Rect sourceRect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); Rect surfaceRect = mSurfaceHolder.getSurfaceFrame(); RectF targetRect = getTargetRect(sourceRect, surfaceRect); canvas.drawColor(Color.BLACK); if (targetRect.width() > 0 && targetRect.height() > 0) { canvas.scale(surfaceRect.width(), surfaceRect.height()); canvas.drawBitmap(bitmap, sourceRect, targetRect, new Paint()); } mSurfaceHolder.unlockCanvasAndPost(canvas); } @Override protected synchronized void onClose() { if (mRenderTarget != null) { mRenderTarget.unregisterAsDisplaySurface(); mRenderTarget.release(); mRenderTarget = null; } if (mSurfaceHolder != null) { mSurfaceHolder.removeCallback(mSurfaceHolderListener); } } private synchronized void onSurfaceCreated(SurfaceHolder holder) { if (mSurfaceHolder != holder) { throw new RuntimeException("Unexpected Holder!"); } mHasSurface = true; } }