影视处理

Camera2 教程 2:图片动态切换滤镜

2020-03-22  本文已影响0人  古风子
青橙相机

章节目录:

    1 直接替换流程
    2 动态替换滤镜效果
        2.1 JGPUImage用于封装滤镜类和render操作
        2.2 动态切换滤镜类
            2.2.1 将新的滤镜对象传入render类中,创建一个队列任务
                1. 销毁老的滤镜类,并不销毁对象,只是重置初始化状态和删除着色器程序,避免对象频繁创建
                2. 初始化新的滤镜类,主要完成着色器程序的加载和着色器中各个属性的引用字段创建
                3. 使用新的滤镜程序
                4. 更新view显示的大小
            2.2.2 requestRender请求刷新,执行onDrawFrame
        2.3 JGPUImage封装GlSurfaceView操作
        2.4 Activity调用
    3 动态替换图片
        3.1 删除旧的图片纹理id
        3.2 设置新的图片资源
        3.3 onDrawFrame
        3.3 Activity调用方法
    4. 效果图
    5. 代码工程

在上一篇文章中,我们是使用默认的着色器文件处理图片的,显示的是正常的图片效果;本章节,我们讲下,怎样动态切换着色器处理效果

1 直接替换流程

正常效果的滤镜处理文件

public class JGPUImageFilter {
    public static final String NO_FILTER_VERTEX_SHADER = "" +
            "attribute vec4 position;\n" +
            "attribute vec4 inputTextureCoordinate;\n" +
            " \n" +
            "varying vec2 textureCoordinate;\n" +
            " \n" +
            "void main()\n" +
            "{\n" +
            "    gl_Position = position;\n" +
            "    textureCoordinate = inputTextureCoordinate.xy;\n" +
            "}";
    public static final String NO_FILTER_FRAGMENT_SHADER = "" +
            "varying highp vec2 textureCoordinate;\n" +
            " \n" +
            "uniform sampler2D inputImageTexture;\n" +
            " \n" +
            "void main()\n" +
            "{\n" +
            "     gl_FragColor = texture2D(inputImageTexture, textureCoordinate);\n" +
            "}";
...
}

我们继承JGPUImageFilter 实现一个黑白滤镜作色器效果

public class JGPUImageGrayFilter extends  JGPUImageFilter {
    private static final String NO_FILTER_FRAGMENT_SHADER =
                      "precision mediump float;\n"
                    + "uniform sampler2D inputImageTexture;\n"
                    + "varying vec2 textureCoordinate;\n"
                    + "const highp vec3 W = vec3(0.2125, 0.7154, 0.0721);\n"
                    + "void main() {\n"
                    + "lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);\n"
                    + "float luminance = dot(textureColor.rgb, W);\n"
                    + "gl_FragColor = vec4(vec3(luminance), textureColor.w);\n"

                    + "}\n";

    public JGPUImageGrayFilter() {
        this(NO_FILTER_VERTEX_SHADER, NO_FILTER_FRAGMENT_SHADER);
    }

    public JGPUImageGrayFilter(final String vertexShader, final String fragmentShader) {
      super(vertexShader,fragmentShader);
    }

}

Activity中替换滤镜类对象


public class PicFilterBaseActivity extends BaseActivity{
public class PicFilterBaseActivity extends BaseActivity{
        setContentView(R.layout.activity_texture);
        mGLSurfaceView = findViewById(R.id.surfaceView);

         //替换成黑白滤镜
        JGPUImageFilter normalFilter = new JGPUImageGrayFilter();
        JGPUImageRenderer renderer = new JGPUImageRenderer(normalFilter);
        setGLSurfaceViewRender(renderer);
        renderer.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.texture));

处理后的效果为:

灰色滤镜

2 动态替换滤镜效果

实际应用中,我们需要动态替换各种滤镜效果,而不需要将所有流程再重新走一遍;特别是在相机预览滤镜场景,我们不可能没替换一次滤镜,就将整个相机的打开流程重新执行一遍

2.1 JGPUImage用于封装滤镜类和render操作

