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.transform; 18 19 import android.graphics.Bitmap; 20 import android.graphics.Canvas; 21 import android.graphics.Matrix; 22 import android.graphics.Paint; 23 import android.util.FloatMath; 24 25 import androidx.media.filterfw.Filter; 26 import androidx.media.filterfw.FrameImage2D; 27 import androidx.media.filterfw.FrameType; 28 import androidx.media.filterfw.ImageShader; 29 import androidx.media.filterfw.InputPort; 30 import androidx.media.filterfw.MffContext; 31 import androidx.media.filterfw.OutputPort; 32 import androidx.media.filterfw.Signature; 33 import androidx.media.filterfw.geometry.Quad; 34 35 public class CropFilter extends Filter { 36 37 private Quad mCropRect = Quad.fromRect(0f, 0f, 1f, 1f); 38 private int mOutputWidth = 0; 39 private int mOutputHeight = 0; 40 private ImageShader mShader; 41 private boolean mUseMipmaps = false; 42 private FrameImage2D mPow2Frame = null; 43 44 public CropFilter(MffContext context, String name) { 45 super(context, name); 46 } 47 48 @Override 49 public Signature getSignature() { 50 FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU); 51 FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU); 52 return new Signature() 53 .addInputPort("image", Signature.PORT_REQUIRED, imageIn) 54 .addInputPort("cropRect", Signature.PORT_REQUIRED, FrameType.single(Quad.class)) 55 .addInputPort("outputWidth", Signature.PORT_OPTIONAL, FrameType.single(int.class)) 56 .addInputPort("outputHeight", Signature.PORT_OPTIONAL, FrameType.single(int.class)) 57 .addInputPort("useMipmaps", Signature.PORT_OPTIONAL, FrameType.single(boolean.class)) 58 .addOutputPort("image", Signature.PORT_REQUIRED, imageOut) 59 .disallowOtherPorts(); 60 } 61 62 @Override 63 public void onInputPortOpen(InputPort port) { 64 if (port.getName().equals("cropRect")) { 65 port.bindToFieldNamed("mCropRect"); 66 port.setAutoPullEnabled(true); 67 } else if (port.getName().equals("outputWidth")) { 68 port.bindToFieldNamed("mOutputWidth"); 69 port.setAutoPullEnabled(true); 70 } else if (port.getName().equals("outputHeight")) { 71 port.bindToFieldNamed("mOutputHeight"); 72 port.setAutoPullEnabled(true); 73 } else if (port.getName().equals("useMipmaps")) { 74 port.bindToFieldNamed("mUseMipmaps"); 75 port.setAutoPullEnabled(true); 76 } 77 } 78 79 @Override 80 protected void onPrepare() { 81 if (isOpenGLSupported()) { 82 mShader = ImageShader.createIdentity(); 83 } 84 } 85 86 @Override 87 protected void onProcess() { 88 OutputPort outPort = getConnectedOutputPort("image"); 89 90 // Pull input frame 91 FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D(); 92 int[] inDims = inputImage.getDimensions(); 93 int[] croppedDims = { (int)FloatMath.ceil(mCropRect.xEdge().length() * inDims[0]), 94 (int)FloatMath.ceil(mCropRect.yEdge().length() * inDims[1]) }; 95 int[] outDims = { getOutputWidth(croppedDims[0], croppedDims[1]), 96 getOutputHeight(croppedDims[0], croppedDims[1]) }; 97 FrameImage2D outputImage = outPort.fetchAvailableFrame(outDims).asFrameImage2D(); 98 99 if (isOpenGLSupported()) { 100 FrameImage2D sourceFrame; 101 Quad sourceQuad = null; 102 boolean scaleDown = (outDims[0] < croppedDims[0]) || (outDims[1] < croppedDims[1]); 103 if (scaleDown && mUseMipmaps) { 104 mPow2Frame = TransformUtils.makeMipMappedFrame(mPow2Frame, croppedDims); 105 int[] extDims = mPow2Frame.getDimensions(); 106 float targetWidth = croppedDims[0] / (float)extDims[0]; 107 float targetHeight = croppedDims[1] / (float)extDims[1]; 108 Quad targetQuad = Quad.fromRect(0f, 0f, targetWidth, targetHeight); 109 mShader.setSourceQuad(mCropRect); 110 mShader.setTargetQuad(targetQuad); 111 mShader.process(inputImage, mPow2Frame); 112 TransformUtils.generateMipMaps(mPow2Frame); 113 sourceFrame = mPow2Frame; 114 sourceQuad = targetQuad; 115 } else { 116 sourceFrame = inputImage; 117 sourceQuad = mCropRect; 118 } 119 120 mShader.setSourceQuad(sourceQuad); 121 mShader.setTargetRect(0f, 0f, 1f, 1f); 122 mShader.process(sourceFrame, outputImage); 123 } else { 124 // Convert quads to canvas coordinate space 125 Quad sourceQuad = mCropRect.scale2(inDims[0], inDims[1]); 126 Quad targetQuad = Quad.fromRect(0f, 0f, inDims[0], inDims[1]); 127 128 // Calculate transform for crop 129 Matrix transform = Quad.getTransform(sourceQuad, targetQuad); 130 transform.postScale(outDims[0] / (float)inDims[0], outDims[1] / (float)inDims[1]); 131 132 // Create target canvas 133 Bitmap.Config config = Bitmap.Config.ARGB_8888; 134 Bitmap cropped = Bitmap.createBitmap(outDims[0], outDims[1], config); 135 Canvas canvas = new Canvas(cropped); 136 137 // Draw source bitmap into target canvas 138 Paint paint = new Paint(); 139 paint.setFilterBitmap(true); 140 Bitmap sourceBitmap = inputImage.toBitmap(); 141 canvas.drawBitmap(sourceBitmap, transform, paint); 142 143 // Assign bitmap to output frame 144 outputImage.setBitmap(cropped); 145 } 146 147 outPort.pushFrame(outputImage); 148 } 149 150 @Override 151 protected void onClose() { 152 if (mPow2Frame != null){ 153 mPow2Frame.release(); 154 mPow2Frame = null; 155 } 156 } 157 158 protected int getOutputWidth(int inWidth, int inHeight) { 159 return mOutputWidth <= 0 ? inWidth : mOutputWidth; 160 } 161 162 protected int getOutputHeight(int inWidth, int inHeight) { 163 return mOutputHeight <= 0 ? inHeight : mOutputHeight; 164 } 165 } 166