GPUImage源码解读(七)

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情
  • GPUImagePicture

从名称就可以知道GPUImagePicture是GPUImage框架中处理与图片相关的类,它的主要作用是将UIImage或CGImage转化为纹理对象。GPUImagePicture继承自GPUImageOutput,从而可以知道它能够作为输出,由于它没有实现GPUImageInput协议,不能处理输入。因此,常常作为响应链源。

初始化方法 ,所有初始化方法最后都会调用最后一个初始化方法

// Initialization and teardown
// 通过图片URL初始化
- (id)initWithURL:(NSURL *)url;
// 通过UIImage或CGImage初始化
- (id)initWithImage:(UIImage *)newImageSource;
- (id)initWithCGImage:(CGImageRef)newImageSource;

// 通过UIImage或CGImage、是否平滑缩放、是否去除预乘alpha来初始化
- (id)initWithImage:(UIImage *)newImageSource smoothlyScaleOutput:(BOOL)smoothlyScaleOutput;
- (id)initWithCGImage:(CGImageRef)newImageSource smoothlyScaleOutput:(BOOL)smoothlyScaleOutput;//所有初始化方法最后都会调用这个方法
  • 实现方法
- (id)initWithCGImage:(CGImageRef)newImageSource smoothlyScaleOutput:(BOOL)smoothlyScaleOutput;
{
    if (!(self = [super init]))
    {
        return nil;
    }

    hasProcessedImage = NO;
    self.shouldSmoothlyScaleOutput = smoothlyScaleOutput;
    imageUpdateSemaphore = dispatch_semaphore_create(0);
    dispatch_semaphore_signal(imageUpdateSemaphore);


    //1获取图片适合的宽高(不能超出OpenGL ES允许的最大纹理宽高)
    // TODO: Dispatch this whole thing asynchronously to move image loading off main thread
    CGFloat widthOfImage = CGImageGetWidth(newImageSource);
    CGFloat heightOfImage = CGImageGetHeight(newImageSource);

    // If passed an empty image reference, CGContextDrawImage will fail in future versions of the SDK.
    NSAssert( widthOfImage > 0 && heightOfImage > 0, @"Passed image must not be empty - it should be at least 1px tall and wide");

    pixelSizeOfImage = CGSizeMake(widthOfImage, heightOfImage);
    CGSize pixelSizeToUseForTexture = pixelSizeOfImage;

    //是否要重绘
    BOOL shouldRedrawUsingCoreGraphics = NO;

    // For now, deal with images larger than the maximum texture size by resizing to be within that limit
    //openglES 允许的最大值
    CGSize scaledImageSizeToFitOnGPU = [GPUImageContext sizeThatFitsWithinATextureForSize:pixelSizeOfImage];
    if (!CGSizeEqualToSize(scaledImageSizeToFitOnGPU, pixelSizeOfImage))
    {
        pixelSizeOfImage = scaledImageSizeToFitOnGPU;
        pixelSizeToUseForTexture = pixelSizeOfImage;
        shouldRedrawUsingCoreGraphics = YES;
    }

    //
    if (self.shouldSmoothlyScaleOutput)
    {

        //2如果使用了smoothlyScaleOutput,需要调整宽高为接近2的幂的值,调整后必须重绘; log2=0.3010 ceil:如果参数是小数,则求最小的整数但不小于本身. @see round:如果参数是小数,则求本身的四舍五入。floor:如果参数是小数,则求最大的整数但不大于本身.
        // In order to use mipmaps, you need to provide power-of-two textures, so convert to the next largest power of two and stretch to fill
        CGFloat powerClosestToWidth = ceil(log2(pixelSizeOfImage.width));
        CGFloat powerClosestToHeight = ceil(log2(pixelSizeOfImage.height));

        pixelSizeToUseForTexture = CGSizeMake(pow(2.0, powerClosestToWidth), pow(2.0, powerClosestToHeight));

        shouldRedrawUsingCoreGraphics = YES;
    }

    GLubyte *imageData = NULL;
    CFDataRef dataFromImageDataProvider = NULL;
    GLenum format = GL_BGRA;

    //3如果不用重绘,则获取大小、alpha等信息;
    if (!shouldRedrawUsingCoreGraphics) {
        /* Check that the memory layout is compatible with GL, as we cannot use glPixelStore to
         * tell GL about the memory layout with GLES.
         */
        //CGImageGetBytesPerRow 每行的字节数  CGImageGetBitsPerPixel每个像素的位数 CGImageGetBitsPerComponent 每个字节的组成部分
        //RGBA格式不符合就要重绘
        if (CGImageGetBytesPerRow(newImageSource) != CGImageGetWidth(newImageSource) * 4 ||
            CGImageGetBitsPerPixel(newImageSource) != 32 ||
            CGImageGetBitsPerComponent(newImageSource) != 8)
        {
            shouldRedrawUsingCoreGraphics = YES;
        } else {
            /* Check that the bitmap pixel format is compatible with GL */
            //位图的组成部分信息
            CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(newImageSource);
            if ((bitmapInfo & kCGBitmapFloatComponents) != 0) {
                /* We don't support float components for use directly in GL */
                shouldRedrawUsingCoreGraphics = YES;
            } else {
                CGBitmapInfo byteOrderInfo = bitmapInfo & kCGBitmapByteOrderMask;
                if (byteOrderInfo == kCGBitmapByteOrder32Little) {
                    /* Little endian, for alpha-first we can use this bitmap directly in GL */
                    CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
                    if (alphaInfo != kCGImageAlphaPremultipliedFirst && alphaInfo != kCGImageAlphaFirst &&
                        alphaInfo != kCGImageAlphaNoneSkipFirst) {
                        shouldRedrawUsingCoreGraphics = YES;
                    }
                } else if (byteOrderInfo == kCGBitmapByteOrderDefault || byteOrderInfo == kCGBitmapByteOrder32Big) {
                    /* Big endian, for alpha-last we can use this bitmap directly in GL */
                    CGImageAlphaInfo alphaInfo = bitmapInfo & kCGBitmapAlphaInfoMask;
                    if (alphaInfo != kCGImageAlphaPremultipliedLast && alphaInfo != kCGImageAlphaLast &&
                        alphaInfo != kCGImageAlphaNoneSkipLast) {
                        shouldRedrawUsingCoreGraphics = YES;
                    } else {
                        /* Can access directly using GL_RGBA pixel format */
                        format = GL_RGBA;
                    }
                }
            }
        }
    }

    //    CFAbsoluteTime elapsedTime, startTime = CFAbsoluteTimeGetCurrent();

    //4需要重绘,则使用CoreGraphics重绘
    if (shouldRedrawUsingCoreGraphics)
    {
        // For resized or incompatible image: redraw
        imageData = (GLubyte *) calloc(1, (int)pixelSizeToUseForTexture.width * (int)pixelSizeToUseForTexture.height * 4);

        CGColorSpaceRef genericRGBColorspace = CGColorSpaceCreateDeviceRGB();

        CGContextRef imageContext = CGBitmapContextCreate(imageData, (size_t)pixelSizeToUseForTexture.width, (size_t)pixelSizeToUseForTexture.height, 8, (size_t)pixelSizeToUseForTexture.width * 4, genericRGBColorspace,  kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst);
        //        CGContextSetBlendMode(imageContext, kCGBlendModeCopy); // From Technical Q&A QA1708: http://developer.apple.com/library/ios/#qa/qa1708/_index.html
        CGContextDrawImage(imageContext, CGRectMake(0.0, 0.0, pixelSizeToUseForTexture.width, pixelSizeToUseForTexture.height), newImageSource);
        CGContextRelease(imageContext);
        CGColorSpaceRelease(genericRGBColorspace);
    }
    else
    {
        // Access the raw image bytes directly
        dataFromImageDataProvider = CGDataProviderCopyData(CGImageGetDataProvider(newImageSource));
        imageData = (GLubyte *)CFDataGetBytePtr(dataFromImageDataProvider);
    }

    //    elapsedTime = (CFAbsoluteTimeGetCurrent() - startTime) * 1000.0;
    //    NSLog(@"Core Graphics drawing time: %f", elapsedTime);

    //    CGFloat currentRedTotal = 0.0f, currentGreenTotal = 0.0f, currentBlueTotal = 0.0f, currentAlphaTotal = 0.0f;
    //  NSUInteger totalNumberOfPixels = round(pixelSizeToUseForTexture.width * pixelSizeToUseForTexture.height);
    //
    //    for (NSUInteger currentPixel = 0; currentPixel < totalNumberOfPixels; currentPixel++)
    //    {
    //        currentBlueTotal += (CGFloat)imageData[(currentPixel * 4)] / 255.0f;
    //        currentGreenTotal += (CGFloat)imageData[(currentPixel * 4) + 1] / 255.0f;
    //        currentRedTotal += (CGFloat)imageData[(currentPixel * 4 + 2)] / 255.0f;
    //        currentAlphaTotal += (CGFloat)imageData[(currentPixel * 4) + 3] / 255.0f;
    //    }
    //
    //    NSLog(@"Debug, average input image red: %f, green: %f, blue: %f, alpha: %f", currentRedTotal / (CGFloat)totalNumberOfPixels, currentGreenTotal / (CGFloat)totalNumberOfPixels, currentBlueTotal / (CGFloat)totalNumberOfPixels, currentAlphaTotal / (CGFloat)totalNumberOfPixels);

    runSynchronouslyOnVideoProcessingQueue(^{
        [GPUImageContext useImageProcessingContext];

        outputFramebuffer = [[GPUImageContext sharedFramebufferCache] fetchFramebufferForSize:pixelSizeToUseForTexture onlyTexture:YES];
        [outputFramebuffer disableReferenceCounting];

        glBindTexture(GL_TEXTURE_2D, [outputFramebuffer texture]);
        if (self.shouldSmoothlyScaleOutput)
        {
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
        }
        // no need to use self.outputTextureOptions here since pictures need this texture formats and type
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, (int)pixelSizeToUseForTexture.width, (int)pixelSizeToUseForTexture.height, 0, format, GL_UNSIGNED_BYTE, imageData);

        //是否生成mipmaps
        if (self.shouldSmoothlyScaleOutput)
        {
            glGenerateMipmap(GL_TEXTURE_2D);
        }
        glBindTexture(GL_TEXTURE_2D, 0);
    });

    //7最后释放资源
    if (shouldRedrawUsingCoreGraphics)
    {
        free(imageData);
    }
    else
    {
        if (dataFromImageDataProvider)
        {
            CFRelease(dataFromImageDataProvider);
        }
    }

    return self;
}
  • 其它方法。这些方法主要是与图片处理相关。
