【Android 美颜相机】第二十一天:GPUImageChromaKeyBlendFilter (颜色加深混合滤镜)

GPUImageColorBurnBlendFilter 代码全解析

本文将全面解析 GPUImageColorBurnBlendFilter.java 代码,逐行添加注释并分段讲解代码含义、核心原理,同时给出实际使用示例。

该代码是基于 Android 平台 GPUImage 框架的颜色加深(Color Burn)混合滤镜,用于将两张图片/纹理按"颜色加深"模式融合,底层基于 OpenGL ES 着色器实现高效的 GPU 渲染。

完整带注释的代码

java 复制代码
/*
 * Copyright (C) 2018 CyberAgent, Inc.
 * 版权声明:代码版权归属 CyberAgent, Inc.(日本赛博特工公司),2018年
 */
/*
 * Licensed under the Apache License, Version 2.0 (the "License");
 * 许可证声明:代码遵循 Apache License 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.
 */

// 声明类所属包路径:GPUImage 框架的滤镜统一存放包
package jp.co.cyberagent.android.gpuimage.filter;

// 定义颜色加深混合滤镜类,继承自 GPUImageTwoInputFilter(双输入纹理滤镜基类)
// GPUImageTwoInputFilter 封装了双纹理输入的基础渲染逻辑(如纹理绑定、着色器编译等)
public class GPUImageColorBurnBlendFilter extends GPUImageTwoInputFilter {
    // 定义颜色加深混合的片段着色器常量(核心滤镜逻辑)
    // 片段着色器(Fragment Shader):运行在 GPU 上,负责计算每个像素的最终颜色
    public static final String COLOR_BURN_BLEND_FRAGMENT_SHADER = "varying highp vec2 textureCoordinate;\n" +
            // varying:从顶点着色器传递的插值变量;highp:高精度浮点(适配移动端 GPU);vec2:二维向量存储纹理坐标
            // textureCoordinate2:第二个输入纹理的坐标
            " varying highp vec2 textureCoordinate2;\n" +
            "\n" +
            // uniform:着色器执行期间不变的全局常量;sampler2D:2D 纹理采样器(绑定输入纹理)
            // inputImageTexture:第一个输入纹理(底层纹理)的采样器
            " uniform sampler2D inputImageTexture;\n" +
            // inputImageTexture2:第二个输入纹理(顶层纹理)的采样器
            " uniform sampler2D inputImageTexture2;\n" +
            " \n" +
            // 片段着色器主函数:每个像素都会执行一次
            " void main()\n" +
            " {\n" +
            // 从第一个纹理采样颜色:texture2D(采样器, 纹理坐标) 返回 vec4(RGBA,取值 0.0-1.0)
            // mediump:中精度浮点(平衡性能与精度);textureColor:底层纹理当前像素颜色
            "    mediump vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);\n" +
            // 从第二个纹理采样颜色:textureColor2 为顶层纹理当前像素颜色
            "    mediump vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);\n" +
            // 定义白色向量(RGBA 均为 1.0),用于颜色加深公式计算
            "    mediump vec4 whiteColor = vec4(1.0);\n" +
            // 核心公式:颜色加深混合算法
            // gl_FragColor:片段着色器输出变量,代表当前像素最终颜色
            // 公式逻辑:白色 - (白色 - 底层颜色) / 顶层颜色 → 让混合后颜色更暗,实现"加深"效果
            "    gl_FragColor = whiteColor - (whiteColor - textureColor) / textureColor2;\n" +
            " }";

    // 无参构造函数
    public GPUImageColorBurnBlendFilter() {
        // 调用父类构造函数,传入自定义片段着色器
        // 父类会完成:编译顶点着色器(基类默认)+ 片段着色器、链接着色器程序、初始化 OpenGL ES 环境
        super(COLOR_BURN_BLEND_FRAGMENT_SHADER);
    }
}

代码分段核心解析

1. 版权与许可声明(第 1-15 行)

这是开源项目的标准声明,核心作用:

  • 声明版权归属(CyberAgent, Inc.)和年份,该公司是 GPUImage-Android 框架的核心维护者;
  • 明确代码遵循 Apache License 2.0 协议:允许商业使用、修改、分发,但需保留版权声明,且软件"按原样"分发,无任何担保。

