图片加载流程探索

2019-09-30  本文已影响0人  Code_人生

1、从磁盘读入缓冲区
2、缓冲区拷贝到用户空间 ------- Data Buffer
3、解压缩 ------- Image Buffer
4、图片处理
5、渲染
Data Buffer ---Decode---> Image Buffer

图片显示
1、Load
2、Decode
3、Render

一、磁盘上的图片文件大小和 imageView 上的图片大小的关系

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验证
Snip20190910_7.png

二、背后发生了什么

三、优化图片

1、优化的两个点
2、主动解码
    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;
        });
    });
    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;
    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;
}
上一篇 下一篇

猜你喜欢

热点阅读