// 处理图片
- (void)processImage;
{
    [self processImageWithCompletionHandler:nil];
}

// 处理图片,可以传入处理完回调的block
- (BOOL)processImageWithCompletionHandler:(void (^)(void))completion;
{
    hasProcessedImage = YES;

    //    dispatch_semaphore_wait(imageUpdateSemaphore, DISPATCH_TIME_FOREVER);

    if (dispatch_semaphore_wait(imageUpdateSemaphore, DISPATCH_TIME_NOW) != 0)
    {
        return NO;
    }

    runAsynchronouslyOnVideoProcessingQueue(^{        
        for (id<GPUImageInput> currentTarget in targets)
        {
            NSInteger indexOfObject = [targets indexOfObject:currentTarget];
            NSInteger textureIndexOfTarget = [[targetTextureIndices objectAtIndex:indexOfObject] integerValue];

            [currentTarget setCurrentlyReceivingMonochromeInput:NO];
            [currentTarget setInputSize:pixelSizeOfImage atIndex:textureIndexOfTarget];
            [currentTarget setInputFramebuffer:outputFramebuffer atIndex:textureIndexOfTarget];
            [currentTarget newFrameReadyAtTime:kCMTimeIndefinite atIndex:textureIndexOfTarget];
        }

        dispatch_semaphore_signal(imageUpdateSemaphore);

        if (completion != nil) {
            completion();
        }
    });

    return YES;
}