2. 包声明(第 17 行)

package jp.co.cyberagent.android.gpuimage.filter;

GPUImage 框架将所有滤镜类统一放在该包下,便于类的管理和外部引用,符合 Android 开发的包命名规范。

3. 类定义(第 19 行)

public class GPUImageColorBurnBlendFilter extends GPUImageTwoInputFilter

  • 继承关系:GPUImageTwoInputFilter 是双输入纹理滤镜的基类,封装了"绑定两个纹理、传递纹理坐标、编译着色器"等重复逻辑,子类只需实现自定义的混合算法;
  • 命名规范:GPUImage 框架的滤镜命名为 GPUImage + 效果名 + BlendFilter(混合滤镜),语义清晰。

4. 片段着色器常量(核心逻辑,第 20-35 行)

着色器是 OpenGL ES 实现滤镜的核心,该常量定义了"颜色加深"的像素计算规则,逐行解析:

代码片段 核心作用
varying highp vec2 textureCoordinate 接收顶点着色器传递的第一个纹理坐标(插值后适配每个像素),highp 保证坐标精度;
uniform sampler2D inputImageTexture 绑定第一个输入纹理(底层图片),是读取纹理颜色的"入口";
texture2D(inputImageTexture, textureCoordinate) 从底层纹理中根据坐标读取像素颜色,返回 vec4 类型(RGBA 四个通道,取值 0.0-1.0);
gl_FragColor = whiteColor - (whiteColor - textureColor) / textureColor2 颜色加深核心公式: - 若顶层纹理为白色(1.0):1.0 - (1.0 - 底层颜色)/1.0 = 底层颜色,无变化; - 若顶层纹理颜色 < 1.0:(1.0 - 底层颜色)/顶层颜色 会放大,最终颜色比底层更暗,实现"加深"; - 注:GPUImage 框架会自动处理"顶层颜色为 0.0 导致除零"的异常。

5. 构造函数(第 37-40 行)

java 复制代码
public GPUImageColorBurnBlendFilter() {
    super(COLOR_BURN_BLEND_FRAGMENT_SHADER);
}
  • 无参构造函数便于外部创建实例;
  • 调用父类构造函数传入自定义片段着色器,父类会自动完成:编译着色器、链接着色器程序、初始化纹理绑定环境,子类无需重复实现底层 OpenGL ES 逻辑。

片段着色器代码深度解析

这段代码是 OpenGL ES 片段着色器(Fragment Shader)的核心实现,用于在 GPU 上逐像素计算颜色加深(Color Burn) 混合效果,是 GPUImageColorBurnBlendFilter 滤镜的核心逻辑。以下逐行拆解解析:

代码逐行注释与含义

c 复制代码
// 拼接后的完整着色器代码(带逐行注释)
varying highp vec2 textureCoordinate;       // 1. 底层纹理坐标变量
varying highp vec2 textureCoordinate2;      // 2. 顶层混合纹理坐标变量

uniform sampler2D inputImageTexture;        // 3. 底层纹理采样器
uniform sampler2D inputImageTexture2;       // 4. 顶层混合纹理采样器

void main()                                 // 5. 着色器主函数(逐像素执行)
{
    // 6. 采样底层纹理当前像素的颜色(RGBA)
    mediump vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
    // 7. 采样顶层纹理当前像素的颜色(RGBA)
    mediump vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
    // 8. 定义白色向量(RGBA 通道均为 1.0)
    mediump vec4 whiteColor = vec4(1.0);
    // 9. 颜色加深核心公式,计算最终像素颜色
    gl_FragColor = whiteColor - (whiteColor - textureColor) / textureColor2;
}

关键行详细解析

