Home | History | Annotate | Download | only in java
      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 
     18 package android.filterpacks.imageproc;
     19 
     20 import android.filterfw.core.Filter;
     21 import android.filterfw.core.FilterContext;
     22 import android.filterfw.core.Frame;
     23 import android.filterfw.core.FrameFormat;
     24 import android.filterfw.core.GenerateFieldPort;
     25 import android.filterfw.core.KeyValueMap;
     26 import android.filterfw.core.NativeProgram;
     27 import android.filterfw.core.NativeFrame;
     28 import android.filterfw.core.Program;
     29 import android.filterfw.core.ShaderProgram;
     30 import android.filterfw.format.ImageFormat;
     31 
     32 import android.util.Log;
     33 
     34 import java.lang.Math;
     35 import java.util.Set;
     36 
     37 /**
     38  * @hide
     39  */
     40 public class FisheyeFilter extends Filter {
     41     private static final String TAG = "FisheyeFilter";
     42 
     43     // This parameter has range between 0 and 1. It controls the effect of radial distortion.
     44     // The larger the value, the more prominent the distortion effect becomes (a straight line
     45     // becomes a curve).
     46     @GenerateFieldPort(name = "scale", hasDefault = true)
     47     private float mScale = 0f;
     48 
     49     @GenerateFieldPort(name = "tile_size", hasDefault = true)
     50     private int mTileSize = 640;
     51 
     52     private Program mProgram;
     53 
     54     private int mWidth = 0;
     55     private int mHeight = 0;
     56     private int mTarget = FrameFormat.TARGET_UNSPECIFIED;
     57 
     58     private static final String mFisheyeShader =
     59             "precision mediump float;\n" +
     60             "uniform sampler2D tex_sampler_0;\n" +
     61             "uniform vec2 center;\n" +
     62             "uniform float alpha;\n" +
     63             "uniform float bound;\n" +
     64             "uniform float radius2;\n" +
     65             "uniform float factor;\n" +
     66             "uniform float inv_height;\n" +
     67             "uniform float inv_width;\n" +
     68             "varying vec2 v_texcoord;\n" +
     69             "void main() {\n" +
     70             "  const float m_pi_2 = 1.570963;\n" +
     71             "  float dist = distance(gl_FragCoord.xy, center);\n" +
     72             "  float radian = m_pi_2 - atan(alpha * sqrt(radius2 - dist * dist), dist);\n" +
     73             "  float scale = radian * factor / dist;\n" +
     74             "  vec2 new_coord = gl_FragCoord.xy * scale + (1.0 - scale) * center;\n" +
     75             "  new_coord.x *= inv_width;\n" +
     76             "  new_coord.y *= inv_height;\n" +
     77             "  vec4 color = texture2D(tex_sampler_0, new_coord);\n" +
     78             "  gl_FragColor = color;\n" +
     79             "}\n";
     80 
     81     public FisheyeFilter(String name) {
     82         super(name);
     83     }
     84 
     85     @Override
     86     public void setupPorts() {
     87         addMaskedInputPort("image", ImageFormat.create(ImageFormat.COLORSPACE_RGBA));
     88         addOutputBasedOnInput("image", "image");
     89     }
     90 
     91     @Override
     92     public FrameFormat getOutputFormat(String portName, FrameFormat inputFormat) {
     93         return inputFormat;
     94     }
     95 
     96     public void initProgram(FilterContext context, int target) {
     97         switch (target) {
     98             case FrameFormat.TARGET_GPU:
     99                 ShaderProgram shaderProgram = new ShaderProgram(context, mFisheyeShader);
    100                 shaderProgram.setMaximumTileSize(mTileSize);
    101                 mProgram = shaderProgram;
    102                 break;
    103 
    104             default:
    105                 throw new RuntimeException("Filter FisheyeFilter does not support frames of " +
    106                     "target " + target + "!");
    107         }
    108         mTarget = target;
    109     }
    110 
    111     @Override
    112     public void process(FilterContext context) {
    113         // Get input frame
    114         Frame input = pullInput("image");
    115         FrameFormat inputFormat = input.getFormat();
    116 
    117         // Create output frame
    118         Frame output = context.getFrameManager().newFrame(inputFormat);
    119 
    120         // Create program if not created already
    121         if (mProgram == null || inputFormat.getTarget() != mTarget) {
    122             initProgram(context, inputFormat.getTarget());
    123         }
    124 
    125         // Check if the frame size has changed
    126         if (inputFormat.getWidth() != mWidth || inputFormat.getHeight() != mHeight) {
    127             updateFrameSize(inputFormat.getWidth(), inputFormat.getHeight());
    128         }
    129 
    130         // Process
    131         mProgram.process(input, output);
    132 
    133         // Push output
    134         pushOutput("image", output);
    135 
    136         // Release pushed frame
    137         output.release();
    138     }
    139 
    140     @Override
    141     public void fieldPortValueUpdated(String name, FilterContext context) {
    142         if (mProgram != null) {
    143             updateProgramParams();
    144         }
    145     }
    146 
    147     private void updateFrameSize(int width, int height) {
    148         float center[] = {0.5f * width, 0.5f * height};
    149 
    150         mProgram.setHostValue("center", center);
    151         mProgram.setHostValue("inv_width", 1.0f / width);
    152         mProgram.setHostValue("inv_height", 1.0f / height);
    153 
    154         mWidth = width;
    155         mHeight = height;
    156 
    157         updateProgramParams();
    158     }
    159 
    160     private void updateProgramParams() {
    161         final float pi = 3.14159265f;
    162 
    163         float alpha = mScale * 2.0f + 0.75f;
    164         float bound2 = 0.25f * (mWidth * mWidth  + mHeight * mHeight);
    165         float bound = (float) Math.sqrt(bound2);
    166         float radius = 1.15f * bound;
    167         float radius2 = radius * radius;
    168         float max_radian = 0.5f * pi -
    169             (float) Math.atan(alpha / bound * (float) Math.sqrt(radius2 - bound2));
    170         float factor = bound / max_radian;
    171 
    172         mProgram.setHostValue("radius2",radius2);
    173         mProgram.setHostValue("factor", factor);
    174         mProgram.setHostValue("alpha", (float) (mScale * 2.0 + 0.75));
    175     }
    176 
    177 
    178 }
    179