- (void)processImageUpToFilter:(GPUImageOutput<GPUImageInput> *)finalFilterInChain withCompletionHandler:(void (^)(UIImage *processedImage))block;
{
    [finalFilterInChain useNextFrameForImageCapture];
    [self processImageWithCompletionHandler:^{
        UIImage *imageFromFilter = [finalFilterInChain imageFromCurrentFramebuffer];
        //返回处理后的图片
        block(imageFromFilter);
    }];
}

// 输出图片大小,由于图像大小可能被调整(详见初始化方法)。因此,提供了获取图像大小的方法。
- (CGSize)outputImageSize;
{
    return pixelSizeOfImage;
}

// 由响应链的final filter生成UIImage图像
- (void)addTarget:(id<GPUImageInput>)newTarget atTextureLocation:(NSInteger)textureLocation;
{
    [super addTarget:newTarget atTextureLocation:textureLocation];

    if (hasProcessedImage)
    {
        [newTarget setInputSize:pixelSizeOfImage atIndex:textureLocation];
        [newTarget newFrameReadyAtTime:kCMTimeIndefinite atIndex:textureLocation];
    }
}
iOS逆向抖音学习滤镜,你知道吗? 逆向不只可以让我娱乐别人的APP,我们也可以从别人的APP中学到一些东西,有时候我们会为了某种实现去逆向。由于前段时间公司项目需要写一个类似抖音的滤镜,不免就对抖音起了好奇心。最后效果如下:IMG_0719.jpg工具依然使用MonkeyDev,然后是分析工具Hopper,log工具NSLogger步骤在这里我还是要推荐下我自己建的iOS开发学习群:680565220,群里都是学ios开发的,如果... 阅读详情