1. 纹理坐标变量(第1-2行)
glsl 复制代码
varying highp vec2 textureCoordinate;
varying highp vec2 textureCoordinate2;
  • varying:OpenGL ES 关键字,代表插值变量------该变量的值由顶点着色器(Vertex Shader)传递,并在像素间自动插值(比如图片拉伸/缩放时,坐标会平滑过渡)。
  • highp:精度限定符,代表高精度浮点型high precision),移动端 GPU 中用于纹理坐标可避免精度丢失(坐标偏移、模糊)。
  • vec2:二维向量类型,存储纹理坐标(范围 0.0~1.0(0,0) 对应纹理左上角,(1,1) 对应右下角)。
  • textureCoordinate:底层纹理(被混合的基础图片)的坐标;textureCoordinate2:顶层纹理(用于混合的叠加图片)的坐标。
2. 纹理采样器(第3-4行)
glsl 复制代码
uniform sampler2D inputImageTexture;
uniform sampler2D inputImageTexture2;
  • uniform:OpenGL ES 关键字,代表全局常量------着色器执行期间值不变,由 Java 层(GPUImage 框架)传入(绑定具体的纹理ID)。
  • sampler2D:2D 纹理采样器类型,是读取纹理像素颜色的"接口",可以理解为"纹理的句柄"。
  • inputImageTexture:绑定底层纹理;inputImageTexture2:绑定顶层混合纹理。
3. 主函数与像素采样(第5-8行)
glsl 复制代码
void main() {
    mediump vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
    mediump vec4 textureColor2 = texture2D(inputImageTexture2, textureCoordinate2);
    mediump vec4 whiteColor = vec4(1.0);
    // ...
}
  • void main():片段着色器的入口函数,每个像素都会执行一次,是像素颜色计算的核心入口。
  • mediump:精度限定符,代表中精度浮点型medium precision),平衡移动端 GPU 的性能与精度(颜色计算无需超高精度)。
  • vec4:四维向量类型,存储像素的 RGBA 颜色值(每个通道取值 0.0~1.0,比如 vec4(1.0,0.0,0.0,1.0) 是纯红色)。
  • texture2D(sampler2D sampler, vec2 coord):OpenGL ES 内置函数,根据纹理采样器和坐标,读取对应位置的像素颜色,返回 vec4 类型的 RGBA 值。
4. 核心混合公式(第9行)
glsl 复制代码
gl_FragColor = whiteColor - (whiteColor - textureColor) / textureColor2;
  • gl_FragColor:OpenGL ES 内置输出变量,代表当前像素的最终颜色,赋值后会直接渲染到屏幕/纹理。
  • 公式原理 (颜色加深算法):
    拆解公式(以单个颜色通道为例,如 R 通道):
    最终R值 = 1.0 - (1.0 - 底层R值) / 顶层R值
    • 若顶层纹理像素为白色(1.0):1.0 - (1.0 - 底层R值)/1.0 = 底层R值 → 混合后无变化;
    • 若顶层纹理像素颜色 < 1.0(非白色):(1.0 - 底层R值)/顶层R值 会被放大,最终 R 值 < 底层 R 值 → 颜色更暗,实现"加深"效果;
    • 若顶层纹理像素为黑色(0.0):公式会出现除零,GPUImage 框架底层会自动处理(比如限制最小值),避免渲染异常。

颜色加深效果的视觉特征

颜色加深混合的核心是让底层颜色变得更暗,模拟"用颜料加深底色"的效果:

  • 顶层纹理越暗(通道值越小),底层颜色加深越明显;
  • 顶层纹理为白色时,底层颜色完全不变;
  • 顶层纹理为黑色时,底层颜色会被加深为纯黑。

补充注意事项

  1. 精度选择 :纹理坐标用 highp(避免坐标偏移),颜色计算用 mediump(节省 GPU 算力),是移动端 OpenGL ES 的最佳实践;
  2. 除零保护 :若顶层纹理某通道值为 0.0,公式会触发除零,实际开发中可增加边界判断(如 textureColor2 = max(textureColor2, 0.001)),避免颜色异常;
  3. 通道同步 :公式会同时作用于 RGBA 四个通道,若只需混合 RGB(忽略透明度),可修改公式为 gl_FragColor = vec4(whiteColor.rgb - (whiteColor.rgb - textureColor.rgb) / textureColor2.rgb, textureColor.a);

该滤镜的实际使用方法

1. 前置条件:集成 GPUImage 框架

在项目 build.gradle 中添加依赖(以 2.1.0 版本为例):

