优质广告供应商

广告是为了更好地支持作者创作

GPUImage源码分析与使用(一)

GPUImage简介

  • GPUImage是链式编程,可以处理图片和视频,支持iOS和Mac。
  • GPUImage1.0和2.0是基于 OpenGL ES 封装,1.0是OC版本,2.0是swift版本,3.0是基于Metal的swift版本。
  • AVFoundation在GPUImage框架中是用来捕捉视频的

CoreImage与GPUImage的对比

  • CoreImage是Apple原生的图片视频处理框架
  • 两者都是基于OpenGL ES封装的框架,在性能上没有太大区别

GPUImage的优势

  • 在iOS4.0和5.0低版本上GPUImage会比CoreImage表现更好
  • GPUImage在视频滤镜处理上会比CoreImage表现更好
  • GPUImage是开源框架,支持自定义滤镜
  • GPUImage源码开源,会比CoreImage更易于使用

CoreImage的优势

  • CoreImage是官方框架,使用方便,不需要担心维护问题,第三方框架会有停止更新维护的情况
  • 还支持CPU渲染
  • 与Metal、spriteKit、SceneKit、Core Animation等官方框架能更好的配合使用
  • 支持人脸识别功能,但是识别功能不是很强大
  • 支持对大图进行处理,GPU的纹理限制是4096*4096,对于超出限制的图片,GPUImage会压缩处理导致损失图片质量,CoreImage会把图片拆解成小图处理

GPUImage的特性

  • 有丰富的输入组件,可以处理图片、纹理、视频、二进制数据、UIElement(UIView、CALayer),可以使用GPUImage拍照、处理纹理图片、给视频或拍摄中的视频添加滤镜、添加水印可以使用UIElement
  • 集成了很多内置滤镜:
    • 颜色类有亮度、饱和度、色度、对比度、白平衡等;
    • 图像类有仿射变换、裁剪、高斯模糊、毛玻璃等;
    • 颜色混合类有透明度混合、纹理混合;
    • 效果类有素描、像素画、旋涡等
  • 有很多输出组件,输出方式有UIView、视频文件、纹理、二进制数据等
  • 灵活的滤镜链,模块化功能

GPUImage框架

Source(来源)

GPUImageOutput,常用的为前4个,最终处理的都是纹理

  1. GPUImageVideoCamera(正在录制的视频)
  2. GPUImageStillCamera(拍摄的照片)
  3. GPUImagePicture(静态图片)
  4. GPUImageMovie(一段视频)
  5. GPUImageMovieComposition
  6. GPUImageTextureInput(纹理)
  7. GPUImageRawDataOutput(二进制数据)
  8. GPUImageUIElement(UIView/UILayer)
  9. GPUImageColorConversion

Pipeline(管道)

  1. GPUImageFilterPipeline

Filters(过滤器/滤镜)

  1. Base基类
  • GPUImageFilter,所有Filter父类
  • GPUImageTwoInputFilter
  • GPUImageThreeInputFilter
  • GPUImageFourInputFilter
  • GPUImageTwoPassFilter
  • GPUImageTwoPassTextureSamplingFilter
  • GPUImageFilterGroup
  • GPUImage3x3TextureSamplingFilter
  • GPUImageTwoInputCrossTextureSamplingFilter
  • GPUImageBuffer

内置滤镜

  1. Color processing(色彩处理)

  2. Image processing(图像处理)

  3. Blends(混合)

  4. Effects(效果)

Outputs(输出)

4种输出结果

  1. GPUImageView
  2. GPUImageMovieWriter
  3. GPUImageTextureOutput
  4. GPUImageRawDataOutput

SupportFrameworks

  1. CoreMedia.framework
  2. CoreVideo.framework
  3. OpenGL ES.framework(1.0和2.0使用,3.0使用Metal)
  4. QuartzCore.framework
  5. AV Foundation.framework(捕捉视频)

