1227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks/* 2227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Copyright (C) 2011 The Android Open Source Project 3227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * 4227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Licensed under the Apache License, Version 2.0 (the "License"); 5227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * you may not use this file except in compliance with the License. 6227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * You may obtain a copy of the License at 7227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * 8227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * http://www.apache.org/licenses/LICENSE-2.0 9227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * 10227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * Unless required by applicable law or agreed to in writing, software 11227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * distributed under the License is distributed on an "AS IS" BASIS, 12227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * See the License for the specific language governing permissions and 14227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks * limitations under the License. 15227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks */ 16227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 17227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendrickspackage androidx.media.filterpacks.transform; 18227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 19227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.graphics.Bitmap; 20227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.graphics.Canvas; 21227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.graphics.Matrix; 22227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport android.graphics.Paint; 23227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 24227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.Filter; 25227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.FrameImage2D; 26227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.FrameType; 27227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.ImageShader; 28227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.InputPort; 29227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.MffContext; 30227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.OutputPort; 31227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.Signature; 32227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricksimport androidx.media.filterfw.geometry.Quad; 33227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 34227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendrickspublic class CropFilter extends Filter { 35227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 36227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private Quad mCropRect = Quad.fromRect(0f, 0f, 1f, 1f); 37227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private int mOutputWidth = 0; 38227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private int mOutputHeight = 0; 39227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private ImageShader mShader; 40227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private boolean mUseMipmaps = false; 41227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks private FrameImage2D mPow2Frame = null; 42227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 43227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks public CropFilter(MffContext context, String name) { 44227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks super(context, name); 45227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 46227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 47227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks @Override 48227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks public Signature getSignature() { 49227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU); 50227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU); 51227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks return new Signature() 52227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks .addInputPort("image", Signature.PORT_REQUIRED, imageIn) 53227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks .addInputPort("cropRect", Signature.PORT_REQUIRED, FrameType.single(Quad.class)) 54227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks .addInputPort("outputWidth", Signature.PORT_OPTIONAL, FrameType.single(int.class)) 55227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks .addInputPort("outputHeight", Signature.PORT_OPTIONAL, FrameType.single(int.class)) 56227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks .addInputPort("useMipmaps", Signature.PORT_OPTIONAL, FrameType.single(boolean.class)) 57227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks .addOutputPort("image", Signature.PORT_REQUIRED, imageOut) 58227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks .disallowOtherPorts(); 59227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 60227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 61227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks @Override 62227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks public void onInputPortOpen(InputPort port) { 63227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (port.getName().equals("cropRect")) { 64227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks port.bindToFieldNamed("mCropRect"); 65227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks port.setAutoPullEnabled(true); 66227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } else if (port.getName().equals("outputWidth")) { 67227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks port.bindToFieldNamed("mOutputWidth"); 68227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks port.setAutoPullEnabled(true); 69227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } else if (port.getName().equals("outputHeight")) { 70227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks port.bindToFieldNamed("mOutputHeight"); 71227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks port.setAutoPullEnabled(true); 72227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } else if (port.getName().equals("useMipmaps")) { 73227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks port.bindToFieldNamed("mUseMipmaps"); 74227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks port.setAutoPullEnabled(true); 75227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 76227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 77227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 78227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks @Override 79227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected void onPrepare() { 80227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (isOpenGLSupported()) { 81227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mShader = ImageShader.createIdentity(); 82227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 83227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 84227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 85227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks @Override 86227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected void onProcess() { 87227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks OutputPort outPort = getConnectedOutputPort("image"); 88227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 89227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // Pull input frame 90227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D(); 91227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks int[] inDims = inputImage.getDimensions(); 9233253a4baa6279f81a73425b49dfb6abe5f5416eNeil Fuller int[] croppedDims = { (int)Math.ceil(mCropRect.xEdge().length() * inDims[0]), 9333253a4baa6279f81a73425b49dfb6abe5f5416eNeil Fuller (int)Math.ceil(mCropRect.yEdge().length() * inDims[1]) }; 94227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks int[] outDims = { getOutputWidth(croppedDims[0], croppedDims[1]), 95227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks getOutputHeight(croppedDims[0], croppedDims[1]) }; 96227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks FrameImage2D outputImage = outPort.fetchAvailableFrame(outDims).asFrameImage2D(); 97227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 98227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (isOpenGLSupported()) { 99227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks FrameImage2D sourceFrame; 100227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Quad sourceQuad = null; 101227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks boolean scaleDown = (outDims[0] < croppedDims[0]) || (outDims[1] < croppedDims[1]); 102227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (scaleDown && mUseMipmaps) { 103227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mPow2Frame = TransformUtils.makeMipMappedFrame(mPow2Frame, croppedDims); 104227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks int[] extDims = mPow2Frame.getDimensions(); 105227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks float targetWidth = croppedDims[0] / (float)extDims[0]; 106227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks float targetHeight = croppedDims[1] / (float)extDims[1]; 107227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Quad targetQuad = Quad.fromRect(0f, 0f, targetWidth, targetHeight); 108227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mShader.setSourceQuad(mCropRect); 109227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mShader.setTargetQuad(targetQuad); 110227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mShader.process(inputImage, mPow2Frame); 111227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks TransformUtils.generateMipMaps(mPow2Frame); 112227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks sourceFrame = mPow2Frame; 113227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks sourceQuad = targetQuad; 114227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } else { 115227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks sourceFrame = inputImage; 116227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks sourceQuad = mCropRect; 117227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 118227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 119227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mShader.setSourceQuad(sourceQuad); 120227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mShader.setTargetRect(0f, 0f, 1f, 1f); 121227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mShader.process(sourceFrame, outputImage); 122227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } else { 123227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // Convert quads to canvas coordinate space 124227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Quad sourceQuad = mCropRect.scale2(inDims[0], inDims[1]); 125227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Quad targetQuad = Quad.fromRect(0f, 0f, inDims[0], inDims[1]); 126227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 127227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // Calculate transform for crop 128227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Matrix transform = Quad.getTransform(sourceQuad, targetQuad); 129227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks transform.postScale(outDims[0] / (float)inDims[0], outDims[1] / (float)inDims[1]); 130227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 131227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // Create target canvas 132227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Bitmap.Config config = Bitmap.Config.ARGB_8888; 133227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Bitmap cropped = Bitmap.createBitmap(outDims[0], outDims[1], config); 134227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Canvas canvas = new Canvas(cropped); 135227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 136227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // Draw source bitmap into target canvas 137227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Paint paint = new Paint(); 138227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks paint.setFilterBitmap(true); 139227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks Bitmap sourceBitmap = inputImage.toBitmap(); 140227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks canvas.drawBitmap(sourceBitmap, transform, paint); 141227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 142227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks // Assign bitmap to output frame 143227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks outputImage.setBitmap(cropped); 144227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 145227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 146227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks outPort.pushFrame(outputImage); 147227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 148227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 149227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks @Override 150227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected void onClose() { 151227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks if (mPow2Frame != null){ 152227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mPow2Frame.release(); 153227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks mPow2Frame = null; 154227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 155227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 156227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 157227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected int getOutputWidth(int inWidth, int inHeight) { 158227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks return mOutputWidth <= 0 ? inWidth : mOutputWidth; 159227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 160227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks 161227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks protected int getOutputHeight(int inWidth, int inHeight) { 162227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks return mOutputHeight <= 0 ? inHeight : mOutputHeight; 163227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks } 164227b47625d7482b5b47ad0e4c70ce0a246236adeBenjamin Hendricks} 165