最近工作之余在做一个美图秀秀的仿品 做到滤镜这块的时候  自己就参考了网上几位博主(名字忘了记,非常抱歉)的博客,但是发现跟着他们的demo做的滤镜处理,都会有很严重的内存泄漏,于是就自己按照大体的思路将代码重新整理了下,并解决了内存泄漏问题。

 大体思路如下:

根据图片创建一个CoreGraphic的图形上文->根据图形上下文获取图片每个像素的RGBA的色值数组->遍历数组,按照颜色矩阵进行像素色值调整->输出绘制新的图片

 具体流程如下:

首先创建一个RGBA通道位图上下文:注意在以下方法中,不要立刻释放malloc方法生成的bitmapData内存空间指针,(可能有的朋友觉得已经把内存空间地址给了位图上下文就可以立马释放掉了,但是由于位图上下文在后来的图像渲染时,仍然需要这一块内存,因此不能在此处立马释放掉内存,之前拜读的几篇博客索性就不释放内存了,因此会导致内存泄漏,处理一些高清图像时,手机内存会轻易飙升到1G以上,而导致程序挂掉)不然会导致位图上下文的内容数据不能正常存在而导致图片生成失败,在这里需要一个全局内存指针来指向它,并且在合适的时候释放内存,具体看如下代码:

创建RGBA通道位图上下文:该位图上下文主要提供了一个画板,配置了画板的绘图所占用的字节数,设备依赖的RGB通道等信息。该上下文主要用于提供所有渲染图像的像素的RGBA值数组,以便后续对像素值的遍历处理。

#pragma mark---------------------------------------->创建一个使用RGBA通道的位图上下文
static CGContextRef CreateRGBABitmapContex(CGImageRef inImage){ CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void *bitmapData;//内存空间的指针,该内存空间的大小等于图像使用RGB通道所占用的字节数。
long bitmapByteCount;
long bitmapBytePerRow; /* 获取像素的横向和纵向个数 */
size_t pixelsWith = CGImageGetWidth(inImage);
size_t pixelsHigh = CGImageGetHeight(inImage); /* 每一行的像素点占用的字节数,每个像素点的RGBA四个通道各占8bit空间 */
bitmapBytePerRow = (pixelsWith * 4); /* 整张图片占用的字节数 */
bitmapByteCount = (bitmapBytePerRow * pixelsHigh); /* 创建依赖设备的RGB通道 */
colorSpace = CGColorSpaceCreateDeviceRGB(); /* 分配足够容纳图片字节数的内存空间 */
bitmapData = malloc(bitmapByteCount); /* 引用内存地址 以便在合适的地方释放内存空间 */
bitmap = bitmapData; /* 创建CoreGraphic的图形上下文 该上下文描述了bitmaData指向的内存空间需要绘制的图像的一些绘制参数 */ context = CGBitmapContextCreate(bitmapData, pixelsWith, pixelsHigh, 8, bitmapBytePerRow, colorSpace, kCGImageAlphaPremultipliedLast); /* Core Foundation中含有Create、Alloc的方法名字创建的指针,需要使用CFRelease()函数释放 */
CGColorSpaceRelease(colorSpace); /* 此处必须手动释放内存 不然会有内存暴增的现象 但如果在这里释放 真机运行时情况可能就不太好了 (注意:在模拟器上 在此释放不会有任何问题 模拟器的图形上下文和画板机制与真机不同) */
// free(bitmapData);
return context; } 

返回目标图像的RBGA像素色值的数组指针:该指针指向一个数组,数组中的每四个元素都是图像上的一个像素点的RGBA的数值(0-255),用无符号的char是因为它正好的取值范围就是0-255

static unsigned char *RequestImagePixelData(UIImage * inImage){
CGImageRef img = [inImage CGImage];
CGSize size = [inImage size];
//使用上面的函数创建上下文
CGContextRef cgctx = CreateRGBABitmapContex(img);
CGRect rect = {{0,0},{size.width,size.height}}; //将目标图像绘制到指定的上下文,实际为上下文内的bitmapData。
CGContextDrawImage(cgctx, rect, img);
unsigned char *data = CGBitmapContextGetData(cgctx);
//释放上面的函数创建的上下文
CGContextRelease(cgctx);
cgctx = NULL; return data;
}

将一个像素RGBA值数组通过一个颜色矩阵进行转换:颜色矩阵决定了图像的渲染效果,因此不同的滤镜效果可以通过设置不同的颜色矩阵进行转换。如果不懂颜色矩阵,可以参考如下的博客:http://www.cnblogs.com/yjmyzz/archive/2010/10/16/1852878.html,在这里就不过多描述了。

