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;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容