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
18package android.filterpacks.imageproc;
19
20import android.filterfw.core.Filter;
21import android.filterfw.core.FilterContext;
22import android.filterfw.core.Frame;
23import android.filterfw.core.FrameFormat;
24import android.filterfw.core.GenerateFieldPort;
25import android.filterfw.core.KeyValueMap;
26import android.filterfw.core.MutableFrameFormat;
27import android.filterfw.core.NativeProgram;
28import android.filterfw.core.NativeFrame;
29import android.filterfw.core.Program;
30import android.filterfw.core.ShaderProgram;
31import android.filterfw.geometry.Point;
32import android.filterfw.geometry.Quad;
33import android.filterfw.format.ImageFormat;
34import android.filterfw.format.ObjectFormat;
35
36import android.util.Log;
37
38/**
39 * @hide
40 */
41public class CropFilter extends Filter {
42
43    private Program mProgram;
44    private FrameFormat mLastFormat = null;
45
46    @GenerateFieldPort(name = "owidth")
47    private int mOutputWidth = -1;
48
49    @GenerateFieldPort(name = "oheight")
50    private int mOutputHeight = -1;
51
52    @GenerateFieldPort(name = "fillblack")
53    private boolean mFillBlack = false;
54
55    public CropFilter(String name) {
56        super(name);
57    }
58
59    private final String mFragShader =
60      "precision mediump float;\n" +
61      "uniform sampler2D tex_sampler_0;\n" +
62      "varying vec2 v_texcoord;\n" +
63      "void main() {\n" +
64      "  const vec2 lo = vec2(0.0, 0.0);\n" +
65      "  const vec2 hi = vec2(1.0, 1.0);\n" +
66      "  const vec4 black = vec4(0.0, 0.0, 0.0, 1.0);\n" +
67      "  bool out_of_bounds =\n" +
68      "    any(lessThan(v_texcoord, lo)) ||\n" +
69      "    any(greaterThan(v_texcoord, hi));\n" +
70      "  if (out_of_bounds) {\n" +
71      "    gl_FragColor = black;\n" +
72      "  } else {\n" +
73      "    gl_FragColor = texture2D(tex_sampler_0, v_texcoord);\n" +
74      "  }\n" +
75      "}\n";
76
77    @Override
78    public void setupPorts() {
79        addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
80        addMaskedInputPort("box", ObjectFormat.fromClass(Quad.class, FrameFormat.TARGET_SIMPLE));
81        addOutputBasedOnInput("image", "image");
82    }
83
84    @Override
85    public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
86        // Make sure output size is set to unspecified, as we do not know what we will be resizing
87        // to.
88        MutableFrameFormat outputFormat = inputFormat.mutableCopy();
89        outputFormat.setDimensions(FrameFormat.SIZE_UNSPECIFIED, FrameFormat.SIZE_UNSPECIFIED);
90        return outputFormat;
91    }
92
93    protected void createProgram(FilterContext context, FrameFormat format) {
94        // TODO: Add CPU version
95        if (mLastFormat != null && mLastFormat.getTarget() == format.getTarget()) return;
96        mLastFormat = format;
97        mProgram = null;
98        switch (format.getTarget()) {
99            case FrameFormat.TARGET_GPU:
100              if(mFillBlack)
101                mProgram = new ShaderProgram(context, mFragShader);
102              else
103                mProgram = ShaderProgram.createIdentity(context);
104
105                break;
106        }
107        if (mProgram == null) {
108            throw new RuntimeException("Could not create a program for crop filter " + this + "!");
109        }
110    }
111
112    @Override
113    public void process(FilterContext env) {
114        // Get input frame
115        Frame imageFrame = pullInput("image");
116        Frame boxFrame = pullInput("box");
117
118        createProgram(env, imageFrame.getFormat());
119
120        // Get the box
121        Quad box = (Quad)boxFrame.getObjectValue();
122
123        // Create output format
124        MutableFrameFormat outputFormat = imageFrame.getFormat().mutableCopy();
125        outputFormat.setDimensions(mOutputWidth == -1 ? outputFormat.getWidth() : mOutputWidth,
126                                   mOutputHeight == -1 ? outputFormat.getHeight() : mOutputHeight);
127
128        // Create output frame
129        Frame output = env.getFrameManager().newFrame(outputFormat);
130
131        // Set the program parameters
132        if (mProgram instanceof ShaderProgram) {
133            ShaderProgram shaderProgram = (ShaderProgram)mProgram;
134            shaderProgram.setSourceRegion(box);
135        }
136
137        mProgram.process(imageFrame, output);
138
139        // Push output
140        pushOutput("image", output);
141
142        // Release pushed frame
143        output.release();
144    }
145
146
147}
148