注意:在以下方法中,建议先取值并赋值给变量,因为每个像素点的色值,都要调用这个方法,对于一张稍大的高清图,会遍历非常多的次数,因此,里面的每一步多余的操作,都会引起积累起来的长时间处理,博主当时也踩了这个坑,导致处理一张图片时极度耗时。

static void changeRGB(int *red,int* green,int*blue,int*alpha ,const float *f){ 
//先取值并赋值给变量
int redV = *red;
int greenV = *green;
int blueV = *blue;
int alphaV = *alpha; *red = f[0] * redV + f[1]*greenV + f[2]*blueV + f[3] * alphaV + f[4];
*green = f[5] * redV + f[6]*greenV + f[7]*blueV + f[8] * alphaV+ f[9];
*blue = f[10] * redV + f[11]*greenV + f[12]*blueV + f[11] * alphaV+ f[14];
*alpha = f[15] * redV + f[16]*greenV + f[17]*blueV + f[18] * alphaV+ f[19];

//超出边界值的都默认为边界值
if (*red<0) {
*red=0;
} if (*red>255) {
*red = 255;
} if (*green<0) {
*green = 0;
} if (*green>255) {
*green = 255;
}
if (*blue<0) {
*blue = 0;
} if (*blue>255) {
*blue = 255;
} if (*alpha>255) {
*alpha=255;
}
if (*alpha<0) {
*alpha = 0;
} }

以下方法就是暴露给大家的最终图片处理方法了,通过传入一张图片和一个颜色矩阵f,即可完成一张图片的滤镜渲染,并且,在生成一张图片后,最好是将该图像转换为NSData类型进行存储,然后释放掉之前全局变量内存指针,最后再将NSData数据回传给需要的方法。如果不将生成图像转化为NSData存储,而直接使用生成的UIImage对象,则在释放掉内存指针后,UIImage对象也将不存在,楼主亲测,是个大坑,读者尽量避免此类情况。

- (UIImage *)createImageWithImage:(UIImage *)inImage andColorMatrix:(const float *)f{
/* 图片位图像素值数组 */
unsigned char *imgPixel = RequestImagePixelData(inImage); CGImageRef inImageRef = [inImage CGImage];
long w = CGImageGetWidth(inImageRef);
long h = CGImageGetHeight(inImageRef); int wOff = 0;
int pixOff = 0; /* 遍历修改位图像素值 */
for (long y = 0; y<h; y++) {
pixOff = wOff;
for (long x = 0; x<w; x++) {
int red = (unsigned char)imgPixel[pixOff];
int green = (unsigned char)imgPixel[pixOff+1];
int blue = (unsigned char)imgPixel[pixOff +2];
int alpha = (unsigned char)imgPixel[pixOff +3];
changeRGB(&red, &green, &blue, &alpha,f);
imgPixel[pixOff] = red;
imgPixel[pixOff + 1] = green;
imgPixel[pixOff + 2] = blue;
imgPixel[pixOff + 3] = alpha;
pixOff += 4;
}
wOff += w * 4 ;
} NSInteger dataLength = w * h * 4;
//创建要输出的图像的相关参数
CGDataProviderRef provider = CGDataProviderCreateWithData(NULL, imgPixel, dataLength, NULL); if (!provider) {
NSLog(@"创建输出图像相关参数失败!");
}else{
int bitsPerComponent = 8;
int bitsPerPixel = 32;
ItemCount bytesPerRow = 4 * w;
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
CGBitmapInfo bitmapInfo = kCGBitmapByteOrderDefault;
CGColorRenderingIntent rederingIntent = kCGRenderingIntentDefault;
//创建要输出的图像
CGImageRef imageRef = CGImageCreate(w, h,bitsPerComponent, bitsPerPixel, bytesPerRow, colorSpaceRef, bitmapInfo, provider,NULL, NO, rederingIntent);
if (!imageRef) {
NSLog(@"创建输出图像失败"); }else{
UIImage *my_image = [UIImage imageWithCGImage:imageRef];
CFRelease(imageRef);
CGColorSpaceRelease(colorSpaceRef);
CGDataProviderRelease(provider); NSData *data = UIImageJPEGRepresentation(my_image, 1.0); /* 在这里就可以释放内存了 并且在此之后my_image由于运行时和图形上下文机制已经没有图像内容了 只能使用刚刚生成的图片二进制数据进行图片回传 */
free(bitmap);

       //这里的block是demo中需要的 可以不做关注
if (_imageBLOCK) {
_imageBLOCK([UIImage imageWithData:data]);
}
return [UIImage imageWithData:data];
}
}
return nil;
}

我的demo的github地址为:https://github.com/China131/JHFilterDemo.git,效果图如下:

demo的操作很简单,即动态改变颜色矩阵的值,实时生成渲染图片,您可以慢慢调试,如果发现您喜欢的渲染类型,直接点击保存图片,Xcode即可打印一个完整的颜色矩阵,您只需要将颜色矩阵保存,就拥有了独一无二的滤镜哦。

