美文网首页
图片加载流程探索

图片加载流程探索

作者: Code_人生 | 来源:发表于2019-09-30 14:01 被阅读0次
  • 位图:就是一个像素数组,数组中的每个像素就代表着图片中的⼀个点。我们在应⽤中经常用到的 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,解码操作
Snip20190910_7.png

二、背后发生了什么

  • 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;
}

相关文章

  • 图片加载流程探索

    位图:就是一个像素数组,数组中的每个像素就代表着图片中的⼀个点。我们在应⽤中经常用到的 JPEG 和 PNG 图片...

  • OC底层原理探索—类的加载(3)

    上一篇我们探索了类的加载流程等一系列方法以及懒加载类和非懒加载类这节课我们来探索下分类的加载流程 分类的本质 首先...

  • 图片加载过程及代码优化方案

    图片的加载流程 参考下图大致的加载流程 图片初始创建是不会解压的,只有在显示前才会去准备解压,这样如果有很多图片同...

  • 了解iOS中图片的渲染过程

    一.图片加载的工作流程 假设我们使用 +imageWithContentsOfFile: 方法从磁盘中加载一张图片...

  • dyld加载流程探索

    main函数之前底层做了那些准备? 准备 创建一个工程,在ViewController中重写了load方法,在ma...

  • Glide Engine

    图片加载引擎Engine工作流程 一、Engine 引擎 所有图片加载渠道公用,管理Jobs,load()方法,E...

  • SDL 加载一张图片

    原文地址 加载图片的流程仅仅只是在前面的创建窗口流程中加了一个加载图片 读取一张图片到Surface(bmp格式的...

  • 高性能图片优化方案

    目录介绍 01.图片基础概念介绍1.1 图片占用内存介绍1.2 加载网络图片流程1.3 三方库加载图片逻辑1.4 ...

  • IOS-SDWebImage 底层实现原理

    SDWebImage 加载图片的流程 入口 setImageWithURL:placeholderImage:op...

  • glide 源码分析笔记

    glide 是目前android最广泛的图片加载框架之一,下面我们将分析其源码加载流程。 使用流程 glide 最...

网友评论

      本文标题:图片加载流程探索

      本文链接:https://www.haomeiwen.com/subject/osmvyctx.html