public JGPUImage(final Context context) {
        if (!supportsOpenGLES2(context)) {
            throw new IllegalStateException("OpenGL ES 2.0 is not supported on this phone.");
        }

        this.context = context;
        filter = new JGPUImageFilter();
        renderer = new JGPUImageRenderer(filter);
    }

2.2 动态切换滤镜类

   //JGPUImage.java
   public void setFilter(final JGPUImageFilter filter) {
        this.filter = filter;
        renderer.setFilter(this.filter);
        requestRender();
    }

该方法会动态替换JGPUImageRenderer中的filter对象,并在刷新的时候,对新传入的filter重新初始化以获取新的着色器程序id和各个属性的引用

2.2.1 将新的滤镜对象传入render类中,创建一个队列任务

这里只是创建一个队列任务,当Render的onDrawFrame方法被执行时,具体逻辑才会执行;

   public void setFilter(final JGPUImageFilter filter) {
        runOnDraw(new Runnable() {

            @Override
            public void run() {
                final JGPUImageFilter oldFilter = JGPUImageRenderer.this.filter;
                JGPUImageRenderer.this.filter = filter;
                if (oldFilter != null) {
                    oldFilter.destroy();//destory
                }
                JGPUImageRenderer.this.filter.ifNeedInit();
                GLES20.glUseProgram(JGPUImageRenderer.this.filter.getProgram());
                JGPUImageRenderer.this.filter.onOutputSizeChanged(outputWidth, outputHeight);
            }
        });
    }

以上队列任务执行的时候,也就onDrawFram调用时的主要逻辑步骤为:

1. 销毁老的滤镜类,并不销毁对象,只是重置初始化状态和删除着色器程序,避免对象频繁创建
 //JGPUImageFilter基类方法
   public final void destroy() {
        isInitialized = false;
        GLES20.glDeleteProgram(glProgId);
        onDestroy();//模板设计模式,固定算法框架,由基类具体实现自己的策略
    }
2. 初始化新的滤镜类,主要完成着色器程序的加载和着色器中各个属性的引用字段创建
  //JGPUImageFilter基类方法
    public void ifNeedInit() {
        if (!isInitialized) init();//模板设计模式,固定算法框架,由基类具体实现自己的策略
    }

 private final void init() {
        onInit();
        onInitialized();
    }

    public void onInit() {
        //根据传入的作色器字符串,创建,链接,编译对象,返回程序对象id
        glProgId = OpenGlUtils.loadProgram(vertexShader, fragmentShader);
        //获取程序中顶点位置属性引用
        glAttribPosition = GLES20.glGetAttribLocation(glProgId, "position");
        //获取程序中纹理内容属性引用
        glUniformTexture = GLES20.glGetUniformLocation(glProgId, "inputImageTexture");
        //获取程序中顶点纹理坐标属性引用
        glAttribTextureCoordinate = GLES20.glGetAttribLocation(glProgId, "inputTextureCoordinate");
        //标明指初始化一次
        isInitialized = true;
    }

    public void onInitialized() {
    }
3. 使用新的滤镜程序
 GLES20.glUseProgram(mFilter.getProgram());
4. 更新view显示的大小
 mFilter.onOutputSizeChanged(outputWidth, outputHeight);

2.2.2 requestRender请求刷新,执行onDrawFrame

//JGPUImage.java
requestRender();
   public void requestRender() {
            if (glSurfaceView != null) {
                glSurfaceView.requestRender();
            }
    }
  //JGPUImageRenderer
   @Override
    public void onDrawFrame(final GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        //调用队列任务
        runAll(runOnDraw);
       //调用当前filter类渲染图片
        filter.onDraw(glTextureId, glCubeBuffer, glTextureBuffer);
    }

glSurfaceView通过以下方法传入

2.3 JGPUImage封装GlSurfaceView操作

    public void setGLSurfaceView(final GLSurfaceView view) {
        glSurfaceView = view;
        glSurfaceView.setEGLContextClientVersion(2);
        glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
        glSurfaceView.getHolder().setFormat(PixelFormat.RGBA_8888);
        glSurfaceView.setRenderer(renderer);
        glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
        glSurfaceView.requestRender();
    }

2.4 Activity调用

