美文网首页
视觉-iOS中图片文件渲染到屏幕的过程

视觉-iOS中图片文件渲染到屏幕的过程

作者: Johnny_Z | 来源:发表于2020-07-21 01:27 被阅读0次

    一、图片到屏幕view过程

    1、数据处理过程


    图片数据
    • 1)将原始图片数据(png/jpg等)加载到内存中
    • 2)CPU解压图片数据
    • 3)渲染图片数据渲染到屏幕上

    2、硬件协同处理细节

    硬件处理
    • 1)CPU:计算视图frame,图片解码,需要绘制纹理图片通过数据总线交给GPU
    • 2)GPU: 纹理混合,顶点变换与计算,像素点的填充计算,渲染到帧缓冲区
    • 3)时钟信号:垂直同步信号V-Sync / 水平同步信号H-Sync
    • 4)iOS设备双缓冲机制:显示系统通常会引入两个帧缓冲区,双缓冲机制

    二、图片加载到渲染的工作流程

    1、从文件加载图片数据 可以使用类方法+imageNamed: ,此时我们得到图片原始数据
    2、将UIImage 赋值给UIImageView
    3、CATransaction捕获到UIImageView图层树的变化
    4、主线程runloop提交 CATransaction,开始进行解码和图像渲染,这里会涉及到

    • 如果图像数据为未解码的PNG/JPG,解码为位图数据
    • GPU处理数据需要字节对齐,Core Animation会拷贝一份数据,进行字节对齐
    • GPU处理位图数据,进行渲染

    5、渲染

    • GPU获取获取图片的坐标
    • 将坐标交给顶点着色器(顶点计算)
    • 将图片光栅化(获取图片对应屏幕上的像素点)
    • 片元着色器计算(计算每个像素点的最终显示的颜色值)
    • 从帧缓存区中渲染到屏幕上

    注意:
    1、图片只有在确认需要显示时,CPU才对其进行解压;+imageName:or+imageNamed:inBundle:compatibleWithTraitCollection:加载的图片,系统会对其自动缓存,以便下次使用,但如果用+imageWithContentsOfFile:-initWithContentsOfFile:系统会每次都从disk加载,对应的都需要解压;imageWithData直接读取原始数据,如果非GPU能直接处理的数据(位图或矢量图)是需要解压的;
    2、解码是一个比较消耗CPU的操作,且默认在主线程,当界面上一次性确定显示多张图片时表现得尤为突出,此时一般的做法可以将解码操作放到异步线程去完成,解码后再用来展示

    三、探究YYImage

    1、YYImage 类结构分析

    • 图像层:YYImage, YYFrameImage, YYSpriteSheetImage
    • 视图层:YYAnimatedImageView
    • 编/解码层:YYImageCoder

    2、分析YYAnimatedImageView

    • 初始化
    - (instancetype)init {
        self = [super init];
        _runloopMode = NSRunLoopCommonModes;
        _autoPlayAnimatedImage = YES;
        return self;
    }
    
    - (instancetype)initWithFrame:(CGRect)frame {
        self = [super initWithFrame:frame];
        _runloopMode = NSRunLoopCommonModes;
        _autoPlayAnimatedImage = YES;
        return self;
    }
    
    - (instancetype)initWithImage:(UIImage *)image {
        self = [super init];
        _runloopMode = NSRunLoopCommonModes;
        _autoPlayAnimatedImage = YES;
        self.frame = (CGRect) {CGPointZero, image.size };
        self.image = image;
        return self;
    }
    

    初始化中指定了runloopModeNSRunLoopCommonModes

    • 动画的link
      [_link addToRunLoop:[NSRunLoop mainRunLoop] forMode:_runloopMode];
    

    _runloopMode 指定到了 CADisplayLinklink中,不断地替换_curFrame用来跟新layer里面显示的图片frame,将link的指定成NSRunLoopCommonModes保证了在拖动滚动视图时动画还能继续。

    • 图片解压 _YYAnimatedImageViewFetchOperation
    //创建_requestQueue 设置最大并发量1
    _requestQueue = [[NSOperationQueue alloc] init];
    _requestQueue.maxConcurrentOperationCount = 1;
    
    //解码操作加入_requestQueue
    _YYAnimatedImageViewFetchOperation *operation = [_YYAnimatedImageViewFetchOperation new];
    operation.view = self;
    operation.nextIndex = nextIndex;
    operation.curImage = image;
    [_requestQueue addOperation:operation];
    
    @implementation _YYAnimatedImageViewFetchOperation
    - (void)main {
        ...
        
        for (int i = 0; i < max; i++, idx++) {
            @autoreleasepool {
                ...
                if (miss) {
                     //解码图片,并且标记
                    UIImage *img = [_curImage animatedImageFrameAtIndex:idx];
                    img = img.yy_imageByDecoded;
                    if ([self isCancelled]) break;
                    LOCK_VIEW(view->_buffer[@(idx)] = img ? img : [NSNull null]);
                    view = nil;
                }
            }
        }
    }
    @end
    

    可以看到,_YYAnimatedImageViewFetchOperation解码image图片的操作是通过
    NSOperationQueue_requestQueue来异步来处理的

    • 图片数据buffer缓存
    NSMutableDictionary *_buffer; ///< frame buffer
    

    这里cache了显示所需要解压好的图片buffer数据,以便重复使用; 当然这里还有缓存限制和memory告警时清理缓存的机制,我这里就不一一赘述了

    参考链接:
    https://developer.apple.com/documentation/uikit/uiimage
    https://www.jianshu.com/p/72dd074728d8
    https://developer.apple.com/documentation/uikit/uiimage

    相关文章

      网友评论

          本文标题:视觉-iOS中图片文件渲染到屏幕的过程

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