YYImage 源码分析

文章目录

一个 jpeg,png…… 图片是压缩过了的,解压后会变成 尺寸 * 4byte
eg: png 尺寸 480 * 720,需要解压成位图 bitmap 480 * 720 * 4byte

1
UIImage *img = [UIImage imageNamed:@"xxx"];// 隐式解码

自己控制解码

CoreGraphic

ImageIO

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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;

动图:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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];
}

// 最后生成动图
UIImage *animatedImage = [UIImage animatedImageWithImages:images duration:totalDuration];
_imageView.image = animatedImage;

内存占用率很高
如何减小内存占用呢?

下采样,不是加载全图,而是根据当前 显示尺寸下采样图片信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

// 大图缩小为显示尺寸的图
- (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;
}

对于 YYImage 中核心

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//开辟内存空间 - 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 的作用
}
}