1 /*
2  * Copyright 2013 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 package androidx.media.filterpacks.image;
18 
19 import androidx.media.filterfw.Filter;
20 import androidx.media.filterfw.Frame;
21 import androidx.media.filterfw.FrameBuffer2D;
22 import androidx.media.filterfw.FrameImage2D;
23 import androidx.media.filterfw.FrameType;
24 import androidx.media.filterfw.ImageShader;
25 import androidx.media.filterfw.MffContext;
26 import androidx.media.filterfw.OutputPort;
27 import androidx.media.filterfw.RenderTarget;
28 import androidx.media.filterfw.Signature;
29 import androidx.media.filterfw.geometry.Quad;
30 
31 import java.nio.ByteBuffer;
32 
33 public class ToGrayValuesFilter extends Filter {
34 
35     private final static String mGrayPackFragment =
36         "precision mediump float;\n" +
37         "const vec4 coeff_y = vec4(0.299, 0.587, 0.114, 0);\n" +
38         "uniform sampler2D tex_sampler_0;\n" +
39         "uniform float pix_stride;\n" +
40         "varying vec2 v_texcoord;\n" +
41         "void main() {\n" +
42         "  for (int i = 0; i < 4; i++) {\n" +
43         // Here is an example showing how this works:
44         // Assuming the input texture is 1x4 while the output texture is 1x1
45         // the coordinates of the 4 input pixels will be:
46         // { (0.125, 0.5), (0.375, 0.5), (0.625, 0.5), (0.875, 0.5) }
47         // and the coordinates of the 1 output pixels will be:
48         // { (0.5, 0.5) }
49         // the equation below locates the 4 input pixels from the coordinate of the output pixel
50         "    vec4 p = texture2D(tex_sampler_0,\n" +
51         "                       v_texcoord + vec2(pix_stride * (float(i) - 1.5), 0.0));\n" +
52         "    gl_FragColor[i] = dot(p, coeff_y);\n" +
53         "  }\n" +
54         "}\n";
55 
56     private ImageShader mShader;
57 
58     private FrameType mImageInType;
59 
ToGrayValuesFilter(MffContext context, String name)60     public ToGrayValuesFilter(MffContext context, String name) {
61         super(context, name);
62     }
63 
64     @Override
getSignature()65     public Signature getSignature() {
66         mImageInType = FrameType.image2D(FrameType.ELEMENT_RGBA8888, FrameType.READ_GPU);
67         FrameType imageOut = FrameType.buffer2D(FrameType.ELEMENT_INT8);
68         return new Signature()
69             .addInputPort("image", Signature.PORT_REQUIRED, mImageInType)
70             .addOutputPort("image", Signature.PORT_REQUIRED, imageOut)
71             .disallowOtherPorts();
72     }
73 
74     @Override
onPrepare()75     protected void onPrepare() {
76         if (isOpenGLSupported()) {
77             mShader = new ImageShader(mGrayPackFragment);
78         }
79     }
80 
81     @Override
onProcess()82     protected void onProcess() {
83         OutputPort outPort = getConnectedOutputPort("image");
84         FrameImage2D inputImage = getConnectedInputPort("image").pullFrame().asFrameImage2D();
85         int[] dim = inputImage.getDimensions();
86         FrameBuffer2D outputFrame;
87         ByteBuffer grayBuffer;
88 
89         if (isOpenGLSupported()) {
90             // crop out the portion of inputImage that will be used to generate outputFrame.
91             int modular = dim[0] % 4;
92             int[] outDim = new int[] {dim[0] - modular, dim[1]};
93             outputFrame = outPort.fetchAvailableFrame(outDim).asFrameBuffer2D();
94             grayBuffer = outputFrame.lockBytes(Frame.MODE_WRITE);
95 
96             int[] targetDims = new int[] { outDim[0] / 4, outDim[1] };
97             FrameImage2D targetFrame = Frame.create(mImageInType, targetDims).asFrameImage2D();
98             mShader.setSourceQuad(Quad.fromRect(0f, 0f, ((float)outDim[0])/dim[0], 1f));
99             mShader.setUniformValue("pix_stride", 1f / outDim[0]);
100             mShader.process(inputImage, targetFrame);
101             RenderTarget grayTarget = targetFrame.lockRenderTarget();
102             grayTarget.readPixelData(grayBuffer, targetDims[0], targetDims[1]);
103             targetFrame.unlock();
104             targetFrame.release();
105         } else {
106             outputFrame = outPort.fetchAvailableFrame(dim).asFrameBuffer2D();
107             grayBuffer = outputFrame.lockBytes(Frame.MODE_WRITE);
108             ByteBuffer inputBuffer  = inputImage.lockBytes(Frame.MODE_READ);
109             if (!toGrayValues(inputBuffer, grayBuffer)) {
110                 throw new RuntimeException(
111                         "Native implementation encountered an error during processing!");
112             }
113             inputImage.unlock();
114         }
115         outputFrame.unlock();
116         outPort.pushFrame(outputFrame);
117     }
118 
toGrayValues(ByteBuffer imageBuffer, ByteBuffer grayBuffer)119     private static native boolean toGrayValues(ByteBuffer imageBuffer, ByteBuffer grayBuffer);
120 
121     static {
122         System.loadLibrary("smartcamera_jni");
123     }
124 }
125