以上流程完成后,我们可以通过setFilter方法,动态切换应用到图片的滤镜

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_texture);
        mGLSurfaceView = findViewById(R.id.surfaceView);

        //默認使用正常的濾鏡效果
        jgpuImage = new GPUImage(this);

        jgpuImage.setGLSurfaceView(mGLSurfaceView);
        jgpuImage.setImage(BitmapFactory.decodeResource(getResources(), R.drawable.texture));

        findViewById(R.id.switch_filter).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                jgpuImage.setFilter(new GPUImageGrayscaleFilter());

            }
        });

}

3 动态替换图片

动态切换滤镜主要使用对单个图片适用不同滤镜效果的场景;实际应用中,还需要实现对图片动态切换的场景

3.1 删除旧的图片纹理id

  //JGPUImage.java
    public void deleteImage() {
        renderer.deleteImage();
        currentBitmap = null;
        requestRender();
    }
  //将释放老的图片资源id代码,在Render类找中放到一个onDrawFrame执行的队列任务中
   public void deleteImage() {
        runOnDraw(new Runnable() {

            @Override
            public void run() {
                GLES20.glDeleteTextures(1, new int[]{
                        glTextureId
                }, 0);
                glTextureId = NO_IMAGE;
            }
        });
    }

3.2 设置新的图片资源

  //JGPUImage.java
    public void setImage(final Bitmap bitmap) {
        currentBitmap = bitmap;
        renderer.setImageBitmap(bitmap, false);
        requestRender();
    }
  //将创建新的图片资源id代码,在Render类找中放到一个onDrawFrame执行的队列任务中
 public void setImageBitmap(final Bitmap bitmap, final boolean recycle) {
        if (bitmap == null) {
            return;
        }

        runOnDraw(new Runnable() {

            @Override
            public void run() {
                Bitmap resizedBitmap = null;
                //求余数,OPENGL处理的图片大小必须是2的整数次方
                if (bitmap.getWidth() % 2 == 1) {
                    resizedBitmap = Bitmap.createBitmap(bitmap.getWidth() + 1, bitmap.getHeight(),
                            Bitmap.Config.ARGB_8888);
                    Canvas can = new Canvas(resizedBitmap);
                    can.drawARGB(0x00, 0x00, 0x00, 0x00);
                    can.drawBitmap(bitmap, 0, 0, null);
                    addedPadding = 1;
                } else {
                    addedPadding = 0;
                }

                glTextureId = OpenGlUtils.loadTexture(
                        resizedBitmap != null ? resizedBitmap : bitmap, glTextureId, recycle);
                if (resizedBitmap != null) {
                    resizedBitmap.recycle();
                }
                imageWidth = bitmap.getWidth();
                imageHeight = bitmap.getHeight();
                adjustImageScaling();
            }
        });
    }

以上代码调用requestRender();刷新帧数据后,调用onDrawFrame方法

3.3 onDrawFrame

 @Override
    public void onDrawFrame(final GL10 gl) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        //依次调用删除老的纹理id和创建新额纹理id的代码
        runAll(runOnDraw);
       //将新的纹理id传入到着色器中进行处理
        filter.onDraw(glTextureId, glCubeBuffer, glTextureBuffer);
    }

3.4 Activity调用方法

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_texture);
        mGLSurfaceView = findViewById(R.id.surfaceView);

        //默認使用正常的濾鏡效果
        jgpuImage = new GPUImage(this);

        jgpuImage.setGLSurfaceView(mGLSurfaceView);
        jgpuImage.setImage(BitmapFactory.decodeResource(getResources(), R.drawable.texture));

        findViewById(R.id.switch_image).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //删除老的纹理
                jgpuImage.deleteImage();
              //设置和获取新的纹理id
                jgpuImage.setImage(BitmapFactory.decodeResource(getResources(), R.drawable.texture2));

            }
        });

    }

4. 效果图

经过以上流程后,我们看下最终的程序效果图:

动态滤镜效果切换

5 代码工程

代码工程位置参考以下开源文件的PicFilterBaseActivity.java文件

QCCamera

上一篇 下一篇

猜你喜欢

热点阅读