
GPUImageFilter解析
GPUImageFilter 是 Android 平台基于 OpenGL ES 2.0 实现的 GPU 图像滤镜核心基类,为各类图像滤镜(如美颜、风格化特效)提供了统一的初始化、绘制、资源管理、参数配置能力。
本文将逐行解析该类的代码结构、实现逻辑及每行代码的含义。
版权声明与包/类导入
功能说明
该部分包含开源协议声明、包路径定义,以及实现滤镜功能所需的核心类导入,是 Java 类的基础结构。
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 安卓库滤镜模块
package jp.co.cyberagent.android.gpuimage.filter;
// 安卓上下文类,用于获取资源(如 Assets)
import android.content.Context;
// 安卓资产管理类,用于读取 assets 目录下的着色器文件
import android.content.res.AssetManager;
// 二维点坐标类,用于传递纹理/顶点坐标点
import android.graphics.PointF;
// OpenGL ES 2.0 核心 API 类,提供 OpenGL 底层调用
import android.opengl.GLES20;
// 输入流类,用于读取 assets 中的着色器文件内容
import java.io.InputStream;
// 浮点缓冲区类,用于存储顶点/纹理坐标数据(适配 OpenGL 数据格式)
import java.nio.FloatBuffer;
// 链表类,用于存储待执行的绘制任务
import java.util.LinkedList;
// OpenGL 工具类(自定义),封装着色器加载、编译等通用操作
import jp.co.cyberagent.android.gpuimage.util.OpenGlUtils;
类定义与核心常量
功能说明
定义滤镜基类的核心常量(默认着色器代码),顶点着色器负责处理顶点坐标变换,片元着色器负责处理像素颜色渲染,默认实现"无滤镜"效果(直接输出原纹理)。
java
// 定义 GPUImageFilter 公共类,所有具体滤镜(如美白、磨皮)均继承此类
public class GPUImageFilter {
// 无滤镜顶点着色器代码:处理顶点坐标和纹理坐标传递
public static final String NO_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" + // 传递纹理坐标到片元着色器
"}";
// 无滤镜片元着色器代码:直接采样原纹理像素,无任何颜色修改
public static final String NO_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 句柄、输出尺寸、初始化状态等,是滤镜运行的核心数据载体。
java
// 待执行的绘制任务链表:存储需要在绘制阶段执行的 OpenGL 操作(如设置 Uniform 参数)
private final LinkedList<Runnable> runOnDraw;
// 顶点着色器代码(可自定义)
private final String vertexShader;
// 片元着色器代码(可自定义)
private final String fragmentShader;
// OpenGL 程序 ID:着色器编译链接后的程序句柄
private int glProgId;
// position 属性位置:对应顶点着色器中 "position" 变量的句柄
private int glAttribPosition;
// 输入纹理 Uniform 位置:对应片元着色器中 "inputImageTexture" 变量的句柄
private int glUniformTexture;
// 纹理坐标属性位置:对应顶点着色器中 "inputTextureCoordinate" 变量的句柄
private int glAttribTextureCoordinate;
// 滤镜输出宽度
private int outputWidth;
// 滤镜输出高度
private int outputHeight;
// 初始化状态标记:标记 OpenGL 程序是否已初始化完成
private boolean isInitialized;
构造方法
功能说明
提供无参/有参构造方法,无参构造默认使用"无滤镜"着色器,有参构造支持传入自定义顶点/片元着色器,适配不同滤镜的定制化需求。
java
// 无参构造方法:默认使用无滤镜顶点/片元着色器
public GPUImageFilter() {
this(NO_FILTER_VERTEX_SHADER, NO_FILTER_FRAGMENT_SHADER);
}
// 有参构造方法:接收自定义顶点/片元着色器代码
public GPUImageFilter(final String vertexShader, final String fragmentShader) {
// 初始化绘制任务链表
runOnDraw = new LinkedList<>();
// 赋值自定义顶点着色器代码
this.vertexShader = vertexShader;
// 赋值自定义片元着色器代码
this.fragmentShader = fragmentShader;
}
初始化相关方法
功能说明
封装 OpenGL 程序的初始化逻辑,包括着色器编译链接、属性/Uniform 句柄获取,提供初始化检查、回调扩展等能力,确保滤镜绘制前完成必要的初始化。
java
// 内部初始化入口方法:封装初始化流程(私有,仅内部调用)
private final void init() {
// 执行初始化核心逻辑(子类可重写)
onInit();
// 初始化完成回调(子类可重写扩展)
onInitialized();
}
// 初始化核心逻辑:编译链接着色器程序,获取属性/Uniform 句柄
public void onInit() {
// 加载并编译顶点/片元着色器,链接为 OpenGL 程序,返回程序 ID
glProgId = 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 参数)
public void onInitialized() {
}
// 初始化检查方法:若未初始化则执行 init()
public void ifNeedInit() {
if (!isInitialized) init();
}
资源销毁相关方法
功能说明
封装 OpenGL 程序的销毁逻辑,释放显存资源,提供销毁回调扩展,避免内存泄漏。
java
// 最终销毁方法:标记未初始化,删除 OpenGL 程序,执行销毁回调
public final void destroy() {
// 标记初始化状态为未完成
isInitialized = false;
// 删除 OpenGL 程序,释放显存资源
GLES20.glDeleteProgram(glProgId);
// 执行销毁回调(子类可重写)
onDestroy();
}
// 销毁回调方法:空实现,子类可重写(如释放自定义纹理/缓冲区)
public void onDestroy() {
}
输出尺寸变化处理
功能说明
响应滤镜输出尺寸变化(如屏幕旋转、视图大小调整),更新宽高参数,适配不同尺寸的渲染需求。
java
// 输出尺寸变化回调:更新输出宽高(子类可重写扩展)
public void onOutputSizeChanged(final int width, final int height) {
// 更新输出宽度
outputWidth = width;
// 更新输出高度
outputHeight = height;
}
核心绘制方法
功能说明
滤镜的核心绘制逻辑,负责绑定 OpenGL 程序、传递顶点/纹理坐标数据、绑定纹理、执行绘制操作,是滤镜渲染的核心入口。
java
// 绘制方法:接收纹理 ID、顶点缓冲区、纹理坐标缓冲区,执行渲染
public void onDraw(final int textureId, final FloatBuffer cubeBuffer,
final FloatBuffer textureBuffer) {
// 绑定当前 OpenGL 程序(激活着色器)
GLES20.glUseProgram(glProgId);
// 执行待处理的绘制任务(如设置 Uniform 参数)
runPendingOnDrawTasks();
// 若未初始化,直接返回(避免绘制异常)
if (!isInitialized) {
return;
}
// 重置顶点缓冲区指针到起始位置
cubeBuffer.position(0);
// 绑定顶点坐标数据到 position 属性:2 个分量/顶点,浮点型,非归一化,步长 0
GLES20.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:绑定到纹理单元 0
GLES20.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);
}
// 绘制前扩展方法:空实现,子类可重写(如设置滤镜强度、颜色等参数)
protected void onDrawArraysPre() {
}
// 执行待处理的绘制任务:遍历链表,执行所有待执行的 Runnable 任务
protected void runPendingOnDrawTasks() {
// 同步锁:保证多线程下任务执行安全
synchronized (runOnDraw) {
// 遍历任务链表,直到为空
while (!runOnDraw.isEmpty()) {
// 移除并执行第一个任务
runOnDraw.removeFirst().run();
}
}
}
状态与属性获取方法
功能说明
提供初始化状态、输出尺寸、OpenGL 程序/属性句柄的获取方法,方便子类或外部调用。
java
// 获取初始化状态:返回是否已完成 OpenGL 程序初始化
public boolean isInitialized() {
return isInitialized;
}
// 获取输出宽度
public int getOutputWidth() {
return outputWidth;
}
// 获取输出高度
public int getOutputHeight() {
return outputHeight;
}
// 获取 OpenGL 程序 ID
public int getProgram() {
return glProgId;
}
// 获取 position 属性句柄
public int getAttribPosition() {
return glAttribPosition;
}
// 获取 inputTextureCoordinate 属性句柄
public int getAttribTextureCoordinate() {
return glAttribTextureCoordinate;
}
// 获取 inputImageTexture Uniform 句柄
public int getUniformTexture() {
return glUniformTexture;
}
Uniform 参数设置方法
功能说明
封装不同类型 Uniform 参数的设置逻辑,将参数设置操作加入绘制任务队列,确保在 OpenGL 绘制线程执行(避免线程安全问题)。
java
// 设置整型 Uniform 参数:接收 Uniform 位置和整型值
protected void setInteger(final int location, final int intValue) {
// 将参数设置操作加入绘制任务队列
runOnDraw(new Runnable() {
@Override
public void run() {
// 检查并初始化(避免未初始化时操作)
ifNeedInit();
// 设置 1 个整型 Uniform 参数
GLES20.glUniform1i(location, intValue);
}
});
}
// 设置浮点型 Uniform 参数:接收 Uniform 位置和浮点值
protected void setFloat(final int location, final float floatValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
ifNeedInit();
// 设置 1 个浮点型 Uniform 参数
GLES20.glUniform1f(location, floatValue);
}
});
}
// 设置 2 维浮点向量 Uniform 参数:接收 Uniform 位置和浮点数组
protected void setFloatVec2(final int location, final float[] arrayValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
ifNeedInit();
// 设置 2 维浮点向量 Uniform 参数(1 个向量,从数组起始位置读取)
GLES20.glUniform2fv(location, 1, FloatBuffer.wrap(arrayValue));
}
});
}
// 设置 3 维浮点向量 Uniform 参数
protected void setFloatVec3(final int location, final float[] arrayValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
ifNeedInit();
GLES20.glUniform3fv(location, 1, FloatBuffer.wrap(arrayValue));
}
});
}
// 设置 4 维浮点向量 Uniform 参数
protected void setFloatVec4(final int location, final float[] arrayValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
ifNeedInit();
GLES20.glUniform4fv(location, 1, FloatBuffer.wrap(arrayValue));
}
});
}
// 设置浮点数组 Uniform 参数
protected void setFloatArray(final int location, final float[] arrayValue) {
runOnDraw(new Runnable() {
@Override
public void run() {
ifNeedInit();
// 设置浮点数组 Uniform 参数(数组长度为元素个数)
GLES20.glUniform1fv(location, arrayValue.length, FloatBuffer.wrap(arrayValue));
}
});
}
// 设置 PointF 类型 Uniform 参数(转换为 2 维向量)
protected void setPoint(final int location, final PointF point) {
runOnDraw(new Runnable() {
@Override
public void run() {
ifNeedInit();
// 将 PointF 转换为 2 维浮点数组
float[] vec2 = new float[2];
vec2[0] = point.x;
vec2[1] = point.y;
// 设置 2 维向量 Uniform 参数
GLES20.glUniform2fv(location, 1, vec2, 0);
}
});
}
// 设置 3x3 矩阵 Uniform 参数
protected void setUniformMatrix3f(final int location, final float[] matrix) {
runOnDraw(new Runnable() {
@Override
public void run() {
ifNeedInit();
// 设置 3x3 矩阵 Uniform 参数(1 个矩阵,非转置,从数组起始位置读取)
GLES20.glUniformMatrix3fv(location, 1, false, matrix, 0);
}
});
}
// 设置 4x4 矩阵 Uniform 参数
protected void setUniformMatrix4f(final int location, final float[] matrix) {
runOnDraw(new Runnable() {
@Override
public void run() {
ifNeedInit();
// 设置 4x4 矩阵 Uniform 参数(参数含义同 3x3 矩阵)
GLES20.glUniformMatrix4fv(location, 1, false, matrix, 0);
}
});
}
// 添加绘制任务到队列:子类可调用,添加自定义绘制操作
protected void runOnDraw(final Runnable runnable) {
// 同步锁:保证多线程下任务添加安全
synchronized (runOnDraw) {
// 将任务添加到链表尾部
runOnDraw.addLast(runnable);
}
}
工具方法
功能说明
提供从 assets 目录加载着色器代码的工具方法,以及输入流转字符串的辅助方法,方便加载自定义着色器文件。
java
// 从 assets 目录加载着色器代码:接收文件路径和上下文
public static String loadShader(String file, Context context) {
try {
// 获取安卓资产管理器
AssetManager assetManager = context.getAssets();
// 打开 assets 中的着色器文件
InputStream ims = assetManager.open(file);
// 将输入流转换为字符串(着色器代码)
String re = convertStreamToString(ims);
// 关闭输入流
ims.close();
// 返回着色器代码
return re;
} catch (Exception e) {
// 捕获异常并打印堆栈
e.printStackTrace();
}
// 异常时返回空字符串
return "";
}
// 将输入流转换为字符串:辅助方法,读取 assets 文件内容
public static String convertStreamToString(java.io.InputStream is) {
// 创建 Scanner 对象,以 "\\A" 为分隔符(读取整个流)
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
// 若流中有内容则返回,否则返回空字符串
return s.hasNext() ? s.next() : "";
}
}
总结
GPUImageFilter 作为安卓 GPU 图像滤镜的基类,核心价值在于:
- 封装 OpenGL ES 2.0 底层操作,屏蔽复杂的着色器编译、链接、绘制逻辑,降低子类滤镜的开发成本;
- 提供统一的参数配置接口(Uniform 设置),支持整型、浮点型、矩阵等多种类型参数,适配各类滤镜的定制化需求;
- 实现资源生命周期管理(初始化/销毁),避免显存泄漏;
- 支持从 assets 加载着色器代码,提高滤镜代码的可维护性。
