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