相关推荐

GPUImage源码解读(十四)

GPUImageRawDataInput继承自GPUImageOutput,它可以接受RawData输入(包括:RGB、RGBA、BGRA、LUMINANCE数据)并生成帧缓存对象。 - 构造方法。构造的时候主要是根据RawData数据指针,数据大小,以及数据格式进行构造。 // Initialization and teardown - (id)initWithBytes:(GLubyt...

Philm_iOS的博客 369

IOS开发--使用lookup table为图片添加滤镜

文/謝灰灰在找胡蘿蔔(简书作者) 原文链接:http://www.jianshu.com/p/b470a5b5a560# 著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。 在涉及到图片的应用中,基本上都会加入图片滤镜的相关功能。 IOS自身也自带了功能强大的滤镜相关的API,具体希望了解的朋友可以查询一下苹果的文档。 今天这里要介绍的是lookup table(颜色

Layne_Sun的博客 1365

iOS GPUImage研究一:图片滤镜

步骤 内容 第一步 创建预览View 即必须的GPUImageView 第二步 创建对象 即我们要用到的GPUImagePicture 第三步 创建滤镜 即这里我们使用的 GPUImageSobelEdgeDetectionFilter 第四步 设置纹理尺寸 添加滤镜 addTarget 并开始处理 第一步:CGRect mainScreenFrame = [[UI

Quinn's blog ! I'm glad to be here! 2017

iOS图片渲染深入剖析及CGImageRef的使用(源码)

正确获取图片所有参数

mumubumaopao的博客 1819

GPUImage 源码分析

GPUImage源码解读 介绍 GitHub - cats-oss/android-gpuimage: Android filters based on OpenGL (idea from GPUImage for iOS) 做图片或者视频滤镜渲染,离不开 OpenGL,而在移动平台上最令人熟知的就是 GitHub - BradLarson/GPUImage2: GPUImage 2 is ...

crazy_jack 2149

iOS GPUImage研究总结

关于GPUImage 这里直接引用官方描述:GPUImage是使用GPU处理图像的、他可以对图片、实时画面、视频进行处理。他允许你自定义滤镜、支持iOS4.0。然而,目前缺乏核心形象的一些更高级的功能,比如面部检测。

Quinn's blog ! I'm glad to be here! 1万+

iOS GPUImage源码解读

前言 GPUImage是iOS上一个基于OpenGL进行图像处理的开源框架,内置大量滤镜,架构灵活,可以在其基础上很轻松地实现各种图像处理功能。本文主要向大家分享一下项目的核心架构、源码解读及使用心得。 GPUImage有哪些特性 丰富的输入组件 摄像头、图片、视频、OpenGL纹理、二进制数据、UIElement(UIView, CALayer) 大量现成的内置滤镜(4大类) 1). 颜...

majiakun1的专栏 3610

iOS开发——GPUImage源码解析

一、基本概念 GPUImage:一个开源的、基于openGL的图片或视频的处理框架,其本身内置了多达120多种常见的滤镜效果,并且支持照相机和摄像机的实时滤镜,并且能够自定义图像滤镜。同时也很方便在原有基础上加入自己的滤镜Filter,所有滤镜是基于opengl shader(着色器)实现的,所以滤镜效果图像处理是在GPU上实现的,处理效率比较高,在iPhone6及其以上手机,可以做到实时流...

weixin_30240349的博客 376

GPUImage源码分析与使用()

有丰富的输入组件,可以处理图片、纹理、视频、二进制数据、UIElement(UIView、CALayer),可以使用GPUImage拍照、处理纹理图片、给视频或拍摄中的视频添加滤镜、添加水印可以使用UIElement。支持对大图进行处理,GPU的纹理限制是4096*4096,对于超出限制的图片,GPUImage会压缩处理导致损失图片质量,CoreImage会把图片拆解成小图处理。可以传入字符串类型,也可以传入文件类型,比如以.vsh/.fsh/.glsl等为后缀等命名的文件。

程序猿老樊的博客 1117

GPUImage源码解读()

导读: 系列文章会从结构到使用,细化到每一个变量, 使用方法全有对应 DEMO.也会有相对应的技术点扩展如(AVFoundation,OpenGL shader,CAEAGLLayer)等,目前是 Objective-C 版本的,后面会有Swift版本. GPUImage-Objective-C 官方源码 GPUImage-Swift 官方源码 GPUImage-Objective-C 添...

Philm_iOS的博客 798

c语言freeimage库文件,GPUImage源码解读之GLProgram

简述GLProgram是GPUImage中代表openGL ES 中的program,具有glprogram功能。其实是作者对OpenGL ES program的面向对象封装初始化- (id)initWithVertexShaderString:(NSString *)vShaderStringfragmentShaderString:(NSString *)fShaderString;- (id...

weixin_36176188的博客 303

GPUImage源码解读()

这篇文章主要是阅读GPUImage框架中的 GLProgram、GPUImageContext 两个重要类的源码。这两个类是 GPUImage 框架的基础,里面涉及的知识也有 OpenGL ES 基础 和 多线程 基础。以下是源码内容: GLProgram 一 GLProgram GLProgram专门处理OpenGL ES程序的创建等相关工作。 初始化方法,可以根据需要传入顶点着色...

Philm_iOS的博客 567

android 滤镜开源,Android_GPUImage 支持实时摄像头滤镜的GPUImage是iOS下一个开源的基于GPU的图像处理库 - 下载 - 搜珍网...

Android例子源码支持实时摄像头滤镜的GPUImage/Android例子源码支持实时摄像头滤镜的GPUImage/.gitignoreAndroid例子源码支持实时摄像头滤镜的GPUImage/library/Android例子源码支持实时摄像头滤镜的GPUImage/library/.classpathAndroid例子源码支持实时摄像头滤镜的GPUImage/library/.gitig...

weixin_36397146的博客 184

GPUImage源码解读之GLProgram

简述 GLProgram是GPUImage中代表openGL ES 中的program,具有glprogram功能。其实是作者对OpenGL ES program的面向对象封装 初始化 - (id)initWithVertexShaderString:(NSString *)vShaderString fragmentShaderString:(NSString *)fS...

weixin_30346033的博客 168

GPUImage源码解读(二十四)

GPUImageLookupFilter GPUImageLookupFilter 是GPUImage中的颜色查找滤镜,在一般的相机应用中使用得最广泛,它的作用是通过颜色变换从而产生出新风格的图片,在philm项目中也有大量使用 LUT (Lookup Tables)即查找表 。LUT是个非常简单的数值转换表,不同的色彩输入数值“映射”到一套输出数值,用来改变图像的色彩。例如:红色在LUT中可...

Philm_iOS的博客 740

GPUImage源码解读()

GPUImageView 从名称就可以知道GPUImageView是GPUImage框架中显示图片相关的类。GPUImageView实现了GPUImageInput协议,从而可以知道它能够接受GPUImageFramebuffer的输入。因此,常常作为响应链的终端节点,用于显示处理后的帧缓存。 - 重写静态方法 返回 CAEAGLLayer GPUImageView 是UIView的子类...

Philm_iOS的博客 500

GPUImage源码解读(二十三)

GPUImageFilter GPUImageFilter 是GPUImage中很重要、很基础的类,它可以处理帧缓存对象的输入输出,但是对纹理并不添加任何特效,也就是说只是简单的让纹理通过。它更多的是作为其它滤镜的基类,一些具体的滤镜由它的子类去完成。同时它也只能处理单个帧缓存对象的输入,处理多个帧缓存对象的输入也是由它的子类去完成 向量定义 //在 GPUImage 中主要用到了3...

Philm_iOS的博客 485

GPUImage源码解读(二十五)

#GPUImageView 结构说明 . ├── GPUImage.xcodeproj │&amp;nbsp;&amp;nbsp; ├── project.pbxproj │&amp;nbsp;&amp;nbsp; ├── project.xcworkspace ├── GPUImageMac.xcodeproj │&amp;nbsp;&amp;nbsp; ├── project.pbxproj │&amp;nbsp;&amp;nbsp;

Philm_iOS的博客 458

GPUImage源码解读()

介绍GPUImage框架中的GPUImageInput协议以及GPUImageOutput类 GPUImageInput GPUImageInput 在GPUImageContext.h中定义, 协议提供了方法列表,细节由实现的对象实现。GPUImage中实现GPUImageInput的协议的类比较多,常见的有 GPUImageFilter、GPUImageView、GPUImageRa...

Philm_iOS的博客 535
上一篇: GPUImage源码解读(六)
下一篇: GPUImage源码解读(八)
Philm_iOS
博客等级 码龄8年 74粉丝 53原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值