130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni/*
230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * Copyright (C) 2011 The Android Open Source Project
330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *
430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * Licensed under the Apache License, Version 2.0 (the "License");
530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * you may not use this file except in compliance with the License.
630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * You may obtain a copy of the License at
730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *
830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *      http://www.apache.org/licenses/LICENSE-2.0
930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni *
1030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * Unless required by applicable law or agreed to in writing, software
1130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * distributed under the License is distributed on an "AS IS" BASIS,
1230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * See the License for the specific language governing permissions and
1430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni * limitations under the License.
1530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni */
1630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
1730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
1830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceronipackage android.filterpacks.imageproc;
1930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
2030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.Filter;
21692c3bf7a1c7e2d5b1066cf7a1b057e993742c87Marius Rennimport android.filterfw.core.FilterContext;
2230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.Frame;
2330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.FrameFormat;
2421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Rennimport android.filterfw.core.GenerateFieldPort;
2530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.KeyValueMap;
2630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.MutableFrameFormat;
2730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.NativeProgram;
2830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.NativeFrame;
2930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.Program;
3030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.core.ShaderProgram;
3130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.geometry.Point;
3230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.filterfw.geometry.Quad;
33c0017fd82acec5d0427306ea5f536c3d78854f95Marius Rennimport android.filterfw.format.ImageFormat;
3421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Rennimport android.filterfw.format.ObjectFormat;
3530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
3630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroniimport android.util.Log;
3730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
38a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala/**
39a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala * @hide
40a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala */
4130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceronipublic class CropFilter extends Filter {
4230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
4321d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    private Program mProgram;
44262a009cbd3e4eab125246cf70f110ad3cd6c75eWei Hua    private FrameFormat mLastFormat = null;
45c0017fd82acec5d0427306ea5f536c3d78854f95Marius Renn
4621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFieldPort(name = "owidth")
47c0017fd82acec5d0427306ea5f536c3d78854f95Marius Renn    private int mOutputWidth = -1;
48c0017fd82acec5d0427306ea5f536c3d78854f95Marius Renn
4921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFieldPort(name = "oheight")
50c0017fd82acec5d0427306ea5f536c3d78854f95Marius Renn    private int mOutputHeight = -1;
5130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
521cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng    @GenerateFieldPort(name = "fillblack")
531cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng    private boolean mFillBlack = false;
541cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng
5530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    public CropFilter(String name) {
5630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        super(name);
5730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
5830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
591cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng    private final String mFragShader =
601cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "precision mediump float;\n" +
611cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "uniform sampler2D tex_sampler_0;\n" +
621cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "varying vec2 v_texcoord;\n" +
631cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "void main() {\n" +
641cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "  const vec2 lo = vec2(0.0, 0.0);\n" +
651cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "  const vec2 hi = vec2(1.0, 1.0);\n" +
661cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "  const vec4 black = vec4(0.0, 0.0, 0.0, 1.0);\n" +
671cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "  bool out_of_bounds =\n" +
681cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "    any(lessThan(v_texcoord, lo)) ||\n" +
691cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "    any(greaterThan(v_texcoord, hi));\n" +
701cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "  if (out_of_bounds) {\n" +
711cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "    gl_FragColor = black;\n" +
721cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "  } else {\n" +
731cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "    gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
741cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "  }\n" +
751cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng      "}\n";
761cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng
7733d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
7821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void setupPorts() {
7921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
80bf4aaebc555cfb1e49ee411e3477203749fe6a11Marius Renn        addMaskedInputPort("box", ObjectFormat.fromClass(Quad.class, FrameFormat.TARGET_SIMPLE));
8121d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        addOutputBasedOnInput("image", "image");
8230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
8330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
8433d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
8521d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
8621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        // Make sure output size is set to unspecified, as we do not know what we will be resizing
8721d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        // to.
8821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        MutableFrameFormat outputFormat = inputFormat.mutableCopy();
8921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        outputFormat.setDimensions(FrameFormat.SIZE_UNSPECIFIED, FrameFormat.SIZE_UNSPECIFIED);
9021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        return outputFormat;
9130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
9230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
93262a009cbd3e4eab125246cf70f110ad3cd6c75eWei Hua    protected void createProgram(FilterContext context, FrameFormat format) {
9421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        // TODO: Add CPU version
95262a009cbd3e4eab125246cf70f110ad3cd6c75eWei Hua        if (mLastFormat != null && mLastFormat.getTarget() == format.getTarget()) return;
96262a009cbd3e4eab125246cf70f110ad3cd6c75eWei Hua        mLastFormat = format;
97262a009cbd3e4eab125246cf70f110ad3cd6c75eWei Hua        mProgram = null;
98262a009cbd3e4eab125246cf70f110ad3cd6c75eWei Hua        switch (format.getTarget()) {
9921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn            case FrameFormat.TARGET_GPU:
1001cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng              if(mFillBlack)
1011cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng                mProgram = new ShaderProgram(context, mFragShader);
1021cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng              else
103511360e61650864ea22a171159efe073c80d0cdbMarius Renn                mProgram = ShaderProgram.createIdentity(context);
1041cfe527ff61b629f3a1bf0d5882243ac6780297aYifan Peng
10521d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn                break;
10621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        }
10721d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        if (mProgram == null) {
10821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn            throw new RuntimeException("Could not create a program for crop filter " + this + "!");
10921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        }
11030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
11130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
11233d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
11321d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void process(FilterContext env) {
11430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // Get input frame
11521d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        Frame imageFrame = pullInput("image");
11621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        Frame boxFrame = pullInput("box");
11730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
118262a009cbd3e4eab125246cf70f110ad3cd6c75eWei Hua        createProgram(env, imageFrame.getFormat());
119262a009cbd3e4eab125246cf70f110ad3cd6c75eWei Hua
12030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // Get the box
12130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        Quad box = (Quad)boxFrame.getObjectValue();
12230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
123c0017fd82acec5d0427306ea5f536c3d78854f95Marius Renn        // Create output format
124c0017fd82acec5d0427306ea5f536c3d78854f95Marius Renn        MutableFrameFormat outputFormat = imageFrame.getFormat().mutableCopy();
125c0017fd82acec5d0427306ea5f536c3d78854f95Marius Renn        outputFormat.setDimensions(mOutputWidth == -1 ? outputFormat.getWidth() : mOutputWidth,
126c0017fd82acec5d0427306ea5f536c3d78854f95Marius Renn                                   mOutputHeight == -1 ? outputFormat.getHeight() : mOutputHeight);
127c0017fd82acec5d0427306ea5f536c3d78854f95Marius Renn
12830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // Create output frame
129c0017fd82acec5d0427306ea5f536c3d78854f95Marius Renn        Frame output = env.getFrameManager().newFrame(outputFormat);
13030ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
13121d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        // Set the program parameters
13221d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        if (mProgram instanceof ShaderProgram) {
13321d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn            ShaderProgram shaderProgram = (ShaderProgram)mProgram;
13421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn            shaderProgram.setSourceRegion(box);
13521d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        }
13621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn
13730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        mProgram.process(imageFrame, output);
13830ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
13930ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // Push output
14021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        pushOutput("image", output);
14130ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
14230ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        // Release pushed frame
14330ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni        output.release();
14430ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni    }
14530ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
14630ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni
14730ab3fc173709a491c9e2e103f53fb7c0d1b96b7Rodrigo Carceroni}
148