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.MutableFrameFormat;
26import android.filterfw.core.Program;
27import android.filterfw.core.ShaderProgram;
28import android.filterfw.format.ImageFormat;
29
30import android.util.Log;
31
32import java.lang.Math;
33/**
34 * @hide
35 */
36public class ToPackedGrayFilter extends Filter {
37
38    @GenerateFieldPort(name = "owidth", hasDefault = true)
39    private int mOWidth = FrameFormat.SIZE_UNSPECIFIED;
40    @GenerateFieldPort(name = "oheight", hasDefault = true)
41    private int mOHeight = FrameFormat.SIZE_UNSPECIFIED;
42    @GenerateFieldPort(name = "keepAspectRatio", hasDefault = true)
43    private boolean mKeepAspectRatio = false;
44
45    private Program mProgram;
46
47    private final String mColorToPackedGrayShader =
48        "precision mediump float;\n" +
49        "const vec4 coeff_y = vec4(0.299, 0.587, 0.114, 0);\n" +
50        "uniform sampler2D tex_sampler_0;\n" +
51        "uniform float pix_stride;\n" +
52        "varying vec2 v_texcoord;\n" +
53        "void main() {\n" +
54        "  for (int i = 0; i < 4; ++i) {\n" +
55        "    vec4 p = texture2D(tex_sampler_0,\n" +
56        "                       v_texcoord + vec2(pix_stride * float(i), 0.0));\n" +
57        "    gl_FragColor[i] = dot(p, coeff_y);\n" +
58        "  }\n" +
59        "}\n";
60
61    public ToPackedGrayFilter(String name) {
62        super(name);
63    }
64
65    @Override
66    public void setupPorts() {
67        addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
68                                                       FrameFormat.TARGET_GPU));
69        addOutputBasedOnInput("image", "image");
70    }
71
72    @Override
73    public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
74        return convertInputFormat(inputFormat);
75    }
76
77    private void checkOutputDimensions(int outputWidth, int outputHeight) {
78        if (outputWidth <= 0 || outputHeight <= 0) {
79            throw new RuntimeException("Invalid output dimensions: " +
80                                       outputWidth + " " + outputHeight);
81        }
82    }
83
84    private FrameFormat convertInputFormat(FrameFormat inputFormat) {
85        int ow = mOWidth;
86        int oh = mOHeight;
87        int w = inputFormat.getWidth();
88        int h = inputFormat.getHeight();
89        if (mOWidth == FrameFormat.SIZE_UNSPECIFIED) {
90            ow = w;
91        }
92        if (mOHeight == FrameFormat.SIZE_UNSPECIFIED) {
93            oh = h;
94        }
95        if (mKeepAspectRatio) {
96            // if keep aspect ratio, use the bigger dimension to determine the
97            // final output size
98            if (w > h) {
99                ow = Math.max(ow, oh);
100                oh = ow * h / w;
101            } else {
102                oh = Math.max(ow, oh);
103                ow = oh * w / h;
104            }
105        }
106        ow = (ow > 0 && ow < 4) ? 4 : (ow / 4) * 4; // ensure width is multiple of 4
107        return ImageFormat.create(ow, oh,
108                                  ImageFormat.COLORSPACE_GRAY,
109                                  FrameFormat.TARGET_NATIVE);
110    }
111
112    @Override
113    public void prepare(FilterContext context) {
114        mProgram = new ShaderProgram(context, mColorToPackedGrayShader);
115    }
116
117    @Override
118    public void process(FilterContext context) {
119        Frame input = pullInput("image");
120        FrameFormat inputFormat = input.getFormat();
121        FrameFormat outputFormat = convertInputFormat(inputFormat);
122        int ow = outputFormat.getWidth();
123        int oh = outputFormat.getHeight();
124        checkOutputDimensions(ow, oh);
125        mProgram.setHostValue("pix_stride", 1.0f / ow);
126
127        // Do the RGBA to luminance conversion.
128        MutableFrameFormat tempFrameFormat = inputFormat.mutableCopy();
129        tempFrameFormat.setDimensions(ow / 4, oh);
130        Frame temp = context.getFrameManager().newFrame(tempFrameFormat);
131        mProgram.process(input, temp);
132
133        // Read frame from GPU to CPU.
134        Frame output = context.getFrameManager().newFrame(outputFormat);
135        output.setDataFromFrame(temp);
136        temp.release();
137
138        // Push output and yield ownership.
139        pushOutput("image", output);
140        output.release();
141    }
142
143}
144