GPUImageFilter解析
GPUImageFilter 是 Android 平台基于 OpenGL ES 2.0 实现的 GPU 图像滤镜核心基类,为各类图像滤镜(如美颜、风格化特效)提供了统一的初始化、绘制、资源管理、参数配置能力。
本文将逐行解析该类的代码结构、实现逻辑及每行代码的含义。
版权声明与包/类导入
功能说明
该部分包含开源协议声明、包路径定义,以及实现滤镜功能所需的核心类导入,是 Java 类的基础结构。
/* * Copyright (C) 2018 CyberAgent, Inc. // 版权归属:CyberAgent 公司 * * Licensed under the Apache License, Version 2.0 (the "License"); // 遵循 Apache 2.0 开源协议 * you may not use this file except in compliance with the License. // 使用代码需遵守协议约束 * You may obtain a copy of the License at // 协议文本获取地址 * * http://www.apache.org/licenses/LICENSE-2.0 // Apache 2.0 协议官方地址 * * Unless required by applicable law or agreed to in writing, software // 无附加责任声明 * distributed under the License is distributed on an "AS IS" BASIS, // 分发模式:按现状提供 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // 无明示/默示担保 * See the License for the specific language governing permissions and // 协议定义权限与限制 * limitations under the License. */// 声明类所属包路径,归属于 CyberAgent 的 GPUImage 安卓库滤镜模块packagejp.co.cyberagent.android.gpuimage.filter;// 安卓上下文类,用于获取资源(如 Assets)importandroid.content.Context;// 安卓资产管理类,用于读取 assets 目录下的着色器文件importandroid.content.res.AssetManager;// 二维点坐标类,用于传递纹理/顶点坐标点importandroid.graphics.PointF;// OpenGL ES 2.0 核心 API 类,提供 OpenGL 底层调用importandroid.opengl.GLES20;// 输入流类,用于读取 assets 中的着色器文件内容importjava.io.InputStream;// 浮点缓冲区类,用于存储顶点/纹理坐标数据(适配 OpenGL 数据格式)importjava.nio.FloatBuffer;// 链表类,用于存储待执行的绘制任务importjava.util.LinkedList;// OpenGL 工具类(自定义),封装着色器加载、编译等通用操作importjp.co.cyberagent.android.gpuimage.util.OpenGlUtils;类定义与核心常量
功能说明
定义滤镜基类的核心常量(默认着色器代码),顶点着色器负责处理顶点坐标变换,片元着色器负责处理像素颜色渲染,默认实现“无滤镜”效果(直接输出原纹理)。
// 定义 GPUImageFilter 公共类,所有具体滤镜(如美白、磨皮)均继承此类publicclassGPUImageFilter{// 无滤镜顶点着色器代码:处理顶点坐标和纹理坐标传递publicstaticfinalStringNO_FILTER_VERTEX_SHADER=""+"attribute vec4 position;\n"+// 顶点坐标属性(由 Java 层传入)"attribute vec4 inputTextureCoordinate;\n"+// 纹理坐标属性(由 Java 层传入)" \n"+"varying vec2 textureCoordinate;\n"+// 易变变量:传递纹理坐标到片元着色器" \n"+"void main()\n"+// 顶点着色器主函数"{\n"+" gl_Position = position;\n"+// 设置顶点最终坐标(OpenGL 裁剪空间)" textureCoordinate = inputTextureCoordinate.xy;\n"+// 传递纹理坐标到片元着色器"}";// 无滤镜片元着色器代码:直接采样原纹理像素,无任何颜色修改publicstaticfinalStringNO_FILTER_FRAGMENT_SHADER=""+"varying highp vec2 textureCoordinate;\n"+// 接收顶点着色器传递的纹理坐标(高精度)" \n"+"uniform sampler2D inputImageTexture;\n"+// 输入纹理采样器(由 Java 层绑定)" \n"+"void main()\n"+// 片元着色器主函数"{\n"+" gl_FragColor = texture2D(inputImageTexture, textureCoordinate);\n"+// 采样纹理像素,作为输出颜色"}";成员变量
功能说明
定义类的核心成员变量,涵盖绘制任务队列、着色器代码、OpenGL 程序/属性/Uniform 句柄、输出尺寸、初始化状态等,是滤镜运行的核心数据载体。
// 待执行的绘制任务链表:存储需要在绘制阶段执行的 OpenGL 操作(如设置 Uniform 参数)privatefinalLinkedList<Runnable>runOnDraw;// 顶点着色器代码(可自定义)privatefinalStringvertexShader;// 片元着色器代码(可自定义)privatefinalStringfragmentShader;// OpenGL 程序 ID:着色器编译链接后的程序句柄privateintglProgId;// position 属性位置:对应顶点着色器中 "position" 变量的句柄privateintglAttribPosition;// 输入纹理 Uniform 位置:对应片元着色器中 "inputImageTexture" 变量的句柄privateintglUniformTexture;// 纹理坐标属性位置:对应顶点着色器中 "inputTextureCoordinate" 变量的句柄privateintglAttribTextureCoordinate;// 滤镜输出宽度privateintoutputWidth;// 滤镜输出高度privateintoutputHeight;// 初始化状态标记:标记 OpenGL 程序是否已初始化完成privatebooleanisInitialized;构造方法
功能说明
提供无参/有参构造方法,无参构造默认使用“无滤镜”着色器,有参构造支持传入自定义顶点/片元着色器,适配不同滤镜的定制化需求。
// 无参构造方法:默认使用无滤镜顶点/片元着色器publicGPUImageFilter(){this(NO_FILTER_VERTEX_SHADER,NO_FILTER_FRAGMENT_SHADER);}// 有参构造方法:接收自定义顶点/片元着色器代码publicGPUImageFilter(finalStringvertexShader,finalStringfragmentShader){// 初始化绘制任务链表runOnDraw=newLinkedList<>();// 赋值自定义顶点着色器代码this.vertexShader=vertexShader;// 赋值自定义片元着色器代码this.fragmentShader=fragmentShader;}初始化相关方法
功能说明
封装 OpenGL 程序的初始化逻辑,包括着色器编译链接、属性/Uniform 句柄获取,提供初始化检查、回调扩展等能力,确保滤镜绘制前完成必要的初始化。
// 内部初始化入口方法:封装初始化流程(私有,仅内部调用)privatefinalvoidinit(){// 执行初始化核心逻辑(子类可重写)onInit();// 初始化完成回调(子类可重写扩展)onInitialized();}// 初始化核心逻辑:编译链接着色器程序,获取属性/Uniform 句柄publicvoidonInit(){// 加载并编译顶点/片元着色器,链接为 OpenGL 程序,返回程序 IDglProgId=OpenGlUtils.loadProgram(vertexShader,fragmentShader);// 获取顶点着色器中 "position" 属性的句柄glAttribPosition=GLES20.glGetAttribLocation(glProgId,"position");// 获取片元着色器中 "inputImageTexture" Uniform 的句柄glUniformTexture=GLES20.glGetUniformLocation(glProgId,"inputImageTexture");// 获取顶点着色器中 "inputTextureCoordinate" 属性的句柄glAttribTextureCoordinate=GLES20.glGetAttribLocation(glProgId,"inputTextureCoordinate");// 标记初始化完成isInitialized=true;}// 初始化完成回调方法:空实现,子类可重写(如初始化自定义 Uniform 参数)publicvoidonInitialized(){}// 初始化检查方法:若未初始化则执行 init()publicvoidifNeedInit(){if(!isInitialized)init();}资源销毁相关方法
功能说明
封装 OpenGL 程序的销毁逻辑,释放显存资源,提供销毁回调扩展,避免内存泄漏。
// 最终销毁方法:标记未初始化,删除 OpenGL 程序,执行销毁回调publicfinalvoiddestroy(){// 标记初始化状态为未完成isInitialized=false;// 删除 OpenGL 程序,释放显存资源GLES20.glDeleteProgram(glProgId);// 执行销毁回调(子类可重写)onDestroy();}// 销毁回调方法:空实现,子类可重写(如释放自定义纹理/缓冲区)publicvoidonDestroy(){}输出尺寸变化处理
功能说明
响应滤镜输出尺寸变化(如屏幕旋转、视图大小调整),更新宽高参数,适配不同尺寸的渲染需求。
// 输出尺寸变化回调:更新输出宽高(子类可重写扩展)publicvoidonOutputSizeChanged(finalintwidth,finalintheight){// 更新输出宽度outputWidth=width;// 更新输出高度outputHeight=height;}核心绘制方法
功能说明
滤镜的核心绘制逻辑,负责绑定 OpenGL 程序、传递顶点/纹理坐标数据、绑定纹理、执行绘制操作,是滤镜渲染的核心入口。
// 绘制方法:接收纹理 ID、顶点缓冲区、纹理坐标缓冲区,执行渲染publicvoidonDraw(finalinttextureId,finalFloatBuffercubeBuffer,finalFloatBuffertextureBuffer){// 绑定当前 OpenGL 程序(激活着色器)GLES20.glUseProgram(glProgId);// 执行待处理的绘制任务(如设置 Uniform 参数)runPendingOnDrawTasks();// 若未初始化,直接返回(避免绘制异常)if(!isInitialized){return;}// 重置顶点缓冲区指针到起始位置cubeBuffer.position(0);// 绑定顶点坐标数据到 position 属性:2 个分量/顶点,浮点型,非归一化,步长 0GLES20.glVertexAttribPointer(glAttribPosition,2,GLES20.GL_FLOAT,false,0,cubeBuffer);// 启用 position 属性数组(OpenGL 渲染时读取该属性)GLES20.glEnableVertexAttribArray(glAttribPosition);// 重置纹理坐标缓冲区指针到起始位置textureBuffer.position(0);// 绑定纹理坐标数据到 inputTextureCoordinate 属性:参数含义同顶点坐标GLES20.glVertexAttribPointer(glAttribTextureCoordinate,2,GLES20.GL_FLOAT,false,0,textureBuffer);// 启用 inputTextureCoordinate 属性数组GLES20.glEnableVertexAttribArray(glAttribTextureCoordinate);// 若纹理 ID 有效(非空纹理)if(textureId!=OpenGlUtils.NO_TEXTURE){// 激活纹理单元 0(OpenGL 多纹理单元机制)GLES20.glActiveTexture(GLES20.GL_TEXTURE0);// 绑定纹理 ID 到 GL_TEXTURE_2D 目标GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureId);// 设置 inputImageTexture Uniform:绑定到纹理单元 0GLES20.glUniform1i(glUniformTexture,0);}// 绘制前回调(子类可重写,如设置自定义 Uniform 参数)onDrawArraysPre();// 执行绘制:使用 TRIANGLE_STRIP 模式,从第 0 个顶点开始,共 4 个顶点(绘制矩形)GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4);// 禁用 position 属性数组(绘制完成后释放)GLES20.glDisableVertexAttribArray(glAttribPosition);// 禁用 inputTextureCoordinate 属性数组GLES20.glDisableVertexAttribArray(glAttribTextureCoordinate);// 解绑纹理(避免后续操作干扰)GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0);}// 绘制前扩展方法:空实现,子类可重写(如设置滤镜强度、颜色等参数)protectedvoidonDrawArraysPre(){}// 执行待处理的绘制任务:遍历链表,执行所有待执行的 Runnable 任务protectedvoidrunPendingOnDrawTasks(){// 同步锁:保证多线程下任务执行安全synchronized(runOnDraw){// 遍历任务链表,直到为空while(!runOnDraw.isEmpty()){// 移除并执行第一个任务runOnDraw.removeFirst().run();}}}状态与属性获取方法
功能说明
提供初始化状态、输出尺寸、OpenGL 程序/属性句柄的获取方法,方便子类或外部调用。
// 获取初始化状态:返回是否已完成 OpenGL 程序初始化publicbooleanisInitialized(){returnisInitialized;}// 获取输出宽度publicintgetOutputWidth(){returnoutputWidth;}// 获取输出高度publicintgetOutputHeight(){returnoutputHeight;}// 获取 OpenGL 程序 IDpublicintgetProgram(){returnglProgId;}// 获取 position 属性句柄publicintgetAttribPosition(){returnglAttribPosition;}// 获取 inputTextureCoordinate 属性句柄publicintgetAttribTextureCoordinate(){returnglAttribTextureCoordinate;}// 获取 inputImageTexture Uniform 句柄publicintgetUniformTexture(){returnglUniformTexture;}Uniform 参数设置方法
功能说明
封装不同类型 Uniform 参数的设置逻辑,将参数设置操作加入绘制任务队列,确保在 OpenGL 绘制线程执行(避免线程安全问题)。
// 设置整型 Uniform 参数:接收 Uniform 位置和整型值protectedvoidsetInteger(finalintlocation,finalintintValue){// 将参数设置操作加入绘制任务队列runOnDraw(newRunnable(){@Overridepublicvoidrun(){// 检查并初始化(避免未初始化时操作)ifNeedInit();// 设置 1 个整型 Uniform 参数GLES20.glUniform1i(location,intValue);}});}// 设置浮点型 Uniform 参数:接收 Uniform 位置和浮点值protectedvoidsetFloat(finalintlocation,finalfloatfloatValue){runOnDraw(newRunnable(){@Overridepublicvoidrun(){ifNeedInit();// 设置 1 个浮点型 Uniform 参数GLES20.glUniform1f(location,floatValue);}});}// 设置 2 维浮点向量 Uniform 参数:接收 Uniform 位置和浮点数组protectedvoidsetFloatVec2(finalintlocation,finalfloat[]arrayValue){runOnDraw(newRunnable(){@Overridepublicvoidrun(){ifNeedInit();// 设置 2 维浮点向量 Uniform 参数(1 个向量,从数组起始位置读取)GLES20.glUniform2fv(location,1,FloatBuffer.wrap(arrayValue));}});}// 设置 3 维浮点向量 Uniform 参数protectedvoidsetFloatVec3(finalintlocation,finalfloat[]arrayValue){runOnDraw(newRunnable(){@Overridepublicvoidrun(){ifNeedInit();GLES20.glUniform3fv(location,1,FloatBuffer.wrap(arrayValue));}});}// 设置 4 维浮点向量 Uniform 参数protectedvoidsetFloatVec4(finalintlocation,finalfloat[]arrayValue){runOnDraw(newRunnable(){@Overridepublicvoidrun(){ifNeedInit();GLES20.glUniform4fv(location,1,FloatBuffer.wrap(arrayValue));}});}// 设置浮点数组 Uniform 参数protectedvoidsetFloatArray(finalintlocation,finalfloat[]arrayValue){runOnDraw(newRunnable(){@Overridepublicvoidrun(){ifNeedInit();// 设置浮点数组 Uniform 参数(数组长度为元素个数)GLES20.glUniform1fv(location,arrayValue.length,FloatBuffer.wrap(arrayValue));}});}// 设置 PointF 类型 Uniform 参数(转换为 2 维向量)protectedvoidsetPoint(finalintlocation,finalPointFpoint){runOnDraw(newRunnable(){@Overridepublicvoidrun(){ifNeedInit();// 将 PointF 转换为 2 维浮点数组float[]vec2=newfloat[2];vec2[0]=point.x;vec2[1]=point.y;// 设置 2 维向量 Uniform 参数GLES20.glUniform2fv(location,1,vec2,0);}});}// 设置 3x3 矩阵 Uniform 参数protectedvoidsetUniformMatrix3f(finalintlocation,finalfloat[]matrix){runOnDraw(newRunnable(){@Overridepublicvoidrun(){ifNeedInit();// 设置 3x3 矩阵 Uniform 参数(1 个矩阵,非转置,从数组起始位置读取)GLES20.glUniformMatrix3fv(location,1,false,matrix,0);}});}// 设置 4x4 矩阵 Uniform 参数protectedvoidsetUniformMatrix4f(finalintlocation,finalfloat[]matrix){runOnDraw(newRunnable(){@Overridepublicvoidrun(){ifNeedInit();// 设置 4x4 矩阵 Uniform 参数(参数含义同 3x3 矩阵)GLES20.glUniformMatrix4fv(location,1,false,matrix,0);}});}// 添加绘制任务到队列:子类可调用,添加自定义绘制操作protectedvoidrunOnDraw(finalRunnablerunnable){// 同步锁:保证多线程下任务添加安全synchronized(runOnDraw){// 将任务添加到链表尾部runOnDraw.addLast(runnable);}}工具方法
功能说明
提供从 assets 目录加载着色器代码的工具方法,以及输入流转字符串的辅助方法,方便加载自定义着色器文件。
// 从 assets 目录加载着色器代码:接收文件路径和上下文publicstaticStringloadShader(Stringfile,Contextcontext){try{// 获取安卓资产管理器AssetManagerassetManager=context.getAssets();// 打开 assets 中的着色器文件InputStreamims=assetManager.open(file);// 将输入流转换为字符串(着色器代码)Stringre=convertStreamToString(ims);// 关闭输入流ims.close();// 返回着色器代码returnre;}catch(Exceptione){// 捕获异常并打印堆栈e.printStackTrace();}// 异常时返回空字符串return"";}// 将输入流转换为字符串:辅助方法,读取 assets 文件内容publicstaticStringconvertStreamToString(java.io.InputStreamis){// 创建 Scanner 对象,以 "\\A" 为分隔符(读取整个流)java.util.Scanners=newjava.util.Scanner(is).useDelimiter("\\A");// 若流中有内容则返回,否则返回空字符串returns.hasNext()?s.next():"";}}总结
GPUImageFilter 作为安卓 GPU 图像滤镜的基类,核心价值在于:
- 封装 OpenGL ES 2.0 底层操作,屏蔽复杂的着色器编译、链接、绘制逻辑,降低子类滤镜的开发成本;
- 提供统一的参数配置接口(Uniform 设置),支持整型、浮点型、矩阵等多种类型参数,适配各类滤镜的定制化需求;
- 实现资源生命周期管理(初始化/销毁),避免显存泄漏;
- 支持从 assets 加载着色器代码,提高滤镜代码的可维护性。