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