iOS中濾鏡處理及相關內存泄漏問題的解決


 最近工作之余在做一個美圖秀秀的仿品 做到濾鏡這塊的時候  自己就參考了網上幾位博主(名字忘了記,非常抱歉)的博客,但是發現跟着他們的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即可打印一個完整的顏色矩陣,您只需要將顏色矩陣保存,就擁有了獨一無二的濾鏡哦。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM