- 位圖:就是一個(gè)像素?cái)?shù)組,數(shù)組中的每個(gè)像素就代表著圖片中的?個(gè)點(diǎn)。我們?cè)趹?yīng)?中經(jīng)常用到的 JPEG 和 PNG 圖片就是位圖。(壓縮過的圖?格式)
- 像素:字面意思上來說就是圖像的基本元素。舉個(gè)列子,將一張圖片放到 PS 中盡可能放?, 那么我們可以看到?個(gè)個(gè)的小格式,其中每個(gè)?格子就是?個(gè)像素點(diǎn),每個(gè)像素點(diǎn)有且僅有?個(gè)顏?。
1、從磁盤讀入緩沖區(qū)
2、緩沖區(qū)拷貝到用戶空間 ------- Data Buffer
3、解壓縮 ------- Image Buffer
4、圖片處理
5、渲染
Data Buffer ---Decode---> Image Buffer
圖片顯示
1、Load
2、Decode
3、Render
- DataBuffer:原始數(shù)據(jù)(jpg/png)
- 圖像緩沖區(qū)(ImageBuffer):表示一種特定緩沖區(qū),保存了圖像在內(nèi)存中的標(biāo)識(shí);緩沖區(qū)中元素描述了圖像中每個(gè)像素的顏色和透明度(也就是以 RGBA 四個(gè)向量來標(biāo)識(shí))。因此圖像緩沖區(qū)的??和圖像的?小成正比
- 幀緩沖區(qū)(FrameBuffer):負(fù)責(zé)在你的APP中保存實(shí)際渲染后的輸出,因此當(dāng)你的App更新其視圖層次結(jié)構(gòu)的時(shí)候,UIKit將重新渲染APP的窗?及其子視圖到幀緩沖區(qū)當(dāng)中,該幀緩沖區(qū)提供每個(gè)像素的顏色信息,而我們的硬件將讀取這些信息以便點(diǎn)亮顯示?上對(duì)應(yīng)的像素。
一、磁盤上的圖片文件大小和 imageView 上的圖片大小的關(guān)系

- 1.1、和尺寸有關(guān)
- 1.2、和渲染圖片的
Color Profile有關(guān),通常是ARGB - 1.3
imageView上的圖片大小 等于1920*1080*4bytes - 1.4、和磁盤上的圖片文件大小3.8MB無關(guān)
2、代碼驗(yàn)證
// 隱式解碼
UIImage *image = [UIImage imageNamed:@"1920-1080"];
_imageView.image = image;
CFDataRef rawData = CGDataProviderCopyData(CGImageGetDataProvider(_imageView.image.CGImage));
NSLog(@"%ld",[(__bridge NSData *)rawData length]);//8294400
3、Allocations驗(yàn)證
- Xcode -> Open Developer -> Instruments -> Allocations
- 3.1、選擇系統(tǒng)選擇要運(yùn)行App

- 3.2、選擇Generations,快照

- 3.3、點(diǎn)擊Mark Generation

- 3.4、點(diǎn)擊
Generation B,解碼操作

Snip20190910_7.png
二、背后發(fā)生了什么
- Data Buffer:原始數(shù)據(jù)(jpg/png)
- Image Buffer:圖像像素信息,和圖像的尺寸大小成正比,存放在RAM中
- Frame Buffer(幀緩沖區(qū)):存放在video RAM中


