- 位图:就是一个像素数组,数组中的每个像素就代表着图片中的⼀个点。我们在应⽤中经常用到的 JPEG 和 PNG 图片就是位图。(压缩过的图⽚格式)
- 像素:字面意思上来说就是图像的基本元素。举个列子,将一张图片放到 PS 中尽可能放⼤, 那么我们可以看到⼀个个的小格式,其中每个⼩格子就是⼀个像素点,每个像素点有且仅有⼀个颜⾊。
1、从磁盘读入缓冲区
2、缓冲区拷贝到用户空间 ------- Data Buffer
3、解压缩 ------- Image Buffer
4、图片处理
5、渲染
Data Buffer ---Decode---> Image Buffer
图片显示
1、Load
2、Decode
3、Render
- DataBuffer:原始数据(jpg/png)
- 图像缓冲区(ImageBuffer):表示一种特定缓冲区,保存了图像在内存中的标识;缓冲区中元素描述了图像中每个像素的颜色和透明度(也就是以 RGBA 四个向量来标识)。因此图像缓冲区的⼤⼩和图像的⼤小成正比
- 帧缓冲区(FrameBuffer):负责在你的APP中保存实际渲染后的输出,因此当你的App更新其视图层次结构的时候,UIKit将重新渲染APP的窗⼝及其子视图到帧缓冲区当中,该帧缓冲区提供每个像素的颜色信息,而我们的硬件将读取这些信息以便点亮显示器上对应的像素。
一、磁盘上的图片文件大小和 imageView 上的图片大小的关系
- 1.1、和尺寸有关
- 1.2、和渲染图片的
Color Profile
有关,通常是ARGB - 1.3
imageView
上的图片大小 等于1920*1080*4bytes
- 1.4、和磁盘上的图片文件大小3.8MB无关
2、代码验证
// 隐式解码
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验证
- Xcode -> Open Developer -> Instruments -> Allocations
- 3.1、选择系统选择要运行App
- 3.2、选择Generations,快照
- 3.3、点击Mark Generation
- 3.4、点击
Generation B
,解码操作
二、背后发生了什么
- Data Buffer:原始数据(jpg/png)
- Image Buffer:图像像素信息,和图像的尺寸大小成正比,存放在RAM中
- Frame Buffer(帧缓冲区):存放在video RAM中
三、优化图片
1、优化的两个点
- 1.内存的占用
- 1.CPU的使用
2、主动解码
- 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 已经填充了!!
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);
//获取图像的数量
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枚举定义顺序不同,封装一个方法搞一个switch case就行
UIImageOrientation imageOrientation = LG_YYUIImageOrientationFromEXIFValue(exifOrientation);
UIImage *image = [UIImage imageWithCGImage:imageRef scale:[UIScreen mainScreen].scale orientation:imageOrientation];
// 清理,都是C指针,避免内存泄漏
CGImageRelease(imageRef);
CFRelease(sourceRef);
//解码过后的图片数据(imageBuffer)
_imageView.image = image;
- 3.动图
NSData *data = [NSData dataWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@"test@2x" ofType:@"gif"]];
CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)data, NULL);
NSUInteger frameCount = CGImageSourceGetCount(source); //帧数
//解码过后的数据!!!
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原始的帧持续时长,秒数
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, // 缩小图像的同时进行解码
(__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 -- 帧缓冲区(显示的数据信息) - Vsync - 屏幕上!
//操作像素点!- 先拿到像素点 - 修改像素点
+ (UIImage *)grayscaleImageForImage:(UIImage *)image{
NSUInteger width = CGImageGetWidth(image.CGImage);
NSUInteger height = CGImageGetHeight(image.CGImage);
CGColorSpaceRef colorSpaceRef = CGColorSpaceCreateDeviceRGB();
//开辟内存空间 - 8bits
UInt32 *imagePiexl = (UInt32 *)calloc(width * height, sizeof(UInt32));
//创建一个画板(大小,每一个像素的大小,ARGB/RGBA)
CGContextRef contextRef = CGBitmapContextCreate(imagePiexl,
width,
height,
8,
0, //64 的整数倍(对齐!!!)
colorSpaceRef,
kCGImageAlphaNoneSkipLast);
//第四步:根据图片绘制上下文(imageBuffer)
CGContextDrawImage(contextRef, CGRectMake(0, 0, width, height), image.CGImage);
//取出每一个像素点,修改每一个像素点的值~
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;
}