【Android 美颜相机】第二十三天:GPUImageDarkenBlendFilter(变暗混合滤镜)

GPUImageDarkenBlendFilter代码详解

GPUImageDarkenBlendFilter是安卓开源库GPUImage中用于实现变暗混合(Darken Blend) 的滤镜类,基于OpenGL ES的GLSL着色器实现双纹理像素级混合,核心逻辑是取两个纹理像素中更暗的颜色作为混合结果。

本文将逐行解析代码、讲解核心原理,并给出实际使用示例。

完整代码(逐行注释版)

java 复制代码
/*
 * Copyright (C) 2018 CyberAgent, Inc.  // 版权归属:2018年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;

// 继承GPUImageTwoInputFilter:双输入纹理滤镜基类(支持两个纹理混合,如背景+前景)
// 单输入滤镜继承GPUImageFilter,双输入需继承此类
public class GPUImageDarkenBlendFilter extends GPUImageTwoInputFilter {
    // 变暗混合的片段着色器常量(GLSL语言),运行在GPU上处理每个像素的颜色
    public static final String DARKEN_BLEND_FRAGMENT_SHADER = 
        "varying highp vec2 textureCoordinate;\n" +  // 变量1:顶点着色器传递的基础纹理(纹理1)坐标,highp=高精度浮点,vec2=二维向量(UV)
        " varying highp vec2 textureCoordinate2;\n" +  // 变量2:顶点着色器传递的叠加纹理(纹理2)坐标
        "\n" +
        " uniform sampler2D inputImageTexture;\n" +  // 纹理采样器1:绑定基础纹理(如背景图)
        " uniform sampler2D inputImageTexture2;\n" +  // 纹理采样器2:绑定叠加纹理(如前景图)
        " \n" +
        " void main()\n" +  // GLSL主函数:每个像素都会执行一次,是片段着色器的入口
        " {\n" +
        "    lowp vec4 base = texture2D(inputImageTexture, textureCoordinate);\n" +  // 采样基础纹理当前像素颜色:texture2D(采样器, 坐标)返回vec4(RGBA),lowp=低精度(减少GPU开销)
        "    lowp vec4 overlayer = texture2D(inputImageTexture2, textureCoordinate2);\n" +  // 采样叠加纹理当前像素颜色
        "    \n" +
        "    gl_FragColor = vec4(min(overlayer.rgb * base.a, base.rgb * overlayer.a) + overlayer.rgb * (1.0 - base.a) + base.rgb * (1.0 - overlayer.a), 1.0);\n" +  // 核心:计算混合后颜色,赋值给gl_FragColor(GPU内置变量,存储最终像素颜色)
        " }";

    // 无参构造函数:初始化滤镜
    public GPUImageDarkenBlendFilter() {
        // 调用父类构造函数,传入自定义片段着色器
        // 父类GPUImageTwoInputFilter会自动使用默认的双输入顶点着色器(处理纹理坐标传递)
        super(DARKEN_BLEND_FRAGMENT_SHADER);
    }
}

代码模块分段解析

1. 版权与许可声明(头部注释)

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

  • 明确版权归属(CyberAgent公司);
  • 规定使用协议(Apache 2.0):允许商用、修改、分发,但需保留声明,且软件"按现状"提供,作者不承担责任。

2. 包声明

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

指定类归属的包路径,GPUImage库的所有滤镜类都集中在filter包下,便于管理和引用。

3. 类定义

public class GPUImageDarkenBlendFilter extends GPUImageTwoInputFilter

  • 继承关系:GPUImageTwoInputFilter是处理双输入纹理 的基类,适用于"两个纹理混合"的场景(如背景图+前景贴纸混合);若仅处理单纹理(如亮度调整),则继承GPUImageFilter
  • 类功能:实现"变暗混合"------混合两个纹理的像素时,取更暗的颜色值作为最终结果。

4. 片段着色器常量(核心逻辑)

DARKEN_BLEND_FRAGMENT_SHADER是GLSL(OpenGL着色器语言)代码,运行在GPU上,负责计算每个像素的最终颜色,是滤镜的核心。逐行拆解:

代码行 核心含义
varying highp vec2 textureCoordinate 顶点着色器传递的基础纹理坐标 (UV),varying表示变量在顶点/片段着色器间插值传递,highp保证坐标精度
varying highp vec2 textureCoordinate2 顶点着色器传递的叠加纹理坐标(第二个纹理的UV)
uniform sampler2D inputImageTexture 纹理采样器:绑定第一个输入纹理(基础纹理,如背景图),uniform表示整个着色器中值不变
uniform sampler2D inputImageTexture2 纹理采样器:绑定第二个输入纹理(叠加纹理,如前景图)
void main() 片段着色器入口,每个像素执行一次
lowp vec4 base = texture2D(...) 采样基础纹理当前像素的颜色,返回vec4(R, G, B, A)(取值0.0~1.0),lowp降低精度减少GPU开销
lowp vec4 overlayer = texture2D(...) 采样叠加纹理当前像素的颜色
gl_FragColor = vec4(...) 核心混合公式,最终像素颜色赋值给gl_FragColor(GPU内置变量)
混合公式深度解析
glsl 复制代码
min(overlayer.rgb * base.a, base.rgb * overlayer.a) + 
overlayer.rgb * (1.0 - base.a) + 
base.rgb * (1.0 - overlayer.a)

公式兼顾透明度(Alpha通道)变暗混合,拆解:

  • 第一部分:min(overlayer.rgb * base.a, base.rgb * overlayer.a)
    核心"变暗"逻辑:叠加纹理RGB × 基础纹理透明度 与 基础纹理RGB × 叠加纹理透明度 取最小值(更暗的颜色)。
  • 第二部分:overlayer.rgb * (1.0 - base.a)
    基础纹理完全透明时(base.a=0),直接显示叠加纹理。
  • 第三部分:base.rgb * (1.0 - overlayer.a)
    叠加纹理完全透明时(overlayer.a=0),直接显示基础纹理。
  • 最终Alpha通道设为1.0(完全不透明),也可根据需求调整为混合后的透明度。

5. 构造函数

java 复制代码
public GPUImageDarkenBlendFilter() {
    super(DARKEN_BLEND_FRAGMENT_SHADER);
}
  • 调用父类GPUImageTwoInputFilter的构造函数,传入自定义的片段着色器;
  • 父类会自动加载默认的双输入顶点着色器(负责纹理坐标的传递和顶点渲染),无需重复编写;
  • 构造函数执行后,滤镜实例初始化完成,可直接用于纹理混合。

实际使用示例

1. 前置条件

build.gradle中引入GPUImage库依赖(以最新稳定版为例):

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

2. 布局文件(显示处理后的图像)

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">

    <!-- GPUImage库的自定义View,用于显示GPU处理后的图像/视频 -->
    <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.GPUImageDarkenBlendFilter;

public class DarkenBlendDemo extends AppCompatActivity {

    private GPUImageView gpuImageView;
    private GPUImage gpuImage;

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

        // 1. 初始化GPUImageView和GPUImage核心类
        gpuImageView = findViewById(R.id.gpuImageView);
        gpuImage = new GPUImage(this); // GPUImage是滤镜处理的核心入口
        gpuImage.setGLSurfaceView(gpuImageView); // 绑定显示View

        // 2. 加载两个输入纹理(基础图+叠加图)
        // 基础纹理:背景图(res/drawable/background.png)
        Bitmap baseBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.background);
        // 叠加纹理:前景图(res/drawable/overlay.png)
        Bitmap overlayBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.overlay);

        // 3. 创建变暗混合滤镜实例
        GPUImageDarkenBlendFilter darkenFilter = new GPUImageDarkenBlendFilter();

        // 4. 设置滤镜并传入双输入纹理
        gpuImage.setFilter(darkenFilter); // 绑定滤镜到GPUImage
        // 传入双纹理:参数1=基础纹理,参数2=叠加纹理,参数3=双输入滤镜实例
        gpuImage.setImage(baseBitmap, overlayBitmap, darkenFilter);

        // 5. 触发渲染,显示混合后的图像
        gpuImageView.requestRender();
    }
}

4. 使用注意事项

  • 纹理尺寸:建议两张位图尺寸一致,若不一致,GPUImage会自动缩放适配,但可能导致拉伸;
  • 视频处理:该滤镜也可用于视频/相机预览,只需将GPUImage的输入源改为视频流(如gpuImage.setCamera());
  • 自定义扩展:若需调整混合强度,可扩展类添加uniform变量(如mixFactor),在着色器中乘以该因子控制混合程度。

总结

GPUImageDarkenBlendFilter是GPUImage库中典型的双输入混合滤镜,核心设计思路:

  1. 复用基类GPUImageTwoInputFilter的双纹理加载、顶点渲染逻辑;
  2. 通过自定义GLSL片段着色器实现"变暗混合"的像素级算法;
  3. 兼顾透明度处理,保证透明区域的混合效果自然。
相关推荐
小天源4 小时前
银河麒麟 V10(x86_64)离线安装 MySQL 8.0
android·mysql·adb·麒麟v10
2501_915921434 小时前
傻瓜式 HTTPS 抓包,简单抓取iOS设备数据
android·网络协议·ios·小程序·https·uni-app·iphone
csj506 小时前
安卓基础之《(20)—高级控件(2)列表类视图》
android
JMchen1236 小时前
Android计算摄影实战:多帧合成、HDR+与夜景算法深度剖析
android·经验分享·数码相机·算法·移动开发·android-studio
恋猫de小郭7 小时前
Flutter 在 Android 出现随机字体裁剪?其实是图层合并时的边界计算问题
android·flutter·ios
2501_915918417 小时前
把 iOS 性能监控融入日常开发与测试流程的做法
android·ios·小程序·https·uni-app·iphone·webview
leiming68 小时前
Qt视频监控系统开发实战:从视频捕获到照片管理
linux·数码相机·音视频
benjiangliu9 小时前
LINUX系统-09-程序地址空间
android·java·linux
独自破碎E9 小时前
字符串相乘
android·java·jvm