iOS中滤镜处理及相关内存泄漏问题的解决的更多相关文章

  1. iOS中滤镜种类及相关介绍

  2. 在Activity中使用Thread导致的内存泄漏

    https://github.com/bboyfeiyu/android-tech-frontier/tree/master/issue-7/%E5%9C%A8Activity%E4%B8%AD%E4 ...

  3. 深入理解Node.js中的垃圾回收和内存泄漏的捕获

    深入理解Node.js中的垃圾回收和内存泄漏的捕获 文章来自:http://wwsun.github.io/posts/understanding-nodejs-gc.html Jan 5, 2016 ...

  4. 5个Android开发中比较常见的内存泄漏问题及解决办法

    android中一个对象已经不需要了,但是其他对象还持有他的引用,导致他不能回收,导致这个对象暂存在内存中,这样内存泄漏就出现了.   内存泄漏出现多了,会是应用占用过多的没存,当占用的内存超过了系统 ...

  5. 面试官:小伙子,你给我说一下Java中什么情况会导致内存泄漏呢?

    概念 内存泄露:指程序中动态分配内存给一些临时对象,但对象不会被GC回收,它始终占用内存,被分配的对象可达但已无用.即无用对象持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间浪费. 可达 ...

  6. Android 内存泄漏分析与解决方法

    在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...

  7. Android开发之漫漫长途 番外篇——内存泄漏分析与解决

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

  8. Android开发 |常见的内存泄漏问题及解决办法

    在Android开发中,内存泄漏是比较常见的问题,有过一些Android编程经历的童鞋应该都遇到过,但为什么会出现内存泄漏呢?内存泄漏又有什么影响呢? 在Android程序开发中,当一个对象已经不需要 ...

  9. iOS开发那些事--性能优化–内存泄露问题的解决(转)

    内存泄漏问题的解决 内存泄漏(Memory Leaks)是当一个对象或变量在使用完成后没有释放掉,这个对象一直占有着这块内存,直到应用停止.如果这种对象过多内存就会耗尽,其它的应用就无法运行.这个问题 ...

随机推荐

  1. jquey on

    1.如果你的元素是用clone方法复制出来的,并且,用了on来绑定事件的话,必须在clone的后边添加true,负责你的事件不会生效. 2.必须在on $('.js-liubody').on('cli ...

  2. SharpPcap网络包捕获框架的使用--实例代码在vs2005调试通过

    转自:http://hi.baidu.com/boyxgb/blog/item/89ac86fbdff5f82c4e4aea2e.html 由于项目的需要,要从终端与服务器的通讯数据中获取终端硬件状态 ...

  3. [转]Hibernate延迟加载与opensessioninviewFilter

    原文地址:http://blog.csdn.net/a19881029/article/details/7916702 hibernate延迟加载: 一个person对应多个school,使用hibe ...

  4. 图解Javascript原型链

    本文尝试阐述Js中原型(prototype).原型链(prototype chain)等概念及其作用机制.上一篇文章(图解Javascript上下文与作用域)介绍了Js中变量作用域的相关概念,实际上关 ...

  5. 为Debian/Ubuntu的apt-get install添加自动补齐/完成功能

    Debian/Ubuntu的apt-get太常用了,不过偶尔可能也会碰到不太熟悉,想不起来的包的名称,除了去debian packages去查找,另外的方法就是给Debian/Ubuntu添加自动补齐 ...

  6. cogs 577 蝗灾 CDQ分治

    第一道CDQ,抄了下helenkeller的代码,感觉和归并排序差不多... 因为左半边的修改肯定在右半边的询问之前,所以就不用管时间的限制了,可以直接x轴排序树状数组处理y轴... #include ...

  7. poj 1390 Blocks

    poj 1390 Blocks 题意 一排带有颜色的砖块,每一个可以消除相同颜色的砖块,,每一次可以到块数k的平方分数.问怎么消能使分数最大.. 题解 此题在徐源盛<对一类动态规划问题的研究&g ...

  8. Linux系统概述

    1.Linux是一套免费使用 和自 由传播的类Unix操作系统. 这个系统是由世界各地的成千上万的程序员 设计和实现的.其目 的是建立不受任何商品化软件的版权制约的. 全世界都能自 由使用的Unix兼 ...

  9. wamp(win1064位家庭版+apache2.4.20+php5.5.37+mysql5.5.50)环境搭建

    wamp环境搭建之软件准备 *php:http://windows.php.net/downloads/releases/php-5.5.37-Win32-VC11-x86.zip *apache:h ...

  10. JQuery常用方法总结

    1.json的创建方式 <script> $(function () { //第一种 var my = new People("CallmeYhz", 26); ale ...