gradle 复制代码
dependencies {
    implementation 'jp.co.cyberagent.android:gpuimage:2.1.0'
}

2. 布局文件(显示滤镜效果)

res/layout/activity_main.xml 中添加 GPUImageView(GPUImage 框架的图片显示控件):

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <jp.co.cyberagent.android.gpuimage.GPUImageView
        android:id="@+id/gpuImageView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

3. 代码实现(加载图片 + 应用滤镜)

java 复制代码
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import jp.co.cyberagent.android.gpuimage.GPUImage;
import jp.co.cyberagent.android.gpuimage.GPUImageView;
import jp.co.cyberagent.android.gpuimage.filter.GPUImageColorBurnBlendFilter;

public class MainActivity extends AppCompatActivity {

    private GPUImage mGPUImage;
    private GPUImageView mGPUImageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 1. 初始化 GPUImage 核心类和显示控件
        mGPUImageView = findViewById(R.id.gpuImageView);
        mGPUImage = new GPUImage(this);
        mGPUImage.setGLSurfaceView(mGPUImageView); // 绑定显示控件

        // 2. 加载两张纹理图片(底层图 + 顶层混合图)
        Bitmap baseBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.base_img); // 底层图
        Bitmap blendBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.blend_img); // 顶层混合图

        // 3. 创建颜色加深混合滤镜实例
        GPUImageColorBurnBlendFilter colorBurnFilter = new GPUImageColorBurnBlendFilter();

        // 4. 设置滤镜:双输入滤镜需传入第二个纹理
        mGPUImage.setFilter(colorBurnFilter); // 设置主滤镜
        colorBurnFilter.setSecondImage(blendBitmap); // 传入第二个纹理(顶层图)

        // 5. 加载底层图并显示滤镜效果
        mGPUImage.setImage(baseBitmap);
    }

    // 可选:回收资源,避免内存泄漏
    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mGPUImage != null) {
            mGPUImage.deleteImage();
        }
    }
}

4. 使用注意事项

  • 纹理尺寸:两张图片尺寸建议一致,若不一致,GPUImage 会自动缩放纹理坐标,但可能导致混合效果偏差;
  • 性能优化:着色器中优先使用 mediump(中精度),减少 highp 以降低 GPU 开销;
  • 异常处理:若顶层纹理颜色为 0.0,需在着色器中增加边界判断(如 textureColor2 = max(textureColor2, 0.001);),避免除零错误;
  • 资源回收:使用完 Bitmap 后及时调用 recycle()GPUImage 实例在页面销毁时调用 deleteImage() 释放 OpenGL 资源。

总结

GPUImageColorBurnBlendFilter 是基于 GPUImage 框架的双输入混合滤镜,核心通过 OpenGL ES 片段着色器实现"颜色加深"算法。

其代码结构简洁,复用了基类的双纹理渲染逻辑,仅需实现自定义的混合公式;实际使用时只需集成框架、传入两张纹理即可快速实现颜色加深效果,适用于图片编辑、视频处理等移动端视觉场景。

相关推荐
yantaohk3 小时前
【2025亲测】中兴B860AV3.2M完美刷机包ATV版本安卓9-解决1G运存BUG,开ADB已ROOT
android·嵌入式硬件·adb·云计算
乐观勇敢坚强的老彭4 小时前
c++信奥寒假营集训01
android·java·c++
kdniao14 小时前
PHP 页面中如何实现根据快递单号查询物流轨迹?对接快递鸟在途监控 API 实操
android·开发语言·php
言之。4 小时前
MacBook M3 Pro:React Native 安卓开发
android·react native·react.js
感觉不怎么会4 小时前
Android 13 - 对讲app后台休眠后无法录音
android·linux
Minilinux20185 小时前
Android系列之 屏幕触控机制(一)
android·屏幕触控·andorid touch·viewroot
冰语竹5 小时前
Android学习-随笔(安装后设置路径)
android·学习
有位神秘人6 小时前
Android中获取当前屏幕的宽高工具类
android
Yang-Never6 小时前
Open GL ES -> 应用前后台、Recent切换,SurfaceView纹理贴图闪烁问题分析解决
android·开发语言·kotlin·android studio·贴图