blob: 91fe21c57d3071f55ffab97a5cf8bbed1ceb515d [file] [log] [blame]
/*
* 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.transform;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.util.FloatMath;
import androidx.media.filterfw.Filter;
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.OutputPort;
import androidx.media.filterfw.Signature;
import androidx.media.filterfw.geometry.Quad;
public class CropFilter extends Filter {
private Quad mCropRect = Quad.fromRect(0f, 0f, 1f, 1f);
private int mOutputWidth = 0;
private int mOutputHeight = 0;
private ImageShader mShader;
private boolean mUseMipmaps = false;
private FrameImage2D mPow2Frame = null;
public CropFilter(MffContext context, String name) {
super(context, name);
}
@Override
public Signature getSignature() {
FrameType imageIn = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
FrameType imageOut = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.WRITE_GPU);
return new Signature()
.addInputPort("image", Signature.PORT_REQUIRED, imageIn)
.addInputPort("cropRect", Signature.PORT_REQUIRED, FrameType.single(Quad.class))
.addInputPort("outputWidth", Signature.PORT_OPTIONAL, FrameType.single(int.class))
.addInputPort("outputHeight", Signature.PORT_OPTIONAL, FrameType.single(int.class))
.addInputPort("useMipmaps", Signature.PORT_OPTIONAL, FrameType.single(boolean.class))
.addOutputPort("image", Signature.PORT_REQUIRED, imageOut)
.disallowOtherPorts();
}
@Override
public void onInputPortOpen(InputPort port) {
if (port.getName().equals("cropRect")) {
port.bindToFieldNamed("mCropRect");
port.setAutoPullEnabled(true);
} else if (port.getName().equals("outputWidth")) {
port.bindToFieldNamed("mOutputWidth");
port.setAutoPullEnabled(true);
} else if (port.getName().equals("outputHeight")) {
port.bindToFieldNamed("mOutputHeight");
port.setAutoPullEnabled(true);
} else if (port.getName().equals("useMipmaps")) {
port.bindToFieldNamed("mUseMipmaps");
port.setAutoPullEnabled(true);
}
}
@Override
protected void onPrepare() {
if (isOpenGLSupported()) {
mShader = ImageShader.createIdentity();
}
}
@Override
protected void onProcess() {
OutputPort outPort = getConnectedOutputPort("image");
// Pull input frame
FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
int[] inDims = inputImage.getDimensions();
int[] croppedDims = { (int)FloatMath.ceil(mCropRect.xEdge().length() * inDims[0]),
(int)FloatMath.ceil(mCropRect.yEdge().length() * inDims[1]) };
int[] outDims = { getOutputWidth(croppedDims[0], croppedDims[1]),
getOutputHeight(croppedDims[0], croppedDims[1]) };
FrameImage2D outputImage = outPort.fetchAvailableFrame(outDims).asFrameImage2D();
if (isOpenGLSupported()) {
FrameImage2D sourceFrame;
Quad sourceQuad = null;
boolean scaleDown = (outDims[0] < croppedDims[0]) || (outDims[1] < croppedDims[1]);
if (scaleDown && mUseMipmaps) {
mPow2Frame = TransformUtils.makeMipMappedFrame(mPow2Frame, croppedDims);
int[] extDims = mPow2Frame.getDimensions();
float targetWidth = croppedDims[0] / (float)extDims[0];
float targetHeight = croppedDims[1] / (float)extDims[1];
Quad targetQuad = Quad.fromRect(0f, 0f, targetWidth, targetHeight);
mShader.setSourceQuad(mCropRect);
mShader.setTargetQuad(targetQuad);
mShader.process(inputImage, mPow2Frame);
TransformUtils.generateMipMaps(mPow2Frame);
sourceFrame = mPow2Frame;
sourceQuad = targetQuad;
} else {
sourceFrame = inputImage;
sourceQuad = mCropRect;
}
mShader.setSourceQuad(sourceQuad);
mShader.setTargetRect(0f, 0f, 1f, 1f);
mShader.process(sourceFrame, outputImage);
} else {
// Convert quads to canvas coordinate space
Quad sourceQuad = mCropRect.scale2(inDims[0], inDims[1]);
Quad targetQuad = Quad.fromRect(0f, 0f, inDims[0], inDims[1]);
// Calculate transform for crop
Matrix transform = Quad.getTransform(sourceQuad, targetQuad);
transform.postScale(outDims[0] / (float)inDims[0], outDims[1] / (float)inDims[1]);
// Create target canvas
Bitmap.Config config = Bitmap.Config.ARGB_8888;
Bitmap cropped = Bitmap.createBitmap(outDims[0], outDims[1], config);
Canvas canvas = new Canvas(cropped);
// Draw source bitmap into target canvas
Paint paint = new Paint();
paint.setFilterBitmap(true);
Bitmap sourceBitmap = inputImage.toBitmap();
canvas.drawBitmap(sourceBitmap, transform, paint);
// Assign bitmap to output frame
outputImage.setBitmap(cropped);
}
outPort.pushFrame(outputImage);
}
@Override
protected void onClose() {
if (mPow2Frame != null){
mPow2Frame.release();
mPow2Frame = null;
}
}
protected int getOutputWidth(int inWidth, int inHeight) {
return mOutputWidth <= 0 ? inWidth : mOutputWidth;
}
protected int getOutputHeight(int inWidth, int inHeight) {
return mOutputHeight <= 0 ? inHeight : mOutputHeight;
}
}