三、優(yōu)化圖片
1、優(yōu)化的兩個(gè)點(diǎn)
- 1.內(nèi)存的占用
- 1.CPU的使用
2、主動(dòng)解碼
- 1.Core Graphics
UIImage *image = [UIImage imageNamed:@"1920-1080"];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
UIGraphicsBeginImageContextWithOptions(image.size, YES, [UIScreen mainScreen].scale);
[image drawAtPoint:CGPointZero];
//Image Buffer 已經(jīng)填充了??!
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
dispatch_async(dispatch_get_main_queue(), ^{
self->_imageView.image = newImage;
});
});
- 2.ImageIO
NSData *data = [NSData dataWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@"logic" ofType:@"png"]];
//輸入源?。?!
CGImageSourceRef sourceRef = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
//獲取圖片的類型
NSString *typeStr = (__bridge NSString *)CGImageSourceGetType(sourceRef);
//獲取圖像的數(shù)量
NSUInteger count = CGImageSourceGetCount(sourceRef);
NSDictionary *imageProperties = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(sourceRef, 0, NULL);
NSUInteger width = [imageProperties[(__bridge NSString *)kCGImagePropertyPixelWidth] unsignedIntegerValue]; //寬度,像素值
NSUInteger height = [imageProperties[(__bridge NSString *)kCGImagePropertyPixelHeight] unsignedIntegerValue]; //高度,像素值
BOOL hasAlpha = [imageProperties[(__bridge NSString *)kCGImagePropertyHasAlpha] boolValue]; //是否含有Alpha通道
CGImagePropertyOrientation exifOrientation = [imageProperties[(__bridge NSString *)kCGImagePropertyOrientation] integerValue]; // 這里也能直接拿到EXIF方向信息,和前面的一樣。如果是iOS 7,就用NSInteger取吧 :)
//解碼的操作!??!
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(sourceRef, 0, NULL);
// UIImageOrientation和CGImagePropertyOrientation枚舉定義順序不同,封裝一個(gè)方法搞一個(gè)switch case就行
UIImageOrientation imageOrientation = LG_YYUIImageOrientationFromEXIFValue(exifOrientation);
UIImage *image = [UIImage imageWithCGImage:imageRef scale:[UIScreen mainScreen].scale orientation:imageOrientation];
// 清理,都是C指針,避免內(nèi)存泄漏
CGImageRelease(imageRef);
CFRelease(sourceRef);
//解碼過后的圖片數(shù)據(jù)(imageBuffer)
_imageView.image = image;
- 3.動(dòng)圖
NSData *data = [NSData dataWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@"test@2x" ofType:@"gif"]];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
NSUInteger frameCount = CGImageSourceGetCount(source); //幀數(shù)
//解碼過后的數(shù)據(jù)?。。? NSMutableArray <UIImage *> *images = [NSMutableArray array];
double totalDuration = 0;
for (size_t i = 0; i < frameCount; i++) {
NSDictionary *frameProperties = (__bridge NSDictionary *) CGImageSourceCopyPropertiesAtIndex(source, i, NULL);
NSDictionary *gifProperties = frameProperties[(NSString *)kCGImagePropertyGIFDictionary]; // GIF屬性字典
double duration = [gifProperties[(NSString *)kCGImagePropertyGIFUnclampedDelayTime] doubleValue]; // GIF原始的幀持續(xù)時(shí)長,秒數(shù)
CGImagePropertyOrientation exifOrientation = [frameProperties[(__bridge NSString *)kCGImagePropertyOrientation] integerValue]; // 方向
CGImageRef imageRef = CGImageSourceCreateImageAtIndex(source, i, NULL); // CGImage
UIImageOrientation imageOrientation = LG_YYUIImageOrientationFromEXIFValue(exifOrientation);
UIImage *image = [UIImage imageWithCGImage:imageRef scale:[UIScreen mainScreen].scale orientation:imageOrientation];
totalDuration += duration;
[images addObject:image];
3、降低采樣率
// 大圖縮小為顯示尺寸的圖
- (UIImage *)downsampleImageAt:(NSURL *)imageURL to:(CGSize)pointSize scale:(CGFloat)scale {
NSDictionary *imageSourceOptions =
@{
(__bridge NSString *)kCGImageSourceShouldCache: @NO //原始圖像不解碼
};
CGImageSourceRef imageSource =
CGImageSourceCreateWithURL((__bridge CFURLRef)imageURL, (__bridge CFDictionaryRef)imageSourceOptions);
CGFloat maxDimensionInPixels = MAX(pointSize.width, pointSize.height) * scale;
NSDictionary *downsampleOptions =
@{
(__bridge NSString *)kCGImageSourceCreateThumbnailFromImageAlways: @YES,
(__bridge NSString *)kCGImageSourceShouldCacheImmediately: @YES, // 縮小圖像的同時(shí)進(jìn)行解碼
(__bridge NSString *)kCGImageSourceCreateThumbnailWithTransform: @YES,
(__bridge NSString *)kCGImageSourceThumbnailMaxPixelSize: @(maxDimensionInPixels)
};
CGImageRef downsampledImage =
CGImageSourceCreateThumbnailAtIndex(imageSource, 0, (__bridge CFDictionaryRef)downsampleOptions);
UIImage *image = [[UIImage alloc] initWithCGImage:downsampledImage];
CGImageRelease(downsampledImage);
CFRelease(imageSource);
return image;
}
四、CGBitmapContextCreate
//彩色空間變成灰色空間
//CPU -- GPU -- 幀緩沖區(qū)(顯示的數(shù)據(jù)信息) - Vsync - 屏幕上!
//操作像素點(diǎn)!- 先拿到像素點(diǎn) - 修改像素點(diǎn)
+ (UIImage *)grayscaleImageForImage:(UIImage *)image{
NSUInteger width = CGImageGetWidth(image.CGImage);
NSUInteger height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
//開辟內(nèi)存空間 - 8bits
UInt32 *imagePiexl = (UInt32 *)calloc(width * height, sizeof(UInt32));
//創(chuàng)建一個(gè)畫板(大小,每一個(gè)像素的大小,ARGB/RGBA)
CGContextRef contextRef = CGBitmapContextCreate(imagePiexl,
width,
height,
8,
0, //64 的整數(shù)倍(對(duì)齊?。。。? colorSpaceRef,
kCGImageAlphaNoneSkipLast);
//第四步:根據(jù)圖片繪制上下文(imageBuffer)
CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), image.CGImage);
//取出每一個(gè)像素點(diǎn),修改每一個(gè)像素點(diǎn)的值~
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
uint8_t *rgbPiexl = (uint8_t *)&imagePiexl[y * width + x];
//像素操作?。?!
//cpu - gpu 的作用
}
}
CGImageRef imageRef = CGBitmapContextCreateImage(contextRef);
CGContextRelease(contextRef);
CGColorSpaceRelease(colorSpaceRef);
free(imagePiexl);
UIImage *resultUIImage = [UIImage imageWithCGImage:imageRef scale:image.scale orientation:UIImageOrientationUp];
CGImageRelease(imageRef);
return nil;
}