一搪花、關(guān)于圖片的兩種格式,PNG和JPEG
圖片文件被加載后必須要進(jìn)行解碼炒辉,解碼過程是一個(gè)相當(dāng)復(fù)雜的任務(wù),需要消耗非常長的時(shí)間泉手。解碼后的圖片將同樣使用相當(dāng)大的內(nèi)存黔寇。
用于加載的CPU時(shí)間相對(duì)于解碼來說根據(jù)圖片格式而不同。對(duì)于iOS來說大多處理的就是PNG和JPEG了斩萌。
- JPEG可以非常好的壓縮圖片缝裤,但是JPEG是失真的,它去除了人眼很難察覺的信息颊郎,并且這樣做可以超出像gzip這樣壓縮算法的限制憋飞,但是壓縮算法也更復(fù)雜。相對(duì)來說姆吭,JPEG圖片更小榛做,加載更快,但是解壓的步驟要消耗更長的時(shí)間。
- 對(duì)于PNG來說它的壓縮是無損的检眯,所以圖片相對(duì)JPEG來說也大很多升敲,加載時(shí)間會(huì)比JPEG更長,但是解碼會(huì)相對(duì)較快轰传,而且Xcode會(huì)把PNG圖片進(jìn)行解碼優(yōu)化之后引入工程驴党,當(dāng)Xcode優(yōu)化一個(gè) PNG 文件的時(shí)候,它將 PNG 文件變成一個(gè)從技術(shù)上講不再是有效的PNG文件获茬。但是 iOS 可以讀取這種文件港庄,并且這比解壓縮正常的 PNG 文件更快。
當(dāng)加載圖片的時(shí)候恕曲,iOS通常會(huì)延遲解壓圖片的時(shí)間鹏氧,直到加載到內(nèi)存之后。這就會(huì)在準(zhǔn)備繪制圖片的時(shí)候影響性能佩谣,因?yàn)樾枰诶L制之前進(jìn)行解壓(通常是消耗時(shí)間的問題所在)把还。
二、圖片的加載
圖片的加載的幾種方法茸俭,這里只說兩種:
-
使用緩存吊履,多處重復(fù)使用的圖片使用imageNamed方法:
+ (nullable UIImage *)imageNamed:(NSString *)name;
此方法避免延時(shí)解壓,會(huì)在加載圖片之后立刻進(jìn)行解壓调鬓。不過+imageNamed:
只對(duì)從應(yīng)用資源中的圖片有效艇炎,所以對(duì)用戶生成的圖片內(nèi)容或者是下載的圖片就沒法使用了。另外腾窝,此方法會(huì)在系統(tǒng)緩存中查找具有指定名稱的圖像對(duì)象缀踪,并在存在時(shí)返回該對(duì)象。如果匹配的圖像對(duì)象尚未在緩存中虹脯,該方法根據(jù)指定的圖片名加載圖片并解壓驴娃,緩存它,然后返回生成的對(duì)象循集。
在nib文件中引用的圖片同樣也是這個(gè)機(jī)制唇敞,所以你很多時(shí)候都在隱式的使用它。 大圖暇榴,且就加載一次的圖片:
+ (nullable UIImage *)imageWithContentsOfFile:(NSString *)path;
如果圖片只顯示一次厚棵,并且希望它不被添加到系統(tǒng)的緩存中,那么應(yīng)該使用imageWithContentsOfFile:
蔼紧。此方法不應(yīng)用系統(tǒng)圖片緩存婆硬,對(duì)于很多只加載單次的圖片來說,使用此方法可以潛在的改善內(nèi)存使用奸例。
在說圖片的解壓和渲染繪制之前彬犯,我們先說下CoreGraphics中的一些我們要用到的方法等向楼。
1.Graphics Context(圖形上下文)類型:
- View Graphics Context :iOS中的繪圖圖形上下文,在drawRect方法中可以通過UIGraphicsGetCurrentContext獲得谐区。
- Window Graphics Context :Mac OS X中的圖形上下文湖蜕,不多說。
- PDF Graphics Context :用于創(chuàng)建PDF文檔的圖形上下文宋列。
- Bitmap Graphics Context :位圖圖形上下文昭抒。
- Graphics Context for Printing:為打印而來的圖形上下文。
2.坐標(biāo)系:
UIKit默認(rèn)是左上角零點(diǎn)炼杖,y向下的坐標(biāo)系灭返。
而Quartz是默認(rèn)左下角零點(diǎn),y向上的坐標(biāo)系坤邪。
一般情況下熙含,對(duì)于我們經(jīng)常使用的Bitmap Context,如果其對(duì)應(yīng)一個(gè)視圖艇纺,視圖在屏幕上的就是y下坐標(biāo)系怎静,在屏幕外的是y上坐標(biāo)系。
日常使用中需要注意:
UIGraphicsBeginImageContext
黔衡、UIGraphicsBeginImageContextWithOptions
:獲取到的是y向下坐標(biāo)系蚓聘,文檔中指出如果應(yīng)用程序通過調(diào)用函數(shù)UIGraphicsBeginImageContextWithOptions
創(chuàng)建圖形上下文,則UIKit將相同的變換應(yīng)用于上下文的坐標(biāo)系员帮,就像UIView對(duì)象的圖形上下文一樣或粮,所以是y向下的。CGContextDrawImage
:默認(rèn)使用y上坐標(biāo)系來繪圖捞高。CGBitmapContextCreate
:創(chuàng)建的bitmapContext默認(rèn)是y上坐標(biāo)系。雖然是y上坐標(biāo)系渣锦,但是如果是單純繪制圖片硝岗,bitmapContext加載到UIKit視圖上時(shí)會(huì)自動(dòng)轉(zhuǎn)換坐標(biāo)系,所以不需要手動(dòng)變換坐標(biāo)袋毙。
3.再來看CGImage的創(chuàng)建:
CGImageRef __nullable CGImageCreate(
size_t width, size_t height,
size_t bitsPerComponent,
size_t bitsPerPixel,
size_t bytesPerRow,
CGColorSpaceRef cg_nullable space,
CGBitmapInfo bitmapInfo,
CGDataProviderRef cg_nullable provider,
const CGFloat * __nullable decode,
bool shouldInterpolate,
CGColorRenderingIntent intent
)CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
參數(shù)真的是多型檀,一個(gè)一個(gè)看:
width,heigth:這個(gè)不用多想,就是圖片的寬高听盖,單位是像素胀溺。
bitsPerComponent:要知道在RGB中,顏色有4個(gè)分量(R皆看、G仓坞、B、A)腰吟,這個(gè)參數(shù)表示單個(gè)分量所占位數(shù)无埃,再iOS上顏色空間
CGColorSpaceGetDeviceRGB()
對(duì)應(yīng)的是8位徙瓶。bitsPerPixel:表示一個(gè)像素占多少位 =
(bitsPerComponent * number of components + 7)/8 * 8
,RGB的一個(gè)像素由4個(gè)顏色分量組成嫉称,每個(gè)分量通常是8位侦镇,那么一般單個(gè)像素總共占32位(有些RGB沒有A,不是8位)织阅。如果某個(gè)空間中每個(gè)分量是5位壳繁,由4個(gè)分量組成,則一個(gè)像素占24位荔棉。bytesPerRow:表示一行像素的總字節(jié)數(shù) =
width * (bitsPerPixel / BYTE_SIZE)
闹炉,注意這里是字節(jié)(通常1字節(jié)=8位)。space:顏色空間江耀,比如顏色空間RGB中(1,0,0)表示紅色剩胁,比如顏色空間BGR(1,0,0)表示藍(lán)色,同一個(gè)顏色在不同顏色空間里有不同的表達(dá)式祥国。需要手動(dòng)release昵观。
bitmapInfo:位圖的布局信息,枚舉舌稀,使用
(CGImageAlphaInfo | CGBitmapInfo)
啊犬。當(dāng)在ARGB-32下,并且opaque是YES即不包含alpha時(shí)壁查,使用(kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host)
表示沒有alpha并跳過第一個(gè)顏色分量觉至,位圖排序使用“主機(jī)32位序”,這是一個(gè)宏睡腿,在iPhone中通常是‘小端32位序’语御。需要提一下UIGraphicsBeginImageContext
返回的context的顏色空間就是32位RGB,顏色分量8位席怪。provider:數(shù)據(jù)提供者应闯,這個(gè)主要是提供圖片數(shù)據(jù)的。如果提供的是未解壓到圖片數(shù)據(jù)挂捻,那么創(chuàng)建的就是未解壓的圖片碉纺;如果提供的是解壓后的數(shù)據(jù),那么創(chuàng)建的就是解壓后的圖片刻撒。
-
decode:解碼數(shù)組骨田,這個(gè)解碼與圖片的解壓是兩回事,不要混淆了声怔。圖像的解碼映射數(shù)組态贤。如果您不想允許重新映射圖像的顏色值,請(qǐng)傳遞NULL解碼數(shù)組捧搞。對(duì)于圖像顏色空間中的每個(gè)顏色分量(包括alpha分量)抵卫,解碼數(shù)組提供兩組狮荔,即一對(duì)表示范圍上限和下限的值。例如介粘,RGB顏色空間中的源圖像的解碼數(shù)組總共包含六個(gè)分量殖氏,每組由紅色,綠色和藍(lán)色組成,左三個(gè)分量是最小值姻采,右三個(gè)分量是最大值雅采。渲染圖像時(shí),Core Graphics使用線性變換將原始組件值映射到指定范圍內(nèi)適合目標(biāo)顏色空間的相對(duì)數(shù)字慨亲。
- 假設(shè)原色值是藍(lán)色(0,0,1)婚瓜,decode設(shè)置{0.5,1,0.8},相當(dāng)于{0.5,1,0.8,0,0,0}刑棵,色值會(huì)先判斷如果小于最小值巴刻,那么直接取最小值,再判斷如果大于最大值蛉签,那么直接取最大值胡陪,那么得出的結(jié)論就是:色值(0,0,1)變成了(0.5, 1, 0)。
shouldInterpolate:一個(gè)布爾值碍舍,指定是否應(yīng)進(jìn)行插值柠座。插值設(shè)置指定Core Graphics是否要對(duì)圖像應(yīng)用像素平滑算法。在沒有插值的情況下片橡,當(dāng)在具有比圖像數(shù)據(jù)更高分辨率的輸出設(shè)備上繪制時(shí)妈经,圖像可能呈現(xiàn)鋸齒狀或像素化。
intent:渲染意圖常量捧书,渲染意圖常量決定了將顏色從一個(gè)顏色空間映射到另一個(gè)顏色空間的確切方法吹泡。
CGImageAlphaInfo
透明度分量信息和CGBitmapInfo
位圖布局信息:
typedef CF_ENUM(uint32_t, CGImageAlphaInfo) {
kCGImageAlphaNone, /* For example, RGB. */
kCGImageAlphaPremultipliedLast, /* For example, premultiplied RGBA */
kCGImageAlphaPremultipliedFirst, /* For example, premultiplied ARGB */
kCGImageAlphaLast, /* For example, non-premultiplied RGBA */
kCGImageAlphaFirst, /* For example, non-premultiplied ARGB */
kCGImageAlphaNoneSkipLast, /* For example, RBGX. */
kCGImageAlphaNoneSkipFirst, /* For example, XRGB. */
kCGImageAlphaOnly /* No color data, alpha data only */
};
typedef CF_OPTIONS(uint32_t, CGBitmapInfo) {
kCGBitmapAlphaInfoMask = 0x1F,
kCGBitmapFloatInfoMask = 0xF00,
kCGBitmapFloatComponents = (1 << 8),
kCGBitmapByteOrderMask = kCGImageByteOrderMask,
kCGBitmapByteOrderDefault = kCGImageByteOrderDefault,
kCGBitmapByteOrder16Little = kCGImageByteOrder16Little,
kCGBitmapByteOrder32Little = kCGImageByteOrder32Little,
kCGBitmapByteOrder16Big = kCGImageByteOrder16Big,
kCGBitmapByteOrder32Big = kCGImageByteOrder32Big
}
#ifdef __BIG_ENDIAN__
//[大端序](https://zh.wikipedia.org/wiki/%E5%AD%97%E8%8A%82%E5%BA%8F#.E5.A4.A7.E7.AB.AF.E5.BA.8F)
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Big
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Big
#else /* Little endian. */
//小端序
# define kCGBitmapByteOrder16Host kCGBitmapByteOrder16Little
# define kCGBitmapByteOrder32Host kCGBitmapByteOrder32Little
#endif
CGColorRenderingIntent
繪制意圖:
/* Color rendering intents. */
typedef CF_ENUM (int32_t, CGColorRenderingIntent) {
kCGRenderingIntentDefault,
kCGRenderingIntentAbsoluteColorimetric,
kCGRenderingIntentRelativeColorimetric,
kCGRenderingIntentPerceptual,
kCGRenderingIntentSaturation
};
kCGRenderingIntentDefault
:默認(rèn)渲染意圖。kCGRenderingIntentAbsoluteColorimetric
:絕對(duì)色度渲染意圖经瓷。將輸出設(shè)備顏色域外的顏色映射為輸出設(shè)備域內(nèi)與之最接近的顏色荞胡。這可以產(chǎn)生一個(gè)裁減效果,因?yàn)樯蛲獾膬蓚€(gè)不同的顏色值可能被映射為色域內(nèi)的同一個(gè)顏色值了嚎。當(dāng)圖形使用的顏色值同時(shí)包含在源色域及目標(biāo)色域內(nèi)時(shí),這種方法是最好的廊营。常用于logo或者使用專色(spot color)時(shí)歪泳。kCGRenderingIntentRelativeColorimetric
:相對(duì)色度渲染意圖。轉(zhuǎn)換所有的顏色(包括色域內(nèi)的)露筒,以補(bǔ)償圖形上下文的白點(diǎn)與輸出設(shè)備白點(diǎn)之間的色差呐伞。kCGRenderingIntentPerceptual
:可感知渲染意圖。通過壓縮圖形上下文的色域來適應(yīng)輸出設(shè)備的色域慎式,并保持源顏色空間的顏色之間的相對(duì)性伶氢。感知渲染意圖適用于相片及其它復(fù)雜的高細(xì)度圖片趟径。kCGRenderingIntentSaturation
:飽和度渲染意圖。把顏色轉(zhuǎn)換到輸出設(shè)備色域內(nèi)時(shí)癣防,保持顏色的相對(duì)飽和度蜗巧。結(jié)果是包含亮度、飽和度顏色的圖片蕾盯。飽和度意圖適用于生成低細(xì)度的圖片幕屹,如描述性圖表。
4.CGBitmapContext的創(chuàng)建:
CG_EXTERN CGContextRef __nullable CGBitmapContextCreate(
void * __nullable data,
size_t width, size_t height,
size_t bitsPerComponent,
size_t bytesPerRow,
CGColorSpaceRef cg_nullable space, uint32_t bitmapInfo
)CG_AVAILABLE_STARTING(10.0, 2.0);
在之前介紹過的參數(shù)就不詳細(xì)說明了级遭。
data:用來制定位圖存儲(chǔ)空間望拖,保存位圖數(shù)據(jù)。如果設(shè)置為NULL挫鸽,那么系統(tǒng)會(huì)自動(dòng)分配一段合適的內(nèi)存并且bytesPerRow也可以設(shè)置為0说敏;如果不為空,那么bytesPerRow也不能為0丢郊,需要填寫相應(yīng)的數(shù)值盔沫,而且要記得free(data)。
width蚂夕,height:位圖的大小迅诬,一般設(shè)置為將要繪制的圖片的大小。
bitsPerComponent:單個(gè)顏色分量所占位數(shù)婿牍。
btyesPerRow:每行所占總字節(jié)侈贷。當(dāng)data參數(shù)設(shè)置為NULL,bytesPerRow設(shè)置為0等脂,那么系統(tǒng)就會(huì)自動(dòng)計(jì)算每行的總字節(jié)數(shù)俏蛮。
space:顏色空間。
bitmapInfo:位圖的布局信息上遥。
顏色空間space
下圖是蘋果支持的顏色空間和像素格式:
顏色空間iOS支持上述的其中8種搏屑, iOS不支持與設(shè)備無關(guān)或通用的顏色空間。根據(jù)Quartz 2D的文檔所說粉楚,iOS應(yīng)用程序必須使用設(shè)備顏色空間辣恋。
設(shè)備顏色空間主要由iOS應(yīng)用程序使用,因?yàn)槠渌x項(xiàng)不可用模软。在大多數(shù)情況下伟骨,Mac OS X應(yīng)用程序應(yīng)使用通用顏色空間,而不是創(chuàng)建設(shè)備顏色空間燃异,但是携狭,一些Quartz例程要求具有設(shè)備顏色空間的圖像,例如回俐,如果調(diào)用CGImageCreateWithMask并指定圖像作為蒙版逛腿,則必須使用設(shè)備灰色顏色空間定義圖像稀并。
-
CGColorSpaceCreateDeviceGray()
用來創(chuàng)建設(shè)備灰色顏色空間 -
CGColorSpaceCreateDeviceRGB()
用來創(chuàng)建設(shè)備RGB顏色空間 -
CGColorSpaceCreateDeviceCMYK()
用來創(chuàng)建設(shè)備CMYK顏色空間
所以iOS中,我們通常使用CGColorSpaceCreateDeviceRGB()
來創(chuàng)建顏色空間单默,還有bitsPerComponent和btyesPerRow要和顏色空間匹配碘举。
三、圖片的解壓
這是一張30x30的圖雕凹,存儲(chǔ)大小843B殴俱,
UIImage *image = [UIImage imageNamed:@"check_green.png"];
CFDataRef rawData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage));
上面的代碼可以獲得圖片解壓后的大小,為3600B枚抵。解壓縮后的圖片大小與原始文件大小之間沒有任何關(guān)系线欲,而只與圖片的像素有關(guān):
解壓縮后的圖片大小 = 圖片的像素寬 30 * 圖片的像素高 30 * 每個(gè)像素所占的字節(jié)數(shù) 4
使用+imageNamed:
方法進(jìn)行加載并解壓圖片,其實(shí)內(nèi)部就是使用的ImageIO框架汽摹。
1.使用ImageIO庫來加載本地image:使用kCGImageSourceShouldCache
來創(chuàng)建圖片李丰,強(qiáng)制圖片立刻解壓緩存,然后在圖片的生命周期保留解壓后的版本逼泣。
NSString *str = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents/image"];
CGDataProviderRef provider = CGDataProviderCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:str]);
NSDictionary *options = @{(__bridge id)kCGImageSourceShouldCache: @YES};
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source1, 0,(__bridge CFDictionaryRef)options);
UIImage *image = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
CFRelease(source);
繪制圖片過程中趴泌,系統(tǒng)會(huì)對(duì)圖片自動(dòng)解壓,所以繪制圖片也算是一種解壓的方式拉庶。
2.使用UIGraphicsBeginImageContext
進(jìn)行繪制圖片嗜憔,創(chuàng)建一個(gè)基于bitmap的imageContext,并把它設(shè)置成為當(dāng)前正在使用的context,然后將圖片繪制到context上氏仗。這方法在需要大量調(diào)用的時(shí)候不要用
吉捶,因?yàn)槭窍到y(tǒng)釋放內(nèi)存,所以存在延遲釋放的情況皆尔,短時(shí)間內(nèi)大量調(diào)用(如tableView的滑動(dòng)異步繪制圖片)會(huì)出現(xiàn)內(nèi)存暴漲的情況:
//創(chuàng)建一個(gè)基于bitmap的imageContext,并把位圖立即推入到圖形上下文堆棧中呐舔。坐標(biāo)系是UIKit默認(rèn)坐標(biāo)系左上角0點(diǎn)y下。
UIGraphicsBeginImageContextWithOptions(CGSizeMake(160, 220), YES, [UIScreen
mainScreen].scale);
//這里要記住CGContextDrawImage會(huì)默認(rèn)使用離屏bitmap的坐標(biāo)系即左下角0點(diǎn)y上,所以要變換坐標(biāo)慷蠕。
//CGContextRef ctx = UIGraphicsGetCurrentContext();
//CGContextTranslateCTM(ctx, 0, 220);
//CGContextScaleCTM(ctx, 1.0, -1.0);
//CGContextDrawImage(ctx, CGRectMake(0, 0, 160, 220), img.CGImage);
[image drawInRect:CGRectMake(0, 0, width, height)];
UIGraphicsEndImageContext();
3.使用CGBitmapContextCreate
繪制圖片珊拼,同方法1中繪制比較,根據(jù)官方文檔說明流炕,因?yàn)閎itmap context是左下角y上坐標(biāo)系澎现,如果實(shí)際繪畫中需要轉(zhuǎn)換坐標(biāo)系,不一定比imagContext繪制快每辟。但是我們其實(shí)大部分都是在屏幕外繪制圖片的昔头,所以不需要變換坐標(biāo),實(shí)際證明比方法UIGraphicsBeginImageContext
繪制要快影兽。
官方文檔上示例代碼:
//Creating a bitmap graphics context
CGImageRef imgRef = image.CGImage;
size_t pixelsWide = CGImageGetWidth(imgRef);
size_t pixelsHigh = CGImageGetHeight(imgRef);
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
void * bitmapData;
int bitmapByteCount;
int bitmapBytesPerRow;
bitmapBytesPerRow = (pixelsWide * 4);
bitmapByteCount = (bitmapBytesPerRow * pixelsHigh);
colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
void * bitmapData;
bitmapData = calloc( bitmapByteCount, sizeof(uint8_t) );
if (bitmapData == NULL) {
fprintf (stderr, "Memory not allocated!");
return nil;
}
context = CGBitmapContextCreate (bitmapData,
pixelsWide,
pixelsHigh,
8, // bits per component
bitmapBytesPerRow,
colorSpace,
kCGImageAlphaPremultipliedLast);
if (context == NULL) {
free (bitmapData);
fprintf (stderr, "Context not created!");
return NULL;
}
CGColorSpaceRelease( colorSpace );
return context;
//Drawing to a bitmap graphics context
CGRect myBoundingBox;// 1
myBoundingBox = CGRectMake (0, 0, myWidth, myHeight);// 2
myBitmapContext = MyCreateBitmapContext (400, 300);// 3
// ********** Your drawing code here ********** // 4
CGContextSetRGBFillColor (myBitmapContext, 1, 0, 0, 1);
CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 200, 100 ));
CGContextSetRGBFillColor (myBitmapContext, 0, 0, 1, .5);
CGContextFillRect (myBitmapContext, CGRectMake (0, 0, 100, 200 ));
myImage = CGBitmapContextCreateImage (myBitmapContext);// 5
CGContextDrawImage(myContext, myBoundingBox, myImage);// 6
char *bitmapData = CGBitmapContextGetData(myBitmapContext); // 7
CGContextRelease (myBitmapContext);// 8
if (bitmapData) free(bitmapData); // 9
CGImageRelease(myImage);
上面的代碼用到了bitmapData,一定要記得手動(dòng)釋放莱革。實(shí)際使用的時(shí)候峻堰,我們一般data輸入NULL就好讹开,bitmapBytesPerRow設(shè)置為0,這樣系統(tǒng)會(huì)自動(dòng)為我們分配位圖存儲(chǔ)內(nèi)存捐名,并自動(dòng)計(jì)算每行字節(jié)數(shù)旦万。
下面是我們通常使用bitmap context來繪制圖片的代碼:
+ (UIImage *)imageDecodeByBitmap:(UIImage *)image
{
//創(chuàng)建一個(gè)bitmap的context,并把它設(shè)置成為當(dāng)前正在使用的context
CGImageRef imgRef = image.CGImage;
size_t width = CGImageGetWidth(imgRef);
size_t height = CGImageGetHeight(imgRef);
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(imgRef);
BOOL hasAlpha = NO;
if (alphaInfo == kCGImageAlphaPremultipliedLast ||
alphaInfo == kCGImageAlphaPremultipliedFirst ||
alphaInfo == kCGImageAlphaLast ||
alphaInfo == kCGImageAlphaFirst) {
hasAlpha = YES;
}
CGBitmapInfo info = kCGBitmapByteOrder32Host | (hasAlpha ? kCGImageAlphaPremultipliedFirst: kCGImageAlphaNoneSkipFirst);
//創(chuàng)建一個(gè)bitmap的context
CGContextRef ctx = CGBitmapContextCreate(NULL, width, height, 8, 0, SharedCGColorSpaceGetDeviceRGB(), info);
CGContextDrawImage(ctx, CGRectMake(0, 0, width, height), imgRef);
CGImageRef cgImage = CGBitmapContextCreateImage(ctx);
CGContextRelease(ctx);
image = [UIImage imageWithCGImage:cgImage];
CGImageRelease(cgImage);
return image;
}
4.使用CGDataProviderCopyData解壓,然后使用CGImageCreate重新創(chuàng)建解壓后的圖片镶蹋。
CGImageRef imageRef = image.CGImage;
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
CGColorSpaceRef space = CGImageGetColorSpace(imageRef);
size_t bitsPerComponent = CGImageGetBitsPerComponent(imageRef);
size_t bitsPerPixel = CGImageGetBitsPerPixel(imageRef);
size_t bytesPerRow = CGImageGetBytesPerRow(imageRef);
CGBitmapInfo bitmapInfo = CGImageGetBitmapInfo(imageRef);
CGDataProviderRef provider = CGImageGetDataProvider(imageRef);
CFDataRef rawData = CGDataProviderCopyData(provider); //解壓
provider = CGDataProviderCreateWithCFData(rawData);
CFRelease(rawData);
imageRef = CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, space, bitmapInfo, provider, NULL, false, kCGRenderingIntentDefault);
CGDataProviderRelease(provider);
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);
這是一個(gè)單純的解壓方法成艘,不像前面幾個(gè)方法都是繪制圖片,不過這個(gè)方法也有個(gè)瑕疵贺归,在使用過程中淆两,會(huì)出現(xiàn)內(nèi)存不斷增加的情況:進(jìn)入這個(gè)視圖控制器內(nèi)存增加一些,pop出去后再進(jìn)來又增加一些拂酣,不斷增長秋冰,可能從原本的100M內(nèi)存增加到800M,然后就會(huì)回落到100M婶熬,猜測可能是系統(tǒng)沒有釋放位圖數(shù)據(jù)緩存。
四、圖片的渲染
正常我們?cè)谑褂?code>imageView.image = image;的時(shí)候痰哨,在屏幕顯示的時(shí)候蚌铜,GPU需要繪制圖片,繪制就要渲染圖片饺谬,然后才能顯示出來捂刺。當(dāng)然我們這里說的渲染圖片是提前渲染和異步渲染。
1.把整張圖片繪制到CGContext中商蕴,然后獲取到context中的newImage并保存下來叠萍,原圖片就可以不用了,加載的時(shí)候需要加載newImage绪商,這里代碼就是在解壓欄目里我講的“繪制圖片”代碼苛谷。
這種方法多在tableView和collectionView上使用,異步繪制圖片然后回到主線程中加載格郁,可以使滑動(dòng)非常的流暢腹殿,這種操作主要是把CPU的同步解壓操作換成異步操作,并且把GPU的渲染換成了CPU的離屏渲染例书,具體方法在“解壓圖片”欄目中給出的2锣尉、3兩點(diǎn)。
2.把整個(gè)圖片只繪制到一個(gè)像素大小的bitmap Context中决采。
CGImageRef cgImage = image.CGImage;
size_t width = CGImageGetWidth(cgImage);
size_t height = CGImageGetHeight(cgImage);
if (width == 0 || height == 0) return;
size_t bitsPerComponent = 8;
CGImageAlphaInfo alphaInfo = CGImageGetAlphaInfo(cgImage);
BOOL hasAlpha = NO;
if (alphaInfo == kCGImageAlphaPremultipliedLast ||
alphaInfo == kCGImageAlphaPremultipliedFirst ||
alphaInfo == kCGImageAlphaLast ||
alphaInfo == kCGImageAlphaFirst) {
hasAlpha = YES;
}
CGBitmapInfo info = kCGBitmapByteOrder32Host | (hasAlpha ? kCGImageAlphaPremultipliedFirst: kCGImageAlphaNoneSkipFirst);
CGContextRef context = CGBitmapContextCreate(NULL, 1, 1, bitsPerComponent, 0, SharedCGColorSpaceGetDeviceRGB(), info);
//解壓渲染自沧,但是沒有優(yōu)化
CGContextDrawImage(context, CGRectMake(0, 0, width, height), cgImage);
CGContextRelease(context);
這樣解壓整張圖片并提前渲染,好處是只繪制了一個(gè)像素,繪制沒有消耗任何時(shí)間拇厢。這樣沒有解壓后的圖片爱谁,只能加載原圖,解壓數(shù)據(jù)和渲染數(shù)據(jù)都在系統(tǒng)緩存中孝偎。
這種方法一般用于靜態(tài)頁面中加載圖片访敌。正因?yàn)橹患虞d原圖,導(dǎo)致原圖如果被釋放衣盾,解壓數(shù)據(jù)等在緩存中也會(huì)釋放寺旺,而且正因?yàn)橐蕾囉诰彺妫绻彺姹会尫攀凭觯敲催@個(gè)方法相當(dāng)于無效操作阻塑。
實(shí)際使用中,
- 如果加載的是本地圖片徽龟,不大且重復(fù)使用叮姑,那最好還是使用+imageNamed:系統(tǒng)會(huì)做解壓緩存處理;
- 如果加載本地大圖据悔,或者就一次使用的圖片传透,那么最好用
+imageWithContentsOfFile:
加載圖片; - 對(duì)于滑動(dòng)顯示大量圖片的頁面极颓,把整張圖片異步繪制到CGContext可能是更好的選擇朱盐。
這個(gè)demo是寫這邊文章時(shí)寫的AsynDisplayImage,有興趣可以看下菠隆。