public class BaseHardVideoFilter {
    protected int SIZE_WIDTH;
    protected int SIZE_HEIGHT;
    protected int directionFlag=-1;
    protected ShortBuffer drawIndecesBuffer;

    public void onInit(int VWidth, int VHeight) {
        SIZE_WIDTH = VWidth;
        SIZE_HEIGHT = VHeight;
        drawIndecesBuffer = GLHelper.getDrawIndecesBuffer();
    }

    public void onDraw(final int cameraTexture,final int targetFrameBuffer, final FloatBuffer shapeBuffer, final FloatBuffer textrueBuffer) {
    }

    public void onDestroy() {

    }

    public void onDirectionUpdate(int _directionFlag) {
        this.directionFlag = _directionFlag;
    }
} public class HardVideoGroupFilter extends BaseHardVideoFilter {
    private LinkedList<FilterWrapper> filterWrappers;

    public HardVideoGroupFilter(List<BaseHardVideoFilter> filters) {
        if (filters == null || filters.isEmpty()) {
            throw new IllegalArgumentException("can not create empty GroupFilter");
        }
        filterWrappers = new LinkedList<FilterWrapper>();
        for (BaseHardVideoFilter filter : filters) {
            filterWrappers.add(new FilterWrapper(filter));
        }
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        int i = 0;
        for (FilterWrapper wrapper : filterWrappers) {
            wrapper.filter.onInit(VWidth, VHeight);
            int[] frameBuffer = new int[1];
            int[] frameBufferTexture = new int[1];
            GLESTools.createFrameBuff(frameBuffer,
                    frameBufferTexture,
                    SIZE_WIDTH,
                    SIZE_HEIGHT);
            wrapper.frameBuffer = frameBuffer[0];
            wrapper.frameBufferTexture = frameBufferTexture[0];
            i++;
        }
    }


    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        FilterWrapper preFilterWrapper = null;
        int i = 0;
        int texture;
        for (FilterWrapper wrapper : filterWrappers) {
            if (preFilterWrapper == null) {
                texture = cameraTexture;
            } else {
                texture = preFilterWrapper.frameBufferTexture;
            }
            if (i == (filterWrappers.size() - 1)) {
                wrapper.filter.onDraw(texture, targetFrameBuffer, shapeBuffer, textrueBuffer);
            } else {
                wrapper.filter.onDraw(texture, wrapper.frameBuffer, shapeBuffer, textrueBuffer);
            }
            preFilterWrapper = wrapper;
            i++;
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        for (FilterWrapper wrapper : filterWrappers) {
            wrapper.filter.onDestroy();
            GLES20.glDeleteFramebuffers(1, new int[]{wrapper.frameBuffer}, 0);
            GLES20.glDeleteTextures(1, new int[]{wrapper.frameBufferTexture}, 0);
        }
    }

    @Override
    public void onDirectionUpdate(int _directionFlag) {
        super.onDirectionUpdate(_directionFlag);
        for (FilterWrapper wrapper : filterWrappers) {
            wrapper.filter.onDirectionUpdate(_directionFlag);
        }
    }

    private class FilterWrapper {
        BaseHardVideoFilter filter;
        int frameBuffer;
        int frameBufferTexture;

        FilterWrapper(BaseHardVideoFilter filter) {
            this.filter = filter;
        }
    }
} public class OriginalHardVideoFilter extends BaseHardVideoFilter {
    protected int glProgram;
    protected int glTextureLoc;
    protected int glCamPostionLoc;
    protected int glCamTextureCoordLoc;
    protected String vertexShader_filter = "" +
            "attribute vec4 aCamPosition;\n" +
            "attribute vec2 aCamTextureCoord;\n" +
            "varying vec2 vCamTextureCoord;\n" +
            "void main(){\n" +
            "    gl_Position= aCamPosition;\n" +
            "    vCamTextureCoord = aCamTextureCoord;\n" +
            "}";
    protected String fragmentshader_filter = "" +
            "precision highp float;\n" +
            "varying highp vec2 vCamTextureCoord;\n" +
            "uniform sampler2D uCamTexture;\n" +
            "void main(){\n" +
            "    vec4  color = texture2D(uCamTexture, vCamTextureCoord);\n" +
            "    gl_FragColor = color;\n" +
            "}";

    public OriginalHardVideoFilter(String vertexShaderCode, String fragmentShaderCode) {
        if (vertexShaderCode != null) {
            vertexShader_filter = vertexShaderCode;
        }
        if (fragmentShaderCode != null) {
            fragmentshader_filter = fragmentShaderCode;
        }
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        glProgram = GLESTools.createProgram(vertexShader_filter, fragmentshader_filter);
        GLES20.glUseProgram(glProgram);
        glTextureLoc = GLES20.glGetUniformLocation(glProgram, "uCamTexture");
        glCamPostionLoc = GLES20.glGetAttribLocation(glProgram, "aCamPosition");
        glCamTextureCoordLoc = GLES20.glGetAttribLocation(glProgram, "aCamTextureCoord");
    }


    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, targetFrameBuffer);
        GLES20.glUseProgram(glProgram);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, cameraTexture);
        GLES20.glUniform1i(glTextureLoc, 0);
        GLES20.glEnableVertexAttribArray(glCamPostionLoc);
        GLES20.glEnableVertexAttribArray(glCamTextureCoordLoc);
        shapeBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamPostionLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, shapeBuffer);
        textrueBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamTextureCoordLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, textrueBuffer);
        onPreDraw();
        GLES20.glViewport(0, 0, SIZE_WIDTH, SIZE_HEIGHT);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawIndecesBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, drawIndecesBuffer);
        GLES20.glFinish();
        onAfterDraw();
        GLES20.glDisableVertexAttribArray(glCamPostionLoc);
        GLES20.glDisableVertexAttribArray(glCamTextureCoordLoc);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        GLES20.glUseProgram(0);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    }

    protected void onPreDraw() {

    }

    protected void onAfterDraw() {

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        GLES20.glDeleteProgram(glProgram);
    }
} public class RESConfig {
    public static class FilterMode {
        public static final int HARD = RESCoreParameters.FILTER_MODE_HARD;
        public static final int SOFT = RESCoreParameters.FILTER_MODE_SOFT;
    }

    public static class RenderingMode {
        public static final int NativeWindow = RESCoreParameters.RENDERING_MODE_NATIVE_WINDOW;
        public static final int OpenGLES = RESCoreParameters.RENDERING_MODE_OPENGLES;
    }

    public static class DirectionMode {
        public static final int FLAG_DIRECTION_FLIP_HORIZONTAL = RESCoreParameters.FLAG_DIRECTION_FLIP_HORIZONTAL;
        public static final int FLAG_DIRECTION_FLIP_VERTICAL = RESCoreParameters.FLAG_DIRECTION_FLIP_VERTICAL;
        public static final int FLAG_DIRECTION_ROATATION_0 = RESCoreParameters.FLAG_DIRECTION_ROATATION_0;
        public static final int FLAG_DIRECTION_ROATATION_90 = RESCoreParameters.FLAG_DIRECTION_ROATATION_90;
        public static final int FLAG_DIRECTION_ROATATION_180 = RESCoreParameters.FLAG_DIRECTION_ROATATION_180;
        public static final int FLAG_DIRECTION_ROATATION_270 = RESCoreParameters.FLAG_DIRECTION_ROATATION_270;
    }

    private int filterMode;
    private Size targetVideoSize;
    private int videoBufferQueueNum;
    private int bitRate;
    private String rtmpAddr;
    private int renderingMode;
    private int defaultCamera;
    private int frontCameraDirectionMode;
    private int backCameraDirectionMode;
    private int videoFPS;
    private int videoGOP;
    private boolean printDetailMsg;

    private RESConfig() {
    }

    public static RESConfig obtain() {
        RESConfig res = new RESConfig();
        res.setFilterMode(FilterMode.SOFT);
        res.setRenderingMode(RenderingMode.NativeWindow);
        res.setTargetVideoSize(new Size(1280, 720));
        res.setVideoFPS(15);
        res.setVideoGOP(1);
        res.setVideoBufferQueueNum(5);
        res.setBitRate(2000000);
        res.setPrintDetailMsg(false);
        res.setDefaultCamera(Camera.CameraInfo.CAMERA_FACING_BACK);
        res.setBackCameraDirectionMode(DirectionMode.FLAG_DIRECTION_ROATATION_0);
        res.setFrontCameraDirectionMode(DirectionMode.FLAG_DIRECTION_ROATATION_0);
        return res;
    }

    /**
     * set the filter mode.
     *
     * @param filterMode {@link FilterMode}
     */
    public void setFilterMode(int filterMode) {
        this.filterMode = filterMode;
    }

    /**
     * set the default camera to start stream
     */
    public void setDefaultCamera(int defaultCamera) {
        this.defaultCamera = defaultCamera;
    }

    /**
     * set front camera rotation & flip
     * @param frontCameraDirectionMode {@link DirectionMode}
     */
    public void setFrontCameraDirectionMode(int frontCameraDirectionMode) {
        this.frontCameraDirectionMode = frontCameraDirectionMode;
    }
    /**
     * set front camera rotation & flip
     * @param backCameraDirectionMode {@link DirectionMode}
     */
    public void setBackCameraDirectionMode(int backCameraDirectionMode) {
        this.backCameraDirectionMode = backCameraDirectionMode;
    }

    /**
     * set  renderingMode when using soft mode<br/>
     * no use for hard mode
     * @param renderingMode {@link RenderingMode}
     */
    public void setRenderingMode(int renderingMode) {
        this.renderingMode = renderingMode;
    }

    /**
     * no use for now
     * @param printDetailMsg
     */
    public void setPrintDetailMsg(boolean printDetailMsg) {
        this.printDetailMsg = printDetailMsg;
    }

    /**
     * set the target video size.<br/>
     * real video size may different from it.Depend on device.
     * @param videoSize
     */
    public void setTargetVideoSize(Size videoSize) {
        targetVideoSize = videoSize;
    }

    /**
     * set video buffer number for soft mode.<br/>
     * num larger:video Smoother,more memory.
     * @param num
     */
    public void setVideoBufferQueueNum(int num) {
        videoBufferQueueNum = num;
    }

    /**
     * set video bitrate
     * @param bitRate
     */
    public void setBitRate(int bitRate) {
        this.bitRate = bitRate;
    }

    public int getVideoFPS() {
        return videoFPS;
    }

    public void setVideoFPS(int videoFPS) {
        this.videoFPS = videoFPS;
    }

    public int getVideoGOP(){
        return videoGOP;
    }

    public void setVideoGOP(int videoGOP){
        this.videoGOP = videoGOP;
    }

    public int getVideoBufferQueueNum() {
        return videoBufferQueueNum;
    }

    public int getBitRate() {
        return bitRate;
    }

    public Size getTargetVideoSize() {
        return targetVideoSize;
    }

    public int getFilterMode() {
        return filterMode;
    }

    public int getDefaultCamera() {
        return defaultCamera;
    }

    public int getBackCameraDirectionMode() {
        return backCameraDirectionMode;
    }

    public int getFrontCameraDirectionMode() {
        return frontCameraDirectionMode;
    }

    public int getRenderingMode() {
        return renderingMode;
    }

    public String getRtmpAddr() {
        return rtmpAddr;
    }

    public void setRtmpAddr(String rtmpAddr) {
        this.rtmpAddr = rtmpAddr;
    }

    public boolean isPrintDetailMsg() {
        return printDetailMsg;
    }
} public class ColorMixHardFilter extends OriginalHardVideoFilter {
    private static String fragmentshader = "" +
            "precision highp float;\n" +
            "varying highp vec2 vCamTextureCoord;\n" +
            "uniform sampler2D uCamTexture;\n" +
            "uniform vec4 mixcolor;" +
            "void main(){\n" +
            "    vec4  color = texture2D(uCamTexture, vCamTextureCoord);\n" +
            "    gl_FragColor = vec4(mix(color.rgb,mixcolor.rgb,mixcolor.a),1.0);\n" +
            "}";

    private int mixColorLoc;
    private float r, g, b, a;

    public ColorMixHardFilter(float r, float g, float b, float a) {
        super(null, fragmentshader);
        this.r = r;
        this.g = g;
        this.b = b;
        this.a = a;
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        mixColorLoc = GLES20.glGetUniformLocation(glProgram, "mixcolor");
    }

    @Override
    protected void onPreDraw() {
        super.onPreDraw();
        GLES20.glUniform4f(mixColorLoc, r, g, b, a);
    }

    public void setMixColor(float r, float g, float b, float a) {
        this.r = r;
        this.g = g;
        this.b = b;
        this.a = a;
    }
} public class DifferenceBlendFilterHard extends TowInputFilterHard {
    private static String FRAGMENTSHADER = "" +
            "#extension GL_OES_EGL_image_external : require\n" +
            "precision highp float;\n" +
            "varying highp vec2 vCamTextureCoord;\n" +
            "varying highp vec2 vImageTextureCoord;\n" +
            "uniform sampler2D uCamTexture;\n" +
            "uniform sampler2D uImageTexture;\n" +
            "void main(){\n" +
            "   lowp vec4 c1 = texture2D(uCamTexture, vCamTextureCoord);\n" +
            "   lowp vec4 c2 = texture2D(uImageTexture, vImageTextureCoord);\n" +
            "   gl_FragColor = vec4(abs(c1.rgb - c2.rgb), 1.0);\n" +
            "}";

    public DifferenceBlendFilterHard(Bitmap image) {
        super(null,FRAGMENTSHADER,image);
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
    }
} public class FishEyeFilterHard extends OriginalHardVideoFilter {
    private static String FRAGMENTSHADER = "" +
            "#extension GL_OES_EGL_image_external : require\n" +
            "precision highp float;\n" +
            "uniform sampler2D uCamTexture;\n" +
            "varying highp vec2 vCamTextureCoord;\n" +
            "const highp float PI = 3.1415926535;\n" +
            "const highp float aperture = 180.0;\n" +
            "const highp float apertureHalf = 0.5 * aperture * (PI / 180.0);\n" +
            "const highp float maxFactor = sin(apertureHalf);\n" +
            "void main(){\n" +
            "    vec2 pos = 2.0 * vCamTextureCoord.st - 1.0;\n" +
            "    float l = length(pos);\n" +
            "    if (l > 1.0) {\n" +
            "      gl_FragColor = vec4(0.0,0.0,0.0,1);\n" +
            "    }\n" +
            "    else {\n" +
            "        float x = maxFactor * pos.x;\n" +
            "        float y = maxFactor * pos.y;\n" +
            "        float n = length(vec2(x, y));\n" +
            "        float z = sqrt(1.0 - n * n);\n" +
            "        float r = atan(n, z) / PI;\n" +
            "        float phi = atan(y, x);\n" +
            "        float u = r * cos(phi) + 0.5;\n" +
            "        float v = r * sin(phi) + 0.5;\n" +
            "       gl_FragColor = texture2D(uCamTexture,vec2(u,v));\n" +
            "    }\n" +
            "}";

    public FishEyeFilterHard() {
        super(null, FRAGMENTSHADER);
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
    }
} public class GaussianBlurHardFilter extends BaseHardVideoFilter {
    private int blurRadius;
    protected int glProgram;
    protected int glTextureLoc;
    protected int glCamPostionLoc;
    protected int glCamTextureCoordLoc;
    protected int glStepLoc;
    protected String vertexShader_filter = "" +
            "attribute vec4 aCamPosition;\n" +
            "attribute vec2 aCamTextureCoord;\n" +
            "varying vec2 vCamTextureCoord;\n" +
            "void main(){\n" +
            "    gl_Position= aCamPosition;\n" +
            "    vCamTextureCoord = aCamTextureCoord;\n" +
            "}";
    protected String fragmentshader_filter = "" +
            "#ifdef GAUSSIAN_BLUR_RADIUS\n" +
            "#define RADIUS GAUSSIAN_BLUR_RADIUS\n" +
            "#else\n" +
            "#define RADIUS 10.\n" +
            "#endif\n" +
            "\n" +
            "precision highp float;\n" +
            "varying vec2 vCamTextureCoord;\n" +
            "uniform sampler2D uCamTexture;\n" +
            "uniform vec2 step;\n" +
            "const int blurRadius = 10;\n" +
            "void main(){\n" +
            "\tvec2 currPos = vCamTextureCoord - (step*RADIUS);\n" +
            "\tvec2 endPos = vCamTextureCoord + (step*RADIUS);\n" +
            "\tvec3 sum=vec3(0.,0.,0.);\n" +
            "    float fact=0.;\n" +
            "    for(float r=-RADIUS;r<=RADIUS;r+=1.)\n" +
            "    {\n" +
            "    \tif(any(lessThanEqual(currPos,vec2(0.,0.)))||any(greaterThanEqual(currPos,vec2(1.,1.))))\n" +
            "    \t{\n" +
            "    \t\tcurrPos+=step;\n" +
            "    \t\tcontinue;\n" +
            "    \t}\n" +
            "    \tfloat gauss = exp(r*r / (-4.*RADIUS));\n" +
            "        sum += pow(texture2D(uCamTexture,currPos).rgb,vec3(2.,2.,2.))*gauss;\n" +
            "        fact += gauss;\n" +
            "        currPos+=step;\n" +
            "    }\n" +
            "    gl_FragColor = vec4(sqrt(sum/fact),1.);\n" +
            "}";
    int[] frameBuffer = new int[1];
    int[] frameBufferTexture = new int[1];

    public GaussianBlurHardFilter(int blurRadius) {
        this.blurRadius = blurRadius;
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        String fragShader = "#define GAUSSIAN_BLUR_RADIUS "+blurRadius+".\n"+fragmentshader_filter;
        glProgram = GLESTools.createProgram(vertexShader_filter, fragShader);
        GLES20.glUseProgram(glProgram);
        glTextureLoc = GLES20.glGetUniformLocation(glProgram, "uCamTexture");
        glCamPostionLoc = GLES20.glGetAttribLocation(glProgram, "aCamPosition");
        glCamTextureCoordLoc = GLES20.glGetAttribLocation(glProgram, "aCamTextureCoord");
        glStepLoc = GLES20.glGetUniformLocation(glProgram,"step");
        GLESTools.createFrameBuff(frameBuffer,
                frameBufferTexture,
                SIZE_WIDTH,
                SIZE_HEIGHT);
    }

    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer[0]);
        GLES20.glUseProgram(glProgram);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, cameraTexture);
        GLES20.glUniform1i(glTextureLoc, 0);
        GLES20.glUniform2f(glStepLoc,1f/SIZE_WIDTH,0f);
        GLES20.glEnableVertexAttribArray(glCamPostionLoc);
        GLES20.glEnableVertexAttribArray(glCamTextureCoordLoc);
        shapeBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamPostionLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, shapeBuffer);
        textrueBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamTextureCoordLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, textrueBuffer);
        GLES20.glViewport(0, 0, SIZE_WIDTH, SIZE_HEIGHT);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawIndecesBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, drawIndecesBuffer);
        GLES20.glFinish();
        //=========================
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, targetFrameBuffer);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameBufferTexture[0]);
        GLES20.glUniform1i(glTextureLoc, 0);
        GLES20.glUniform2f(glStepLoc,0f,1f/SIZE_HEIGHT);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawIndecesBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, drawIndecesBuffer);
        GLES20.glFinish();
        GLES20.glDisableVertexAttribArray(glCamPostionLoc);
        GLES20.glDisableVertexAttribArray(glCamTextureCoordLoc);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        GLES20.glUseProgram(0);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        GLES20.glDeleteProgram(glProgram);
        GLES20.glDeleteFramebuffers(1, frameBuffer, 0);
        GLES20.glDeleteTextures(1, frameBufferTexture, 0);
    }
} public class IconHardFilter extends BaseHardVideoFilter {
    protected int glProgram;
    protected int glCamTextureLoc;
    protected int glCamPostionLoc;
    protected int glCamTextureCoordLoc;
    protected int glImageTextureLoc;
    protected int glImageRectLoc;
    protected String vertexShader_filter = "" +
            "attribute vec4 aCamPosition;\n" +
            "attribute vec2 aCamTextureCoord;\n" +
            "varying vec2 vCamTextureCoord;\n" +
            "void main(){\n" +
            "   gl_Position= aCamPosition;\n" +
            "   vCamTextureCoord = aCamTextureCoord;\n" +
            "}";
    protected String fragmentshader_filter = "" +
            "precision highp float;\n" +
            "varying highp vec2 vCamTextureCoord;\n" +
            "uniform sampler2D uCamTexture;\n" +
            "uniform sampler2D uImageTexture;\n" +
            "uniform vec4 imageRect;\n" +
            "void main(){\n" +
            "   lowp vec4 c1 = texture2D(uCamTexture, vCamTextureCoord);\n" +
            "   lowp vec2 vCamTextureCoord2 = vec2(vCamTextureCoord.x,1.0-vCamTextureCoord.y);\n" +
            "   if(vCamTextureCoord2.x>imageRect.r && vCamTextureCoord2.x<imageRect.b && vCamTextureCoord2.y>imageRect.g && vCamTextureCoord2.y<imageRect.a)\n" +
            "   {\n" +
            "        vec2 imagexy = vec2((vCamTextureCoord2.x-imageRect.r)/(imageRect.b-imageRect.r),(vCamTextureCoord2.y-imageRect.g)/(imageRect.a-imageRect.g));\n" +
            "        lowp vec4 c2 = texture2D(uImageTexture, imagexy);\n" +
            "        lowp vec4 outputColor = c2+c1*c1.a*(1.0-c2.a);\n" +
            "        outputColor.a = 1.0;\n" +
            "        gl_FragColor = outputColor;\n" +
            "   }else\n" +
            "   {\n" +
            "       gl_FragColor = c1;\n" +
            "   }\n" +
            "}";
    protected int imageTexture = GLESTools.NO_TEXTURE;

    protected final Object syncBitmap = new Object();
    protected Bitmap iconBitmap;
    protected boolean needUpdate;
    protected RectF iconRectF;
    protected Rect iconRect;

    public IconHardFilter(@NonNull Bitmap _bitmap, @NonNull Rect _rect) {
        iconBitmap = _bitmap;
        needUpdate = true;
        iconRectF = new RectF();
        iconRect = _rect;
    }

    protected IconHardFilter() {
        iconBitmap = null;
        needUpdate = false;
        iconRectF = new RectF(0,0,0,0);
    }

    public void updateIcon(Bitmap _bitmap, Rect _rect) {
        synchronized (syncBitmap) {
            if (_bitmap != null) {
                iconBitmap = _bitmap;
                needUpdate = true;
            }
            if (_rect != null) {
                iconRect = _rect;
            }
        }
    }


    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        glProgram = GLESTools.createProgram(vertexShader_filter, fragmentshader_filter);
        GLES20.glUseProgram(glProgram);
        glCamTextureLoc = GLES20.glGetUniformLocation(glProgram, "uCamTexture");
        glImageTextureLoc = GLES20.glGetUniformLocation(glProgram, "uImageTexture");
        glCamPostionLoc = GLES20.glGetAttribLocation(glProgram, "aCamPosition");
        glCamTextureCoordLoc = GLES20.glGetAttribLocation(glProgram, "aCamTextureCoord");
        glImageRectLoc = GLES20.glGetUniformLocation(glProgram, "imageRect");

    }

    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        synchronized (syncBitmap) {
            if (needUpdate) {
                if (imageTexture != GLESTools.NO_TEXTURE) {
                    GLES20.glDeleteTextures(1, new int[]{imageTexture}, 0);
                }
                imageTexture = GLESTools.loadTexture(iconBitmap, GLESTools.NO_TEXTURE);
            }
        }
        iconRectF.top = iconRect.top / (float) SIZE_HEIGHT;
        iconRectF.bottom = iconRect.bottom / (float) SIZE_HEIGHT;
        iconRectF.left = iconRect.left / (float) SIZE_WIDTH;
        iconRectF.right = iconRect.right / (float) SIZE_WIDTH;
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, targetFrameBuffer);
        GLES20.glUseProgram(glProgram);
        GLES20.glUniform4f(glImageRectLoc, iconRectF.left, iconRectF.top, iconRectF.right, iconRectF.bottom);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, cameraTexture);
        GLES20.glUniform1i(glCamTextureLoc, 0);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imageTexture);
        GLES20.glUniform1i(glImageTextureLoc, 1);
        GLES20.glEnableVertexAttribArray(glCamPostionLoc);
        GLES20.glEnableVertexAttribArray(glCamTextureCoordLoc);
        shapeBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamPostionLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, shapeBuffer);
        textrueBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamTextureCoordLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, textrueBuffer);
        GLES20.glViewport(0, 0, SIZE_WIDTH, SIZE_HEIGHT);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawIndecesBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, drawIndecesBuffer);
        GLES20.glFinish();
        GLES20.glDisableVertexAttribArray(glCamPostionLoc);
        GLES20.glDisableVertexAttribArray(glCamTextureCoordLoc);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        GLES20.glUseProgram(0);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        GLES20.glDeleteProgram(glProgram);
        GLES20.glDeleteTextures(1, new int[]{imageTexture}, 0);
    }
} public class SeaScapeFilter extends BaseHardVideoFilter {
    protected int glProgram;
    protected int iGlobalTimeLoc;
    protected int iResolutionLoc;
    protected int glCamPostionLoc;
    protected int glCamTextureCoordLoc;
    protected String vertexShader_filter = "" +
            "attribute vec4 aCamPosition; \n" +
            "attribute vec2 aCamTextureCoord; \n" +
            "varying vec2 fragCoord;\n" +
            "void main(){ \n" +
            "    gl_Position= aCamPosition; \n" +
            "    fragCoord = aCamTextureCoord;\n" +
            "}";
    protected String fragmentshader_filter = "" +
            "precision highp float;\n" +
            "varying highp vec2 fragCoord;\n" +
            "uniform vec2 iResolution;\n" +
            "uniform float iGlobalTime;\n" +
            "const int NUM_STEPS = 8;\n" +
            "const float PI\t \t= 3.1415;\n" +
            "const float EPSILON\t= 1e-3;\n" +
            "float EPSILON_NRM;\n" +
            "\n" +
            "// sea\n" +
            "const int ITER_GEOMETRY = 3;\n" +
            "const int ITER_FRAGMENT = 5;\n" +
            "const float SEA_HEIGHT = 0.6;\n" +
            "const float SEA_CHOPPY = 4.0;\n" +
            "const float SEA_SPEED = 0.8;\n" +
            "const float SEA_FREQ = 0.16;\n" +
            "const vec3 SEA_BASE = vec3(0.1,0.19,0.22);\n" +
            "const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);\n" +
            "float SEA_TIME;\n" +
            "mat2 octave_m = mat2(1.6,1.2,-1.2,1.6);\n" +
            "\n" +
            "// math\n" +
            "mat3 fromEuler(vec3 ang) {\n" +
            "\tvec2 a1 = vec2(sin(ang.x),cos(ang.x));\n" +
            "    vec2 a2 = vec2(sin(ang.y),cos(ang.y));\n" +
            "    vec2 a3 = vec2(sin(ang.z),cos(ang.z));\n" +
            "    mat3 m;\n" +
            "    m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);\n" +
            "\tm[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);\n" +
            "\tm[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);\n" +
            "\treturn m;\n" +
            "}\n" +
            "float hash( vec2 p ) {\n" +
            "\tfloat h = dot(p,vec2(127.1,311.7));\n" +
            "    return fract(sin(h)*43758.5453123);\n" +
            "}\n" +
            "float noise( in vec2 p ) {\n" +
            "    vec2 i = floor( p );\n" +
            "    vec2 f = fract( p );\n" +
            "\tvec2 u = f*f*(3.0-2.0*f);\n" +
            "    return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ),\n" +
            "                     hash( i + vec2(1.0,0.0) ), u.x),\n" +
            "                mix( hash( i + vec2(0.0,1.0) ),\n" +
            "                     hash( i + vec2(1.0,1.0) ), u.x), u.y);\n" +
            "}\n" +
            "\n" +
            "// lighting\n" +
            "float diffuse(vec3 n,vec3 l,float p) {\n" +
            "    return pow(dot(n,l) * 0.4 + 0.6,p);\n" +
            "}\n" +
            "float specular(vec3 n,vec3 l,vec3 e,float s) {\n" +
            "    float nrm = (s + 8.0) / (3.1415 * 8.0);\n" +
            "    return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;\n" +
            "}\n" +
            "\n" +
            "// sky\n" +
            "vec3 getSkyColor(vec3 e) {\n" +
            "    e.y = max(e.y,0.0);\n" +
            "    vec3 ret;\n" +
            "    ret.x = pow(1.0-e.y,2.0);\n" +
            "    ret.y = 1.0-e.y;\n" +
            "    ret.z = 0.6+(1.0-e.y)*0.4;\n" +
            "    return ret;\n" +
            "}\n" +
            "\n" +
            "// sea\n" +
            "float sea_octave(vec2 uv, float choppy) {\n" +
            "    uv += noise(uv);\n" +
            "    vec2 wv = 1.0-abs(sin(uv));\n" +
            "    vec2 swv = abs(cos(uv));\n" +
            "    wv = mix(wv,swv,wv);\n" +
            "    return pow(1.0-pow(wv.x * wv.y,0.65),choppy);\n" +
            "}\n" +
            "\n" +
            "float map(vec3 p) {\n" +
            "    float freq = SEA_FREQ;\n" +
            "    float amp = SEA_HEIGHT;\n" +
            "    float choppy = SEA_CHOPPY;\n" +
            "    vec2 uv = p.xz; uv.x *= 0.75;\n" +
            "\n" +
            "    float d, h = 0.0;\n" +
            "    for(int i = 0; i < ITER_GEOMETRY; i++) {\n" +
            "    \td = sea_octave((uv+SEA_TIME)*freq,choppy);\n" +
            "    \td += sea_octave((uv-SEA_TIME)*freq,choppy);\n" +
            "        h += d * amp;\n" +
            "    \tuv *= octave_m; freq *= 1.9; amp *= 0.22;\n" +
            "        choppy = mix(choppy,1.0,0.2);\n" +
            "    }\n" +
            "    return p.y - h;\n" +
            "}\n" +
            "\n" +
            "float map_detailed(vec3 p) {\n" +
            "    float freq = SEA_FREQ;\n" +
            "    float amp = SEA_HEIGHT;\n" +
            "    float choppy = SEA_CHOPPY;\n" +
            "    vec2 uv = p.xz; uv.x *= 0.75;\n" +
            "\n" +
            "    float d, h = 0.0;\n" +
            "    for(int i = 0; i < ITER_FRAGMENT; i++) {\n" +
            "    \td = sea_octave((uv+SEA_TIME)*freq,choppy);\n" +
            "    \td += sea_octave((uv-SEA_TIME)*freq,choppy);\n" +
            "        h += d * amp;\n" +
            "    \tuv *= octave_m; freq *= 1.9; amp *= 0.22;\n" +
            "        choppy = mix(choppy,1.0,0.2);\n" +
            "    }\n" +
            "    return p.y - h;\n" +
            "}\n" +
            "\n" +
            "vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {\n" +
            "    float fresnel = 1.0 - max(dot(n,-eye),0.0);\n" +
            "    fresnel = pow(fresnel,3.0) * 0.65;\n" +
            "\n" +
            "    vec3 reflected = getSkyColor(reflect(eye,n));\n" +
            "    vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12;\n" +
            "\n" +
            "    vec3 color = mix(refracted,reflected,fresnel);\n" +
            "\n" +
            "    float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);\n" +
            "    color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;\n" +
            "\n" +
            "    color += vec3(specular(n,l,eye,60.0));\n" +
            "\n" +
            "    return color;\n" +
            "}\n" +
            "\n" +
            "// tracing\n" +
            "vec3 getNormal(vec3 p, float eps) {\n" +
            "    vec3 n;\n" +
            "    n.y = map_detailed(p);\n" +
            "    n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y;\n" +
            "    n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y;\n" +
            "    n.y = eps;\n" +
            "    return normalize(n);\n" +
            "}\n" +
            "\n" +
            "float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) {\n" +
            "    float tm = 0.0;\n" +
            "    float tx = 1000.0;\n" +
            "    float hx = map(ori + dir * tx);\n" +
            "    if(hx > 0.0) return tx;\n" +
            "    float hm = map(ori + dir * tm);\n" +
            "    float tmid = 0.0;\n" +
            "    for(int i = 0; i < NUM_STEPS; i++) {\n" +
            "        tmid = mix(tm,tx, hm/(hm-hx));\n" +
            "        p = ori + dir * tmid;\n" +
            "    \tfloat hmid = map(p);\n" +
            "\t\tif(hmid < 0.0) {\n" +
            "        \ttx = tmid;\n" +
            "            hx = hmid;\n" +
            "        } else {\n" +
            "            tm = tmid;\n" +
            "            hm = hmid;\n" +
            "        }\n" +
            "    }\n" +
            "    return tmid;\n" +
            "}\n" +
            "\n" +
            "// main\n" +
            "void main() {\n" +
            "    SEA_TIME =  iGlobalTime * SEA_SPEED;\n" +
            "    EPSILON_NRM\t= 0.1 / iResolution.x;\n" +
            "    vec2 uv = fragCoord.xy;\n" +
            "    uv = uv * 2.0 - 1.0;\n" +
            "    uv.x *= iResolution.x / iResolution.y;\n" +
            "    float time = iGlobalTime * 0.3;\n" +
            "\n" +
            "    // ray\n" +
            "    vec3 ang = vec3(sin(time*3.0)*0.1,sin(time)*0.2+0.3,time);\n" +
            "    vec3 ori = vec3(0.0,3.5,time*5.0);\n" +
            "    vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z += length(uv) * 0.15;\n" +
            "    dir = normalize(dir) * fromEuler(ang);\n" +
            "\n" +
            "    // tracing\n" +
            "    vec3 p;\n" +
            "    heightMapTracing(ori,dir,p);\n" +
            "    vec3 dist = p - ori;\n" +
            "    vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);\n" +
            "    vec3 light = normalize(vec3(0.0,1.0,0.8));\n" +
            "\n" +
            "    // color\n" +
            "    vec3 color = mix(\n" +
            "        getSkyColor(dir),\n" +
            "        getSeaColor(p,n,light,dir,dist),\n" +
            "    \tpow(smoothstep(0.0,-0.05,dir.y),0.3));\n" +
            "\n" +
            "    // post\n" +
            "\tgl_FragColor = vec4(pow(color,vec3(0.75)), 1.0);\n" +
            "}";
    protected long startTime;

    public SeaScapeFilter() {
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        glProgram = GLESTools.createProgram(vertexShader_filter, fragmentshader_filter);
        GLES20.glUseProgram(glProgram);
        glCamPostionLoc = GLES20.glGetAttribLocation(glProgram, "aCamPosition");
        glCamTextureCoordLoc = GLES20.glGetAttribLocation(glProgram, "aCamTextureCoord");
        iGlobalTimeLoc = GLES20.glGetUniformLocation(glProgram, "iGlobalTime");
        iResolutionLoc = GLES20.glGetUniformLocation(glProgram, "iResolution");
    }


    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        if (startTime == 0) {
            startTime = System.currentTimeMillis();
        }
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, targetFrameBuffer);
        GLES20.glUseProgram(glProgram);
        GLES20.glUniform2f(iResolutionLoc,(float)SIZE_WIDTH,(float)SIZE_HEIGHT);
        GLES20.glUniform1f(iGlobalTimeLoc, (float) (System.currentTimeMillis() - startTime) / 1000.0f);
        GLES20.glEnableVertexAttribArray(glCamPostionLoc);
        GLES20.glEnableVertexAttribArray(glCamTextureCoordLoc);
        shapeBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamPostionLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, shapeBuffer);
        textrueBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamTextureCoordLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, textrueBuffer);
        GLES20.glViewport(0, 0, SIZE_WIDTH, SIZE_HEIGHT);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawIndecesBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, drawIndecesBuffer);
        GLES20.glFinish();
        GLES20.glDisableVertexAttribArray(glCamPostionLoc);
        GLES20.glDisableVertexAttribArray(glCamTextureCoordLoc);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        GLES20.glUseProgram(0);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        GLES20.glDeleteProgram(glProgram);
    }
} public class SkinBlurHardVideoFilter extends OriginalHardVideoFilter {
    private static String FRAGMENTSHADER = "" +
            "precision highp float;\n" +
            "uniform sampler2D uCamTexture;\n" +
            "varying highp vec2 vCamTextureCoord;\n" +
            "const float maxdelta = 0.08;\n" +
            "uniform highp float xStep;\n" +
            "uniform highp float yStep;\n" +
            "const highp mat3 rgb2yuv = mat3(0.299,-0.147,0.615,0.587,-0.289,-0.515,0.114,0.436,-0.1);\n" +
            "const highp mat3 gaussianMap = mat3(0.142,0.131,0.104,0.131,0.122,0.096,0.104,0.096,0.075);\n" +
            "void main(){\n" +
            "    vec4 color = texture2D(uCamTexture,vCamTextureCoord);\n" +
            "    vec3 yuv = rgb2yuv*color.rgb;\n" +
            "    if(yuv.g<-0.225 || yuv.g>0.0 || yuv.b<0.022 || yuv.b>0.206){\n" +
            "        gl_FragColor = color;\n" +
            "        return;\n" +
            "    }\n" +
            "    float xfS = vCamTextureCoord.x - xStep*2.0;\n" +
            "    float yf = vCamTextureCoord.y - yStep*2.0;\n" +
            "    int x,y;\n" +
            "    float xf=xfS;\n" +
            "    vec4 sum=vec4(0.0,0.0,0.0,0.0);\n" +
            "    vec4 fact=vec4(0.0,0.0,0.0,0.0);\n" +
            "    vec4 tmp;\n" +
            "    vec4 color2;\n" +
            "    float gauss;\n" +
            "    for(y=-2;y<3;y+=1){\n" +
            "        if (yf < 0.0 || yf > 1.0){\n" +
            "            yf+=yStep;\n" +
            "            continue;\n" +
            "        }\n" +
            "        for(x=-2;x<3;x+=1){\n" +
            "            if (xf < 0.0 || xf > 1.0){\n" +
            "                xf+=xStep;\n" +
            "                continue;\n" +
            "            }\n" +
            "            color2 = texture2D(uCamTexture,vec2(xf,yf));\n" +
            "            tmp = color - color2;\n" +
            "            gauss = gaussianMap[x<0?-x:x][y<0?-y:y];\n" +
            "            if (abs(tmp.r) < maxdelta){\n" +
            "                sum.r += (color2.r*gauss);\n" +
            "                fact.r +=gauss;\n" +
            "            }\n" +
            "            if (abs(tmp.g) < maxdelta){\n" +
            "                sum.g += color2.g*gauss;\n" +
            "                fact.g +=gauss;\n" +
            "            }\n" +
            "            if (abs(tmp.b) < maxdelta){\n" +
            "                sum.b += color2.b*gauss;\n" +
            "                fact.b +=gauss;\n" +
            "            }\n" +
            "            xf+=xStep;\n" +
            "        }\n" +
            "        yf+=yStep;\n" +
            "        xf=xfS;\n" +
            "    }\n" +
            "    vec4 res = sum/fact;\n" +
            "    if(fact.r<1.0){\n" +
            "        tmp.r = color.r;\n" +
            "    }else{\n" +
            "        tmp.r = res.r;\n" +
            "    }\n" +
            "    if(fact.g<1.0){\n" +
            "        tmp.g = color.g;\n" +
            "    }else{\n" +
            "        tmp.g = res.g;\n" +
            "    }\n" +
            "    if(fact.b<1.0){\n" +
            "        tmp.b = color.b;\n" +
            "    }else{\n" +
            "        tmp.b = res.b;\n" +
            "    }\n" +
            "    gl_FragColor = vec4(tmp.rgb,1.0);\n" +
            "}\n";


    private int xStepLoc;
    private int yStepLoc;
    private float stepScale;

    /**
     * @param stepScale suggest:480P = 2,720P = 3
     */
    public SkinBlurHardVideoFilter(int stepScale) {
        super(null, FRAGMENTSHADER);
        this.stepScale = (float) stepScale;
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        yStepLoc = GLES20.glGetUniformLocation(glProgram, "yStep");
        xStepLoc = GLES20.glGetUniformLocation(glProgram, "xStep");
    }

    @Override
    protected void onPreDraw() {
        super.onPreDraw();
        GLES20.glUniform1f(xStepLoc, (float) (stepScale / SIZE_WIDTH));
        GLES20.glUniform1f(yStepLoc, (float) (stepScale / SIZE_HEIGHT));
    }
} public class SobelEdgeDetectionHardVideoFilter extends Base3x3SamplingHardVideoFilter {
    public static final String FRAGMENTSHADER = "" +
            "#extension GL_OES_EGL_image_external : require\n" +
            "precision highp float;\n" +
            "\n" +
            "varying vec2 vCamTextureCoord;\n" +
            "varying vec2 leftTextureCoordinate;\n" +
            "varying vec2 rightTextureCoordinate;\n" +
            "\n" +
            "varying vec2 topTextureCoordinate;\n" +
            "varying vec2 topLeftTextureCoordinate;\n" +
            "varying vec2 topRightTextureCoordinate;\n" +
            "\n" +
            "varying vec2 bottomTextureCoordinate;\n" +
            "varying vec2 bottomLeftTextureCoordinate;\n" +
            "varying vec2 bottomRightTextureCoordinate;\n" +
            "\n" +
            "uniform sampler2D uCamTexture;\n" +
            "\n" +
            "void main()\n" +
            "{\n" +
            "    float bottomLeftIntensity = texture2D(uCamTexture, bottomLeftTextureCoordinate).r;\n" +
            "    float topRightIntensity = texture2D(uCamTexture, topRightTextureCoordinate).r;\n" +
            "    float topLeftIntensity = texture2D(uCamTexture, topLeftTextureCoordinate).r;\n" +
            "    float bottomRightIntensity = texture2D(uCamTexture, bottomRightTextureCoordinate).r;\n" +
            "    float leftIntensity = texture2D(uCamTexture, leftTextureCoordinate).r;\n" +
            "    float rightIntensity = texture2D(uCamTexture, rightTextureCoordinate).r;\n" +
            "    float bottomIntensity = texture2D(uCamTexture, bottomTextureCoordinate).r;\n" +
            "    float topIntensity = texture2D(uCamTexture, topTextureCoordinate).r;\n" +
            "    float h = -topLeftIntensity - 2.0 * topIntensity - topRightIntensity + bottomLeftIntensity + 2.0 * bottomIntensity + bottomRightIntensity;\n" +
            "    float v = -bottomLeftIntensity - 2.0 * leftIntensity - topLeftIntensity + bottomRightIntensity + 2.0 * rightIntensity + topRightIntensity;\n" +
            "    float mag = length(vec2(h, v));\n" +
            "    gl_FragColor = vec4(vec3(mag), 1.0);\n" +
            "}";

    public SobelEdgeDetectionHardVideoFilter() {
        super(null, FRAGMENTSHADER);
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
    }
} public class TextHardFilter extends BaseHardVideoFilter {
    private CharSequence text;
    private int textColor=Color.WHITE;
    private int textSize=30;
    private boolean textNeedUpdate;
    private boolean postionNeedUpdate;

    protected int imageTexture = GLESTools.NO_TEXTURE;
    protected Bitmap textBitmap;

    private Rect textRect =new Rect(0, 0, 0, 0);
    private int gravity;
    private int verticalMargin;
    private int horizontalMargin;

    public TextHardFilter(CharSequence text, int textColor, int textSize) {
        setText(text, textColor, textSize);
    }

    public TextHardFilter(@Nullable CharSequence text) {
        setText(text);
    }

    /**
     *  update text.
     * @param charSequence html,spannablestring supported.
     * @param textColor if (textColor == Color.TRANSPARENT), The text color will become inverted.
     * @param textSize text size in pixel
     */
    public void setText(CharSequence charSequence, @ColorInt int textColor, int textSize) {
        this.text = charSequence;
        this.textColor = textColor;
        this.textSize = textSize;
        textNeedUpdate = true;
        postionNeedUpdate = true;
    }

    public void setText(CharSequence charSequence) {
        setText(charSequence, textColor, textSize);
    }

    public void setPostion(int gravity, int verticalMargin, int horizontalMargin) {
        this.gravity = gravity;
        if (this.verticalMargin != verticalMargin || this.horizontalMargin != horizontalMargin) {
            textNeedUpdate = true;
            this.verticalMargin = verticalMargin;
            this.horizontalMargin = horizontalMargin;
        }
        postionNeedUpdate = true;
    }

    private void updateText() {
        if (textNeedUpdate) {
            int w = 0, h = 0;
            TextPaint textPaint = new TextPaint();
            if (textColor == Color.TRANSPARENT) {
                textPaint.setColor(Color.BLACK);
            } else {
                textPaint.setColor(textColor);
            }
            textPaint.setTextSize(textSize);
            textPaint.setAntiAlias(true);
            StaticLayout innerStaticLayout = new StaticLayout(text,
                    textPaint,
                    SIZE_WIDTH - horizontalMargin,
                    Layout.Alignment.ALIGN_NORMAL,
                    1.0f,//相对行间距,字体高度倍数
                    0.0f,//在基础行距上添加多少
                    false);
            int lineCount = innerStaticLayout.getLineCount();
            for (int i = 0; i < lineCount; i++) {
                float lineW = innerStaticLayout.getLineWidth(i);
                if (lineW > w) {
                    w = (int) (lineW + 0.5f);
                }
            }
            h = innerStaticLayout.getHeight();
            Canvas innerCanvas;
            if (textBitmap == null || textBitmap.getWidth() != w || textBitmap.getHeight() != h) {
                textBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
                if (imageTexture != GLESTools.NO_TEXTURE) {
                    GLES20.glDeleteTextures(1, new int[]{imageTexture}, 0);
                }
                imageTexture = GLESTools.NO_TEXTURE;
            }
            innerCanvas = new Canvas(textBitmap);
            innerCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
            innerStaticLayout.draw(innerCanvas);
            imageTexture = GLESTools.loadTexture(textBitmap, imageTexture);
            textNeedUpdate = false;
        }
        if (postionNeedUpdate) {
            updatePostion();
            postionNeedUpdate = false;
        }
    }


    protected int glProgram;
    protected int glCamTextureLoc;
    protected int glCamPostionLoc;
    protected int glCamTextureCoordLoc;
    protected int glImageTextureLoc;
    protected int glImageRectLoc;
    protected int glInverseColorLoc;
    protected String vertexShader_filter = "" +
            "attribute vec4 aCamPosition;\n" +
            "attribute vec2 aCamTextureCoord;\n" +
            "varying vec2 vCamTextureCoord;\n" +
            "void main(){\n" +
            "   gl_Position= aCamPosition;\n" +
            "   vCamTextureCoord = aCamTextureCoord;\n" +
            "}";
    protected String fragmentshader_filter = "" +
            "precision highp float;\n" +
            "varying highp vec2 vCamTextureCoord;\n" +
            "uniform sampler2D uCamTexture;\n" +
            "uniform sampler2D uImageTexture;\n" +
            "uniform vec4 imageRect;\n" +
            "uniform int inverseColor;\n" +
            "void main(){\n" +
            "    lowp vec4 c1 = texture2D(uCamTexture, vCamTextureCoord);\n" +
            "    lowp vec2 vCamTextureCoord2 = vec2(vCamTextureCoord.x,1.0-vCamTextureCoord.y);\n" +
            "    if(vCamTextureCoord2.x>imageRect.r && vCamTextureCoord2.x<imageRect.b && vCamTextureCoord2.y>imageRect.g && vCamTextureCoord2.y<imageRect.a)\n" +
            "    {\n" +
            "        vec2 imagexy = vec2((vCamTextureCoord2.x-imageRect.r)/(imageRect.b-imageRect.r),(vCamTextureCoord2.y-imageRect.g)/(imageRect.a-imageRect.g));\n" +
            "        lowp vec4 c2 = texture2D(uImageTexture, imagexy);\n" +
            "        if(inverseColor==1){\n" +
            "            if(c2.a==0.0){\n" +
            "                gl_FragColor = c1;\n" +
            "            }else{\n" +
            "                gl_FragColor = vec4(c1.r-c2.a*2.0*(c1.r-0.5),\n" +
            "                    c1.g-c2.a*2.0*(c1.g-0.5),\n" +
            "                    c1.b-c2.a*2.0*(c1.b-0.5),1.0);\n" +
            "            }\n" +
            "        }else{\n" +
            "            lowp vec4 outputColor = c2+c1*c1.a*(1.0-c2.a);\n" +
            "            outputColor.a = 1.0;\n" +
            "            gl_FragColor = outputColor;\n" +
            "        }\n" +
            "    }else{\n" +
            "        gl_FragColor = c1;\n" +
            "    }\n" +
            "}";

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        glProgram = GLESTools.createProgram(vertexShader_filter, fragmentshader_filter);
        GLES20.glUseProgram(glProgram);
        glCamTextureLoc = GLES20.glGetUniformLocation(glProgram, "uCamTexture");
        glImageTextureLoc = GLES20.glGetUniformLocation(glProgram, "uImageTexture");
        glCamPostionLoc = GLES20.glGetAttribLocation(glProgram, "aCamPosition");
        glCamTextureCoordLoc = GLES20.glGetAttribLocation(glProgram, "aCamTextureCoord");
        glImageRectLoc = GLES20.glGetUniformLocation(glProgram, "imageRect");
        glInverseColorLoc = GLES20.glGetUniformLocation(glProgram, "inverseColor");
        textNeedUpdate = true;
        postionNeedUpdate = true;
    }

    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        updateText();
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, targetFrameBuffer);
        GLES20.glUseProgram(glProgram);
        GLES20.glUniform1i(glInverseColorLoc, textColor == Color.TRANSPARENT ? 1 : 0);
        GLES20.glUniform4f(glImageRectLoc, textRect.left / (float) SIZE_WIDTH,
                textRect.top / (float) SIZE_HEIGHT,
                textRect.right / (float) SIZE_WIDTH,
                textRect.bottom / (float) SIZE_HEIGHT);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, cameraTexture);
        GLES20.glUniform1i(glCamTextureLoc, 0);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imageTexture);
        GLES20.glUniform1i(glImageTextureLoc, 1);
        GLES20.glEnableVertexAttribArray(glCamPostionLoc);
        GLES20.glEnableVertexAttribArray(glCamTextureCoordLoc);
        shapeBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamPostionLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, shapeBuffer);
        textrueBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamTextureCoordLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, textrueBuffer);
        GLES20.glViewport(0, 0, SIZE_WIDTH, SIZE_HEIGHT);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawIndecesBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, drawIndecesBuffer);
        GLES20.glFinish();
        GLES20.glDisableVertexAttribArray(glCamPostionLoc);
        GLES20.glDisableVertexAttribArray(glCamTextureCoordLoc);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        GLES20.glUseProgram(0);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        GLES20.glDeleteProgram(glProgram);
        GLES20.glDeleteTextures(1, new int[]{imageTexture}, 0);
        imageTexture = GLESTools.NO_TEXTURE;
    }

    private void updatePostion() {
        int v, h;
        int top, bottom, left, right;
        v = gravity & Gravity.VERTICAL_MASK;
        h = gravity & Gravity.HORIZONTA_MASK;
        switch (v) {
            case Gravity.CENTER_VERTICAL:
                top = (SIZE_HEIGHT - textBitmap.getHeight()) / 2;
                bottom = top + textBitmap.getHeight();
                break;
            case Gravity.BOTTOM:
                bottom = SIZE_HEIGHT - verticalMargin;
                top = bottom - textBitmap.getHeight();
                break;
            case Gravity.TOP:
            default:
                top = verticalMargin;
                bottom = verticalMargin + textBitmap.getHeight();
                break;
        }
        switch (h) {
            case Gravity.CENTER_HORIZONTAL:
                left = (SIZE_WIDTH - textBitmap.getWidth()) / 2;
                right = left + textBitmap.getWidth();
                break;
            case Gravity.RIGHT:
                right = SIZE_WIDTH - horizontalMargin;
                left = right - textBitmap.getWidth();
                break;
            case Gravity.LEFT:
            default:
                left = horizontalMargin;
                right = horizontalMargin + textBitmap.getWidth();
                break;
        }
        textRect.set(left, top, right, bottom);
    }

    public class Gravity {
        public static final int TOP = 0x0000;
        public static final int BOTTOM = 0x0001;
        public static final int CENTER_VERTICAL = 0x0010;
        public static final int VERTICAL_MASK = 0x0011;
        public static final int LEFT = 0x0000;
        public static final int RIGHT = 0x0100;
        public static final int CENTER_HORIZONTAL = 0x1000;
        public static final int HORIZONTA_MASK = 0x1100;
    }
} public class TimeStampHardFilter extends TextHardFilter {
    private static final int UPDATE_INTERVAL = 1000;
    private static final String DEFAULT_TIME_FORMATER = "yyyy-MM-dd kk:mm:ss";
    private long lastUpdateTime;
    private SimpleDateFormat f4;

    public TimeStampHardFilter(@Nullable String formater,int textColor, int textSize) {
        super(DEFAULT_TIME_FORMATER,textColor,textSize);
        formater = formater==null?DEFAULT_TIME_FORMATER:formater;
        f4 = new SimpleDateFormat(formater, Locale.getDefault());
        setText(f4.format(new Date()));
        lastUpdateTime = System.currentTimeMillis();
    }

    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        if ((System.currentTimeMillis() - lastUpdateTime) > UPDATE_INTERVAL) {
            setText(f4.format(new Date()));
            lastUpdateTime = System.currentTimeMillis();
        }
        super.onDraw(cameraTexture, targetFrameBuffer, shapeBuffer, textrueBuffer);
    }
} public class TowInputFilterHard extends BaseHardVideoFilter {
    protected int glProgram;
    protected int glCamTextureLoc;
    protected int glCamPostionLoc;
    protected int glCamTextureCoordLoc;
    protected int glImageTextureLoc;
    protected int glImageTextureCoordLoc;
    protected String vertexShader_filter = "" +
            "attribute vec4 aCamPosition;\n" +
            "attribute vec2 aCamTextureCoord;\n" +
            "attribute vec2 aImageTextureCoord;\n" +
            "varying vec2 vCamTextureCoord;\n" +
            "varying vec2 vImageTextureCoord;\n" +
            "void main(){\n" +
            "   gl_Position= aCamPosition;\n" +
            "   vCamTextureCoord = aCamTextureCoord;\n" +
            "   vImageTextureCoord = aImageTextureCoord;\n" +
            "}";
    protected String fragmentshader_filter = "" +
            "precision highp float;\n" +
            "varying highp vec2 vCamTextureCoord;\n" +
            "varying highp vec2 vImageTextureCoord;\n" +
            "uniform sampler2D uCamTexture;\n" +
            "uniform sampler2D uImageTexture;\n" +
            "void main(){\n" +
            "   lowp vec4 c1 = texture2D(uCamTexture, vCamTextureCoord);\n" +
            "   lowp vec4 c2 = texture2D(uImageTexture, vImageTextureCoord);\n" +
            "   lowp vec4 outputColor = c2+c1*c1.a*(1.0-c2.a);\n" +
            "   outputColor.a = 1.0;\n" +
            "   gl_FragColor = outputColor;\n" +
            "}";
    protected static float texture2Vertices[] = {
            0.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,
            1.0f, 0.0f};
    protected FloatBuffer textureImageCoordBuffer;
    protected int imageTexture;
    private Bitmap image;

    public TowInputFilterHard(String vertexShaderCode, String fragmentShaderCode, Bitmap image) {
        this.image = image;
        if (vertexShaderCode != null) {
            vertexShader_filter = vertexShaderCode;
        }
        if (fragmentShaderCode != null) {
            fragmentshader_filter = fragmentShaderCode;
        }
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        imageTexture = GLESTools.loadTexture(image, GLESTools.NO_TEXTURE);
        glProgram = GLESTools.createProgram(vertexShader_filter, fragmentshader_filter);
        GLES20.glUseProgram(glProgram);
        glCamTextureLoc = GLES20.glGetUniformLocation(glProgram, "uCamTexture");
        glImageTextureLoc = GLES20.glGetUniformLocation(glProgram, "uImageTexture");
        glCamPostionLoc = GLES20.glGetAttribLocation(glProgram, "aCamPosition");
        glCamTextureCoordLoc = GLES20.glGetAttribLocation(glProgram, "aCamTextureCoord");
        glImageTextureCoordLoc = GLES20.glGetAttribLocation(glProgram, "aImageTextureCoord");

        textureImageCoordBuffer = ByteBuffer.allocateDirect(4 * texture2Vertices.length).
                order(ByteOrder.nativeOrder()).
                asFloatBuffer();
        textureImageCoordBuffer.put(texture2Vertices);
        textureImageCoordBuffer.position(0);
    }

    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, targetFrameBuffer);
        GLES20.glUseProgram(glProgram);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, cameraTexture);
        GLES20.glUniform1i(glCamTextureLoc, 0);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imageTexture);
        GLES20.glUniform1i(glImageTextureLoc, 1);
        GLES20.glEnableVertexAttribArray(glCamPostionLoc);
        GLES20.glEnableVertexAttribArray(glCamTextureCoordLoc);
        GLES20.glEnableVertexAttribArray(glImageTextureCoordLoc);
        shapeBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamPostionLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, shapeBuffer);
        textrueBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamTextureCoordLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, textrueBuffer);
        textureImageCoordBuffer.position(0);
        GLES20.glVertexAttribPointer(glImageTextureCoordLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, textureImageCoordBuffer);
        GLES20.glViewport(0, 0, SIZE_WIDTH, SIZE_HEIGHT);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawIndecesBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, drawIndecesBuffer);
        GLES20.glFinish();
        GLES20.glDisableVertexAttribArray(glCamPostionLoc);
        GLES20.glDisableVertexAttribArray(glCamTextureCoordLoc);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        GLES20.glUseProgram(0);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        GLES20.glDeleteProgram(glProgram);
        GLES20.glDeleteTextures(1, new int[]{imageTexture}, 0);
        textureImageCoordBuffer.clear();
    }
} public class ViewHardFilter extends BaseHardVideoFilter {
    private FakeView fakeViewGroup;
    private static final int ROOT_TEX_ID = 11;
    private SurfaceTexture rootViewSufaceTexture;
    private Surface rootViewSuface;

    public ViewHardFilter(@NonNull FakeView viewGroup) {
        fakeViewGroup = viewGroup;
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        rootViewSufaceTexture = new SurfaceTexture(ROOT_TEX_ID);
        rootViewSufaceTexture.setDefaultBufferSize(VWidth, VHeight);
        rootViewSuface = new Surface(rootViewSufaceTexture);

        glViewProgram = GLESTools.createProgram(viewVertexShader_filter, viewFragmentshader_filter);
        GLES20.glUseProgram(glViewProgram);
        glViewTextureLoc = GLES20.glGetUniformLocation(glViewProgram, "uViewTexture");
        glViewCamTextureLoc = GLES20.glGetUniformLocation(glViewProgram, "uViewCamTexture");
        glViewPostionLoc = GLES20.glGetAttribLocation(glViewProgram, "aViewPosition");
        glViewTextureCoordLoc = GLES20.glGetAttribLocation(glViewProgram, "aViewTextureCoord");
        fakeViewGroup.init(VWidth,VHeight);
        fakeViewGroup.startAnim();
    }

    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        //drawview
        Canvas canvas = rootViewSuface.lockCanvas(null);
        canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
        fakeViewGroup.draw(canvas);
        rootViewSuface.unlockCanvasAndPost(canvas);
        rootViewSufaceTexture.updateTexImage();
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, targetFrameBuffer);
        //drawviewoncamera
        GLES20.glUseProgram(glViewProgram);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, ROOT_TEX_ID);
        GLES20.glUniform1i(glViewTextureLoc, 0);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, cameraTexture);
        GLES20.glUniform1i(glViewCamTextureLoc, 1);
        GLES20.glEnableVertexAttribArray(glViewPostionLoc);
        GLES20.glEnableVertexAttribArray(glViewTextureCoordLoc);
        shapeBuffer.position(0);
        GLES20.glVertexAttribPointer(glViewPostionLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, shapeBuffer);
        textrueBuffer.position(0);
        GLES20.glVertexAttribPointer(glViewTextureCoordLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, textrueBuffer);
        GLES20.glViewport(0, 0, SIZE_WIDTH, SIZE_HEIGHT);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawIndecesBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, drawIndecesBuffer);
        GLES20.glFinish();
        GLES20.glDisableVertexAttribArray(glViewPostionLoc);
        GLES20.glDisableVertexAttribArray(glViewTextureCoordLoc);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        GLES20.glUseProgram(0);

        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        rootViewSuface.release();
        rootViewSufaceTexture.release();
        fakeViewGroup.stopAnim();
        fakeViewGroup.destroy();
    }


    protected int glViewProgram;
    protected int glViewTextureLoc;
    protected int glViewCamTextureLoc;
    protected int glViewPostionLoc;
    protected int glViewTextureCoordLoc;
    protected String viewVertexShader_filter = "" +
            "attribute vec4 aViewPosition;\n" +
            "attribute vec2 aViewTextureCoord;\n" +
            "varying vec2 vViewTextureCoord;\n" +
            "void main(){\n" +
            "    gl_Position= aViewPosition;\n" +
            "    vViewTextureCoord = aViewTextureCoord;\n" +
            "}";
    protected String viewFragmentshader_filter = "" +
            "#extension GL_OES_EGL_image_external : require\n" +
            "precision highp float;\n" +
            "varying highp vec2 vViewTextureCoord;\n" +
            "uniform sampler2D uViewCamTexture;\n" +
            "uniform samplerExternalOES uViewTexture;\n" +
            "void main(){\n" +
            "    vec4  c2 = texture2D(uViewTexture, vec2(vViewTextureCoord.x,1.0-vViewTextureCoord.y));\n" +
            "    vec4  c1 = texture2D(uViewCamTexture, vViewTextureCoord);\n" +
            "    lowp vec4 outputColor = c2+c1*c1.a*(1.0-c2.a);\n" +
            "    gl_FragColor = outputColor;\n" +
            "}";
} public class WhiteningHardVideoFilter extends BaseHardVideoFilter {
    byte[] colorMap;

    public WhiteningHardVideoFilter() {
        colorMap = new byte[1024];
        int cur = -1;
        for (int i = 0; i < 256; i++) {
            colorMap[++cur] = ((byte) (int) (255 * Math.pow(i / 255.0, 0.7)));
            colorMap[++cur] = ((byte) (int) (255 * Math.pow(i / 255.0, 0.7)));;
            colorMap[++cur] = ((byte) (int) (255 * Math.pow(i / 255.0, 0.65)));;
            colorMap[++cur] = 0;
        }
    }

    protected int glProgram;
    protected int glCamTextureLoc;
    protected int glCamPostionLoc;
    protected int glCamTextureCoordLoc;
    protected int glColorMapTextureLoc;
    protected static String VERTEXSHADER = "" +
            "attribute vec4 aCamPosition;\n" +
            "attribute vec2 aCamTextureCoord;\n" +
            "varying vec2 vCamTextureCoord;\n" +
            "void main(){\n" +
            "   gl_Position= aCamPosition;\n" +
            "   vCamTextureCoord = aCamTextureCoord;\n" +
            "}";
    protected static String FRAGMENTSHADER = "" +
            "precision highp float;\n" +
            "varying highp vec2 vCamTextureCoord;\n" +
            "uniform sampler2D uCamTexture;\n" +
            "uniform sampler2D uColorMapTexture;\n" +
            "void main(){\n" +
            "   vec4 c1 = texture2D(uCamTexture, vCamTextureCoord);\n" +
            "   float r = texture2D(uColorMapTexture, vec2(c1.r,0.0)).r;\n" +
            "   float g = texture2D(uColorMapTexture, vec2(c1.g,0.0)).g;\n" +
            "   float b = texture2D(uColorMapTexture, vec2(c1.b,0.0)).b;\n" +
            "   gl_FragColor = vec4(r,g,b,1.0);\n" +
            "}";
    protected int imageTexture;


    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        int texture[] = new int[1];
        GLES20.glGenTextures(1, texture, 0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
        ByteBuffer result = ByteBuffer.allocateDirect(colorMap.length).
                order(ByteOrder.nativeOrder());
        result.position(0);
        result.put(colorMap);
        result.position(0);
        GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, 256, 1, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, result);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        imageTexture = texture[0];
        glProgram = GLESTools.createProgram(VERTEXSHADER, FRAGMENTSHADER);
        GLES20.glUseProgram(glProgram);
        glCamTextureLoc = GLES20.glGetUniformLocation(glProgram, "uCamTexture");
        glColorMapTextureLoc = GLES20.glGetUniformLocation(glProgram, "uColorMapTexture");
        glCamPostionLoc = GLES20.glGetAttribLocation(glProgram, "aCamPosition");
        glCamTextureCoordLoc = GLES20.glGetAttribLocation(glProgram, "aCamTextureCoord");

    }

    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, targetFrameBuffer);
        GLES20.glUseProgram(glProgram);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, cameraTexture);
        GLES20.glUniform1i(glCamTextureLoc, 0);
        GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, imageTexture);
        GLES20.glUniform1i(glColorMapTextureLoc, 1);
        GLES20.glEnableVertexAttribArray(glCamPostionLoc);
        GLES20.glEnableVertexAttribArray(glCamTextureCoordLoc);
        shapeBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamPostionLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, shapeBuffer);
        textrueBuffer.position(0);
        GLES20.glVertexAttribPointer(glCamTextureCoordLoc, 2,
                GLES20.GL_FLOAT, false,
                2 * 4, textrueBuffer);
        GLES20.glViewport(0, 0, SIZE_WIDTH, SIZE_HEIGHT);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawIndecesBuffer.limit(), GLES20.GL_UNSIGNED_SHORT, drawIndecesBuffer);
        GLES20.glFinish();
        GLES20.glDisableVertexAttribArray(glCamPostionLoc);
        GLES20.glDisableVertexAttribArray(glCamTextureCoordLoc);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        GLES20.glUseProgram(0);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        GLES20.glDeleteProgram(glProgram);
        GLES20.glDeleteTextures(1, new int[]{imageTexture}, 0);
    }
} public class GPUImageCompatibleFilter<T extends GPUImageFilter> extends BaseHardVideoFilter {
    private T innerGPUImageFilter;

    private FloatBuffer innerShapeBuffer;
    private FloatBuffer innerTextureBuffer;

    public GPUImageCompatibleFilter(T filter) {
        innerGPUImageFilter = filter;
    }

    public T getGPUImageFilter() {
        return innerGPUImageFilter;
    }

    @Override
    public void onInit(int VWidth, int VHeight) {
        super.onInit(VWidth, VHeight);
        innerGPUImageFilter.init();
        innerGPUImageFilter.onOutputSizeChanged(VWidth, VHeight);
    }

    @Override
    public void onDraw(int cameraTexture, int targetFrameBuffer, FloatBuffer shapeBuffer, FloatBuffer textrueBuffer) {
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, targetFrameBuffer);
        innerGPUImageFilter.onDraw(cameraTexture, innerShapeBuffer, innerTextureBuffer);
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        innerGPUImageFilter.destroy();
    }

    @Override
    public void onDirectionUpdate(int _directionFlag) {
        if (directionFlag != _directionFlag) {
            innerShapeBuffer = getGPUImageCompatShapeVerticesBuffer();
            innerTextureBuffer = getGPUImageCompatTextureVerticesBuffer(directionFlag);
        }
    }

    public static final float TEXTURE_NO_ROTATION[] = {
            1.0f, 1.0f,
            0.0f, 1.0f,
            1.0f, 0.0f,
            0.0f, 0.0f,
    };

    public static final float TEXTURE_ROTATED_90[] = {
            0.0f, 1.0f,
            0.0f, 0.0f,
            1.0f, 1.0f,
            1.0f, 0.0f,
    };
    public static final float TEXTURE_ROTATED_180[] = {
            0.0f, 0.0f,
            1.0f, 0.0f,
            0.0f, 1.0f,
            1.0f, 1.0f,
    };
    public static final float TEXTURE_ROTATED_270[] = {
            1.0f, 0.0f,
            1.0f, 1.0f,
            0.0f, 0.0f,
            0.0f, 1.0f,
    };
    static final float CUBE[] = {
            -1.0f, -1.0f,
            1.0f, -1.0f,
            -1.0f, 1.0f,
            1.0f, 1.0f,
    };

    public static FloatBuffer getGPUImageCompatShapeVerticesBuffer() {
        FloatBuffer result = ByteBuffer.allocateDirect(GLESTools.FLOAT_SIZE_BYTES * CUBE.length).
                order(ByteOrder.nativeOrder()).
                asFloatBuffer();
        result.put(CUBE);
        result.position(0);
        return result;
    }

    public static FloatBuffer getGPUImageCompatTextureVerticesBuffer(final int directionFlag) {
        float[] buffer;
        switch (directionFlag & 0xF0) {
            case RESCoreParameters.FLAG_DIRECTION_ROATATION_90:
                buffer = TEXTURE_ROTATED_90.clone();
                break;
            case RESCoreParameters.FLAG_DIRECTION_ROATATION_180:
                buffer = TEXTURE_ROTATED_180.clone();
                break;
            case RESCoreParameters.FLAG_DIRECTION_ROATATION_270:
                buffer = TEXTURE_ROTATED_270.clone();
                break;
            default:
                buffer = TEXTURE_NO_ROTATION.clone();
        }
        if ((directionFlag & RESCoreParameters.FLAG_DIRECTION_FLIP_HORIZONTAL) != 0) {
            buffer[0] = flip(buffer[0]);
            buffer[2] = flip(buffer[2]);
            buffer[4] = flip(buffer[4]);
            buffer[6] = flip(buffer[6]);
        }
        if ((directionFlag & RESCoreParameters.FLAG_DIRECTION_FLIP_VERTICAL) != 0) {
            buffer[1] = flip(buffer[1]);
            buffer[3] = flip(buffer[3]);
            buffer[5] = flip(buffer[5]);
            buffer[7] = flip(buffer[7]);
        }
        FloatBuffer result = ByteBuffer.allocateDirect(GLESTools.FLOAT_SIZE_BYTES * buffer.length).
                order(ByteOrder.nativeOrder()).
                asFloatBuffer();
        result.put(buffer);
        result.position(0);
        return result;
    }

    private static float flip(final float i) {
        return i == 0.0f ? 1.0f : 0.0f;
    }
} public class FakeView extends FrameLayout {
    Button btn_test;
    TextView tv_test;
    ImageView iv_test;
    SeekBar sb_test;
    private int VWidth, VHeight;

    public FakeView(Context context) {
        super(context);
    }

    public void init(int w, int h) {
        VWidth = w;
        VHeight = h;
        this.setBackgroundColor(Color.argb(0, 0, 0, 0));
        tv_test = new TextView(this.getContext());
        FrameLayout.LayoutParams testLayoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        testLayoutParams.gravity = Gravity.TOP | Gravity.LEFT;
        tv_test.setLayoutParams(testLayoutParams);
        tv_test.setText("TextView");
        tv_test.setTextSize(10);
        tv_test.setTextColor(Color.RED);
        tv_test.setBackgroundColor(Color.argb(255, 150, 150, 150));
        this.addView(tv_test);

        btn_test = new Button(this.getContext());
        testLayoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        testLayoutParams.gravity = Gravity.TOP | Gravity.RIGHT;
        btn_test.setLayoutParams(testLayoutParams);
        btn_test.setText("Button");
        btn_test.setTextSize(10);
        btn_test.setTextColor(Color.RED);
        this.addView(btn_test);

        iv_test = new ImageView(this.getContext());
        int small = VWidth>VHeight?VHeight:VWidth;
        testLayoutParams = new FrameLayout.LayoutParams(small/2, small/2);
        testLayoutParams.gravity = Gravity.CENTER;
        iv_test.setImageResource(R.mipmap.cat);
        iv_test.setLayoutParams(testLayoutParams);
        this.addView(iv_test);

        sb_test = new SeekBar(this.getContext());
        testLayoutParams = new FrameLayout.LayoutParams(VWidth/2, ViewGroup.LayoutParams.WRAP_CONTENT);
        testLayoutParams.gravity = Gravity.BOTTOM|Gravity.LEFT;
        sb_test.setLayoutParams(testLayoutParams);
        this.addView(sb_test);

        AlwaysMarqueeTextView tv_marqueetest = new AlwaysMarqueeTextView(this.getContext());
        testLayoutParams = new FrameLayout.LayoutParams(VWidth/2, ViewGroup.LayoutParams.WRAP_CONTENT);
        testLayoutParams.gravity = Gravity.BOTTOM | Gravity.RIGHT;
        tv_marqueetest.setSingleLine();
        tv_marqueetest.setEllipsize(TextUtils.TruncateAt.MARQUEE);
        tv_marqueetest.setMarqueeRepeatLimit(-1);
        tv_marqueetest.setText("MarqueeLonnnnnnnngTextView");
        tv_marqueetest.setTextSize(10);
        tv_marqueetest.setTextColor(Color.BLUE);
        tv_marqueetest.setBackgroundColor(Color.argb(255, 150, 150, 150));
        tv_marqueetest.setLayoutParams(testLayoutParams);
        this.addView(tv_marqueetest);

        update();
    }

    private void update() {
        this.measure(VWidth, VHeight);
        this.layout(0, 0, VWidth, VHeight);
    }

    public void destroy() {
        this.removeAllViews();
    }

    ObjectAnimator translationAnim;
    ObjectAnimator rotatioinAnim;
    AnimatorSet totationSetAnim;
    ValueAnimator seekAnim;

    public void startAnim() {
        translationAnim = ObjectAnimator.ofFloat(tv_test, "Y",
                0f, 300f);
        translationAnim.setInterpolator(new DecelerateInterpolator());
        translationAnim.setDuration(3000);
        translationAnim.setRepeatCount(ValueAnimator.INFINITE);
        translationAnim.setRepeatMode(Animation.REVERSE);//设置动画循环模式。
        translationAnim.start();

        rotatioinAnim = ObjectAnimator.ofFloat(btn_test, "rotation", 0f, 360f);
        rotatioinAnim.setDuration(3000);
        rotatioinAnim.setRepeatCount(ValueAnimator.INFINITE);
        rotatioinAnim.start();

        totationSetAnim = new AnimatorSet();
        ObjectAnimator anim = ObjectAnimator.ofFloat(iv_test, "rotationX", 0f, 180f);
        anim.setDuration(3000);
        anim.setRepeatCount(ValueAnimator.INFINITE);
        anim.setRepeatMode(ValueAnimator.REVERSE);
        ObjectAnimator anim3 = ObjectAnimator.ofFloat(iv_test, "rotationY", 0f, 180f);
        anim3.setDuration(3000);
        anim3.setRepeatCount(ValueAnimator.INFINITE);
        anim3.setRepeatMode(ValueAnimator.REVERSE);
        totationSetAnim.play(anim);
        totationSetAnim.play(anim3);
        totationSetAnim.start();

        seekAnim = ValueAnimator.ofInt(0, sb_test.getMax());
        seekAnim.setDuration(3000);
        seekAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int animProgress = (Integer) animation.getAnimatedValue();
                sb_test.setProgress(animProgress);
            }
        });
        seekAnim.setRepeatCount(ValueAnimator.INFINITE);
        seekAnim.setRepeatMode(ValueAnimator.REVERSE);
        seekAnim.start();
    }

    public void stopAnim() {
        translationAnim.cancel();
        rotatioinAnim.cancel();
        totationSetAnim.cancel();
        seekAnim.cancel();
    }
}
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import jp.co.cyberagent.android.gpuimage.GPUImage3x3ConvolutionFilter;
import jp.co.cyberagent.android.gpuimage.GPUImage3x3TextureSamplingFilter;
import jp.co.cyberagent.android.gpuimage.GPUImageCGAColorspaceFilter;
import jp.co.cyberagent.android.gpuimage.GPUImageColorInvertFilter;
import jp.co.cyberagent.android.gpuimage.GPUImageCrosshatchFilter;
import jp.co.cyberagent.android.gpuimage.GPUImageFilter;
import jp.co.cyberagent.android.gpuimage.GPUImageGrayscaleFilter;
import jp.co.cyberagent.android.gpuimage.GPUImagePixelationFilter;
import me.lake.librestreaming.filter.hardvideofilter.BaseHardVideoFilter;
import me.lake.librestreaming.filter.hardvideofilter.HardVideoGroupFilter;
import me.lake.librestreaming.filter.hardvideofilter.OriginalHardVideoFilter;
import me.lake.librestreaming.model.RESConfig;
import me.lake.librestreaming.sample.hardfilter.ColorMixHardFilter;
import me.lake.librestreaming.sample.hardfilter.DifferenceBlendFilterHard;
import me.lake.librestreaming.sample.hardfilter.FishEyeFilterHard;
import me.lake.librestreaming.sample.hardfilter.GaussianBlurHardFilter;
import me.lake.librestreaming.sample.hardfilter.IconHardFilter;
import me.lake.librestreaming.sample.hardfilter.SeaScapeFilter;
import me.lake.librestreaming.sample.hardfilter.SkinBlurHardVideoFilter;
import me.lake.librestreaming.sample.hardfilter.SobelEdgeDetectionHardVideoFilter;
import me.lake.librestreaming.sample.hardfilter.TextHardFilter;
import me.lake.librestreaming.sample.hardfilter.TimeStampHardFilter;
import me.lake.librestreaming.sample.hardfilter.TowInputFilterHard;
import me.lake.librestreaming.sample.hardfilter.ViewHardFilter;
import me.lake.librestreaming.sample.hardfilter.WhiteningHardVideoFilter;
import me.lake.librestreaming.sample.hardfilter.extra.GPUImageCompatibleFilter;
import me.lake.librestreaming.sample.ui.FakeView;
package me.lake.librestreaming.sample;




/**
 * Created by lake on 16-5-31.
 */
public class HardStreamingActivity extends BaseStreamingActivity {
    protected FilterAdapter filterAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        filtermode = RESConfig.FilterMode.HARD;
        super.onCreate(savedInstanceState);
        /**
         * filters just for demo
         */
        ArrayList<FilterItem> filterItems = new ArrayList<>();
        filterItems.add(new FilterItem("NoFilter", null));
        filterItems.add(new FilterItem("DoNothing", new OriginalHardVideoFilter(null, null)));
        filterItems.add(new FilterItem("ViewRending", new ViewHardFilter(new FakeView(this))));
        filterItems.add(new FilterItem("FishEye", new FishEyeFilterHard()));
        filterItems.add(new FilterItem("SkinBlur", new SkinBlurHardVideoFilter(2)));
        filterItems.add(new FilterItem("Whitening", new WhiteningHardVideoFilter()));
        filterItems.add(new FilterItem("ColorMix", new ColorMixHardFilter(0.98f, 0.72f, 0.82f, 0.3f)));
        LinkedList<BaseHardVideoFilter> filters = new LinkedList<>();
        filters.add(new SkinBlurHardVideoFilter(2));
        filters.add(new WhiteningHardVideoFilter());
        filterItems.add(new FilterItem("FacialUp", new HardVideoGroupFilter(filters)));
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        filterItems.add(new FilterItem("NormalBlend", new TowInputFilterHard(null, null, bitmap)));
        filters = new LinkedList<>();
        filters.add(new SobelEdgeDetectionHardVideoFilter());
        filters.add(new FishEyeFilterHard());
        filterItems.add(new FilterItem("GroupFilter", new HardVideoGroupFilter(filters)));
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        filterItems.add(new FilterItem("DifferenceBlend", new DifferenceBlendFilterHard(bitmap)));
        filterItems.add(new FilterItem("SobelEdgeDetection", new SobelEdgeDetectionHardVideoFilter()));
        bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
        filterItems.add(new FilterItem("Icon",new IconHardFilter(bitmap,new Rect(100,100,200,200))));
        filterItems.add(new FilterItem("SeaScape", new SeaScapeFilter()));
        filterItems.add(new FilterItem("SimpleText", new TextHardFilter("タイムマシン", Color.argb(127, 255, 0, 0), 35)));
        CharSequence htmlText = Html.fromHtml("<font color=\"#7FFF0000\">红色字体红色</font><br/><b>字体加粗</b><br/><s>删除字体</s><i>斜体字体</i>");
        TextHardFilter htmlTextHardFilter = new TextHardFilter(htmlText);
        htmlTextHardFilter.setPostion(TextHardFilter.Gravity.RIGHT | TextHardFilter.Gravity.TOP, 50, 50);
        filterItems.add(new FilterItem("HtmlText", htmlTextHardFilter));
        TextHardFilter moreText = new TextHardFilter("Fou ki ra hyear presia reen\n" +
                "Was zweie ra na stel yorra zuieg manaf\n" +
                "Ma zweie ra irs manaf chyet oz omnis\n" +
                "en na cyurio re chyet\n" +
                "Was touwaka gaya presia accrroad ieeya\n" +
                "Nn num gagis knawa na lequera walasye\n" +
                "Was quel gagis presia accrroad ieeya whou wearequewie fogabe",Color.RED,30);
        moreText.setPostion(TextHardFilter.Gravity.BOTTOM,50,50);
        filterItems.add(new FilterItem("MoreText", moreText));
        TimeStampHardFilter timeStampHardFilter = new TimeStampHardFilter(null,Color.RED,30);
        timeStampHardFilter.setPostion(TextHardFilter.Gravity.BOTTOM|TextHardFilter.Gravity.RIGHT,30,30);
        filterItems.add(new FilterItem("TimeStamp", timeStampHardFilter));
        TimeStampHardFilter timeStampHardFilter2 = new TimeStampHardFilter(null,Color.TRANSPARENT,30);
        timeStampHardFilter2.setPostion(TextHardFilter.Gravity.BOTTOM | TextHardFilter.Gravity.RIGHT, 30, 30);
        filterItems.add(new FilterItem("invertText", timeStampHardFilter2));
        filterItems.add(new FilterItem("GaussianBlur",new GaussianBlurHardFilter(20)));