GPUImage类的介绍

  1. GLProgram,shader管理,编译链接
  2. GPUImageContext,OpenGL Context管理
  3. GPUImageFramebuffer和GPUImageFramebufferCache,buffer管理
  4. Source,数据源
  5. Pipeline,处理管道
  6. Filters,滤镜组合
  7. Outputs,输出组件

图片处理示例

  1. 饱和度滤镜
  • 原图->GPUImagePicture->GPUImageSaturationFilter(饱和度滤镜)->GPUImageView->添加滤镜后的图片
  • 处理流程跟OpenGl ES 一样,只是更具有封装性

GPUImage基类的具体介绍

GPUImage.h

GPUImage所有需要使用到的类的声明基本上都在这里了。

GLProgram

shader管理

GLProgram.h

  1. 变量声明,全局变量声明
{
    NSMutableArray  *attributes; //属性
    NSMutableArray  *uniforms; //通道
    GLuint          program,
    vertShader,  //顶点着色器
    fragShader;  //片元着色器
}
  1. 属性声明,记录编译信息等
@property(readwrite, nonatomic) BOOL initialized;
//编译信息记录
@property(readwrite, copy, nonatomic) NSString *vertexShaderLog;
@property(readwrite, copy, nonatomic) NSString *fragmentShaderLog;
@property(readwrite, copy, nonatomic) NSString *programLog;
  1. 初始化,三种初始化方法,第一种是主要方法
/*
 传入顶点着色器代码和片元着色器代码
 可以传入字符串类型,也可以传入文件类型,比如以.vsh/.fsh/.glsl等为后缀等命名的文件
 GPUImage里自定义滤镜基本都是使用字符串类型
*/
- (id)initWithVertexShaderString:(NSString *)vShaderString 
            fragmentShaderString:(NSString *)fShaderString;
- (id)initWithVertexShaderString:(NSString *)vShaderString 
          fragmentShaderFilename:(NSString *)fShaderFilename;
- (id)initWithVertexShaderFilename:(NSString *)vShaderFilename 
            fragmentShaderFilename:(NSString *)fShaderFilename;
  1. 添加attribute
- (void)addAttribute:(NSString *)attributeName;
  1. attribute和uniform的获取
//获得attribute
- (GLuint)attributeIndex:(NSString *)attributeName;
//获得uniform
- (GLuint)uniformIndex:(NSString *)uniformName;
  1. program操作
- (BOOL)link;
- (void)use;
- (void)validate;

GLProgram.m

  1. 初始化方法,后两种初始化方法都是在获取到本地文件路径后读取glsl字符串,然后调用第一种方法
//传入顶点着色器和片元着色器进行初始化
- (id)initWithVertexShaderString:(NSString *)vShaderString 
            fragmentShaderString:(NSString *)fShaderString;
{
    if ((self = [super init])) 
    {
        _initialized = NO;
        
        attributes = [[NSMutableArray alloc] init];
        uniforms = [[NSMutableArray alloc] init];
        program = glCreateProgram();
        
        //拿到着色器代码进行编译
        if (![self compileShader:&vertShader 
                            type:GL_VERTEX_SHADER 
                          string:vShaderString])
        {
            NSLog(@"Failed to compile vertex shader");
        }
        
        // Create and compile fragment shader
        if (![self compileShader:&fragShader 
                            type:GL_FRAGMENT_SHADER 
                          string:fShaderString])
        {
            NSLog(@"Failed to compile fragment shader");
        }
        
        //着色器编译后附着到program
        glAttachShader(program, vertShader);
        glAttachShader(program, fragShader);
    }
    
    return self;
}
  1. 着色器编译方法
