Home | History | Annotate | Download | only in filterfw
      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 
     60     public ToGrayValuesFilter(MffContext context, String name) {
     61         super(context, name);
     62     }
     63 
     64     @Override
     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
     75     protected void onPrepare() {
     76         if (isOpenGLSupported()) {
     77             mShader = new ImageShader(mGrayPackFragment);
     78         }
     79     }
     80 
     81     @Override
     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 
    119     private static native boolean toGrayValues(ByteBuffer imageBuffer, ByteBuffer grayBuffer);
    120 
    121     static {
    122         System.loadLibrary("smartcamera_jni");
    123     }
    124 }
    125