1a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni/*
2a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni * Copyright (C) 2011 The Android Open Source Project
3a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni *
4a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni * Licensed under the Apache License, Version 2.0 (the "License");
5a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni * you may not use this file except in compliance with the License.
6a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni * You may obtain a copy of the License at
7a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni *
8a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni *      http://www.apache.org/licenses/LICENSE-2.0
9a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni *
10a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni * Unless required by applicable law or agreed to in writing, software
11a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni * distributed under the License is distributed on an "AS IS" BASIS,
12a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni * See the License for the specific language governing permissions and
14a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni * limitations under the License.
15a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni */
16a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
17a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
18a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceronipackage android.filterpacks.imageproc;
19a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
20a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroniimport android.filterfw.core.Filter;
21a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroniimport android.filterfw.core.FilterContext;
22a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroniimport android.filterfw.core.Frame;
23a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroniimport android.filterfw.core.FrameFormat;
2421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Rennimport android.filterfw.core.GenerateFieldPort;
25a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroniimport android.filterfw.core.MutableFrameFormat;
26a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroniimport android.filterfw.core.Program;
27a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroniimport android.filterfw.core.ShaderProgram;
28a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroniimport android.filterfw.format.ImageFormat;
29a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
30a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroniimport android.util.Log;
31a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
32979b9d227671081fcc90eb74b6de1b340c7ef781Wei Huaimport java.lang.Math;
33a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala/**
34a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala * @hide
35a3bfbe5389c6146abe318a7add3fa688d69bc01bEino-Ville Talvala */
36a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceronipublic class ToPackedGrayFilter extends Filter {
37a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
3821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFieldPort(name = "owidth", hasDefault = true)
39a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    private int mOWidth = FrameFormat.SIZE_UNSPECIFIED;
4021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    @GenerateFieldPort(name = "oheight", hasDefault = true)
41a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    private int mOHeight = FrameFormat.SIZE_UNSPECIFIED;
4247a6896582866f0ed834c87b95e9a40ed164baacWei Hua    @GenerateFieldPort(name = "keepAspectRatio", hasDefault = true)
4347a6896582866f0ed834c87b95e9a40ed164baacWei Hua    private boolean mKeepAspectRatio = false;
44a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
45a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    private Program mProgram;
46a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
47a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    private final String mColorToPackedGrayShader =
48a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "precision mediump float;\n" +
49a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "const vec4 coeff_y = vec4(0.299, 0.587, 0.114, 0);\n" +
50a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "uniform sampler2D tex_sampler_0;\n" +
51a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "uniform float pix_stride;\n" +
52a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "varying vec2 v_texcoord;\n" +
53a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "void main() {\n" +
54a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "  for (int i = 0; i < 4; ++i) {\n" +
55a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "    vec4 p = texture2D(tex_sampler_0,\n" +
56a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "                       v_texcoord + vec2(pix_stride * float(i), 0.0));\n" +
57a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "    gl_FragColor[i] = dot(p, coeff_y);\n" +
58a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "  }\n" +
59a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        "}\n";
60a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
61a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    public ToPackedGrayFilter(String name) {
62a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        super(name);
63a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    }
64a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
6533d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
6621d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void setupPorts() {
6721d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA,
6821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn                                                       FrameFormat.TARGET_GPU));
6921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        addOutputBasedOnInput("image", "image");
70a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    }
71a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
7233d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
7321d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
7421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        return convertInputFormat(inputFormat);
75a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    }
76a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
77979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua    private void checkOutputDimensions(int outputWidth, int outputHeight) {
78979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        if (outputWidth <= 0 || outputHeight <= 0) {
79979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua            throw new RuntimeException("Invalid output dimensions: " +
80979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua                                       outputWidth + " " + outputHeight);
81a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        }
82a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    }
83a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
8421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    private FrameFormat convertInputFormat(FrameFormat inputFormat) {
85979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        int ow = mOWidth;
86979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        int oh = mOHeight;
87979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        int w = inputFormat.getWidth();
88979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        int h = inputFormat.getHeight();
8921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        if (mOWidth == FrameFormat.SIZE_UNSPECIFIED) {
90979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua            ow = w;
9121d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        }
9221d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        if (mOHeight == FrameFormat.SIZE_UNSPECIFIED) {
93979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua            oh = h;
9421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        }
9547a6896582866f0ed834c87b95e9a40ed164baacWei Hua        if (mKeepAspectRatio) {
96979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua            // if keep aspect ratio, use the bigger dimension to determine the
97979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua            // final output size
98979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua            if (w > h) {
99979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua                ow = Math.max(ow, oh);
100979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua                oh = ow * h / w;
101979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua            } else {
102979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua                oh = Math.max(ow, oh);
103979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua                ow = oh * w / h;
104979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua            }
10547a6896582866f0ed834c87b95e9a40ed164baacWei Hua        }
106979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        ow = (ow / 4) * 4; // ensure width is multiply of 4
107979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        return ImageFormat.create(ow, oh,
10821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn                                  ImageFormat.COLORSPACE_GRAY,
10921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn                                  FrameFormat.TARGET_NATIVE);
11021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    }
11121d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn
11233d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
11321d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void prepare(FilterContext context) {
114511360e61650864ea22a171159efe073c80d0cdbMarius Renn        mProgram = new ShaderProgram(context, mColorToPackedGrayShader);
115a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    }
116a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
11733d107bf315c9cc01a02a7a4a2c10a01f62e8c85Wei Hua    @Override
11821d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn    public void process(FilterContext context) {
11921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        Frame input = pullInput("image");
12021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        FrameFormat inputFormat = input.getFormat();
12121d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        FrameFormat outputFormat = convertInputFormat(inputFormat);
122979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        int ow = outputFormat.getWidth();
123979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        int oh = outputFormat.getHeight();
124979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        checkOutputDimensions(ow, oh);
125979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        mProgram.setHostValue("pix_stride", 1.0f / ow);
126a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
127a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        // Do the RGBA to luminance conversion.
128a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        MutableFrameFormat tempFrameFormat = inputFormat.mutableCopy();
129979b9d227671081fcc90eb74b6de1b340c7ef781Wei Hua        tempFrameFormat.setDimensions(ow / 4, oh);
13021d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        Frame temp = context.getFrameManager().newFrame(tempFrameFormat);
131a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        mProgram.process(input, temp);
132a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
133a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        // Read frame from GPU to CPU.
13421d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        Frame output = context.getFrameManager().newFrame(outputFormat);
135a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        output.setDataFromFrame(temp);
136a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        temp.release();
137a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
138a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        // Push output and yield ownership.
13921d0ac7403b836e32e2bdbdc8dc98f42b2dfa4e5Marius Renn        pushOutput("image", output);
140a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni        output.release();
141a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni    }
142a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni
143a4e8683367b9d4cfc4f8a6cbf3875fe2e35009f0Rodrigo Carceroni}
144