- (BOOL)compileShader:(GLuint *)shader 
                 type:(GLenum)type 
               string:(NSString *)shaderString
{
//    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();

    GLint status;
    const GLchar *source;
    
    //拿到source
    source = 
      (GLchar *)[shaderString UTF8String];
    if (!source)
    {
        NSLog(@"Failed to load vertex shader");
        return NO;
    }
    
    //创建着色器
    *shader = glCreateShader(type);
    glShaderSource(*shader, 1, &source, NULL);
    glCompileShader(*shader);
    
    //获取shader信息
    glGetShaderiv(*shader, GL_COMPILE_STATUS, &status);

    if (status != GL_TRUE)
    {
        GLint logLength;
        glGetShaderiv(*shader, GL_INFO_LOG_LENGTH, &logLength);
        if (logLength > 0)
        {
            GLchar *log = (GLchar *)malloc(logLength);
            glGetShaderInfoLog(*shader, logLength, &logLength, log);
            if (shader == &vertShader)
            {
                self.vertexShaderLog = [NSString stringWithFormat:@"%s", log];
            }
            else
            {
                self.fragmentShaderLog = [NSString stringWithFormat:@"%s", log];
            }

            free(log);
        }
    }   
    
//    CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
//    NSLog(@"Compiled in %f ms", linkTime * 1000.0);

    return status == GL_TRUE;
}
  1. 添加属性addAttribute,使用函数glBindAttribLocation
- (void)addAttribute:(NSString *)attributeName
{
    if (![attributes containsObject:attributeName])
    {
        [attributes addObject:attributeName];
        //glBindAttribLocation函数添加addattribute
        glBindAttribLocation(program, 
                             (GLuint)[attributes indexOfObject:attributeName],
                             [attributeName UTF8String]);
    }
}
  1. 获取attribute和uniform
- (GLuint)attributeIndex:(NSString *)attributeName
{
    return (GLuint)[attributes indexOfObject:attributeName];
}
- (GLuint)uniformIndex:(NSString *)uniformName
{
    return glGetUniformLocation(program, [uniformName UTF8String]);
}
  1. 链接program
- (BOOL)link
{
//    CFAbsoluteTime startTime = CFAbsoluteTimeGetCurrent();

    GLint status;
    
    //链接Program
    glLinkProgram(program);
    
    //获取link状态
    glGetProgramiv(program, GL_LINK_STATUS, &status);
    if (status == GL_FALSE)
        return NO;
    
    //link成功之后删除着色器,从program中去着色器信息
    if (vertShader)
    {
        glDeleteShader(vertShader);
        vertShader = 0;
    }
    if (fragShader)
    {
        glDeleteShader(fragShader);
        fragShader = 0;
    }
    
    self.initialized = YES;

//    CFAbsoluteTime linkTime = (CFAbsoluteTimeGetCurrent() - startTime);
//    NSLog(@"Linked in %f ms", linkTime * 1000.0);

    return YES;
}
  1. 使用和销毁program
- (void)use
{
    glUseProgram(program);
}

- (void)validate;
{
    GLint logLength;
    
    glValidateProgram(program);
    glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
    if (logLength > 0)
    {
        GLchar *log = (GLchar *)malloc(logLength);
        glGetProgramInfoLog(program, logLength, &logLength, log);
        self.programLog = [NSString stringWithFormat:@"%s", log];
        free(log);
    }   
}
  1. dealloc,删除着色器和program
- (void)dealloc
{
    if (vertShader)
        glDeleteShader(vertShader);
        
    if (fragShader)
        glDeleteShader(fragShader);
    
    if (program)
        glDeleteProgram(program);
       
}

GPUImageContext

GPUImage会有很多context,需要GPUImageContext进行context管理

GLProgramGPUImageContext 不需要使用GPUImage的开发者更改和直接使用,这是GPUImage框架本身使用的

GPUImageFramebuffer/GPUImageFramebufferCache

Framebuffer的管理

//开辟Framebuffer
// Initialization and teardown
- (id)initWithSize:(CGSize)framebufferSize;
- (id)initWithSize:(CGSize)framebufferSize textureOptions:(GPUTextureOptions)fboTextureOptions onlyTexture:(BOOL)onlyGenerateTexture;
- (id)initWithSize:(CGSize)framebufferSize overriddenTexture:(GLuint)inputTexture;

优质广告供应商

广告是为了更好地支持作者创作

推荐阅读更多精彩内容