• FLAnimatedImageView处理gif过程


    FLAnimatedImageView处理gif过程

    时间控制原理

    GIF图片每一帧的delayTime可能都不一样;

    不同的delayTime

    在展示下一帧的时间控制机制,不能根据以第一帧为准;

    以第一帧为准

    或总动画时长除以帧数来简单做平均值为准,

    以平均值为准

    都是不太好的方案。

    FLAnimatedImageView的控制方式,读取每一帧的delayTime算出最大公约数,用CADisplayLink来控制时间的,比如说(如下图),

    第二帧到第三帧的控制:第二帧的delayTime=2s,第三帧的delayTime=3s,如果第二帧没到时间,FLAnimatedImageView的image数据保持不变,时间一到就从FLAnimatedImage中获取image,赋值给ImageView。

    科学的控制时间方式

    CADisplayLink处理过程

    首先startAnimating中的CADisplayLink初始化,为了防止retain cycle 用了NSProxy weak语义的property,也可以用

    __weak typeof(self) wself = self;
        ...
        // block中处理
        __strong typeof(wself) sself = wself;
        if (!sself) {
            return;
        }
        // 紧跟处理code

    反正都与weak有关,都是固定套路了。

    CADisplayLink的frameInterval,frameInterval = 1时,refreshRate = 60Hz,frameInterval = 2时,refreshRate = 30Hz; 
    此处设置的

    const NSTimeInterval kDisplayRefreshRate = 60.0; // 60Hz
    
    // 最小的frameInterval = 1
    self.displayLink.frameInterval = MAX([self frameDelayGreatestCommonDivisor] * kDisplayRefreshRate, 1);

    接下来就是最关键的处理方法- (void)displayDidRefresh:(CADisplayLink *)displayLink

    1,如果self.needsDisplayWhenImageBecomesAvailable==YES,调用[self.layer setNeedsDisplay];,标记layer需要刷新,下一次runloop中displayLayer方法会被调用;然后设置self.needsDisplayWhenImageBecomesAvailable=NO不到下一帧的时间,不改变ImageView的内容; 
    赋值代码相当简单:

    - (void)displayLayer:(CALayer *)layer
    {
        // 从 image 方法中取currentFrame作为像是内容
        layer.contents = (__bridge id)self.image.CGImage;
    }

    2,累加时间值accumulator跟当前帧的delayTime比对,注释如下:

    /**
     * duration属性提供了每帧之间的时间,也就是屏幕每次刷新之间的的时间,一个略小的值;
     * frameInterval相当一个>=1的固定值,每次refresh,accumulator都累加一小段时间
     **/
    self.accumulator += displayLink.duration * displayLink.frameInterval;
    
    // While-loop first inspired by & good Karma to: https://github.com/ondalabs/OLImageView/blob/master/OLImageView.m
    
    /**
     * 1,如果self.accumulator < 当前的delayTime,直接跳过,下一次refresh,accumulator再累加,相当于这一次image的内容不变;
     * 2,只有当累加值self.accumulator >= 当前的delayTime(说明已经是累加到这一帧的delayTime的最后,下一帧的开头了),进入while循环,循环内部每次将累加器减掉delayTime(accumulator又回到0状态),以便跳出循环;currentFrameIndex累加,下次refresh时,获取下一帧图像数据;
     * 3,如果到达最后一帧,循环次数loopCountdown--,又跳转到首帧图像;
     * 4,最主要的一步标记self.needsDisplayWhenImageBecomesAvailable = YES;下一次refresh时,执行[self.layer setNeedsDisplay]; 进行新一帧图像数据的刷新。
     **/
    while (self.accumulator >= delayTime) {
        self.accumulator -= delayTime;
        self.currentFrameIndex++;
        if (self.currentFrameIndex >= self.animatedImage.frameCount) {
            // If we've looped the number of times that this animated image describes, stop looping.
            self.loopCountdown--;
            if (self.loopCompletionBlock) {
                self.loopCompletionBlock(self.loopCountdown);
            }
    
            if (self.loopCountdown == 0) {
                [self stopAnimating];
                return;
            }
            self.currentFrameIndex = 0;
        }
        // Calling `-setNeedsDisplay` will just paint the current frame, not the new frame that we may have moved to.
        // Instead, set `needsDisplayWhenImageBecomesAvailable` to `YES` -- this will paint the new image once loaded.
        self.needsDisplayWhenImageBecomesAvailable = YES;
    }

    至此就时间控制过程基本处理完成。 
    另外FLAnimatedImage类,专门根据gif图片大小来限制缓存多少帧,当遇到memoryWarning时,如何重试处理;多次遇到memoryWarning,固定缓存帧数等等功能,而且没有用到dispatch_semaphore等加锁/解锁操作;性能有保证。

  • 相关阅读:
    1010考试T1
    P5631 最小mex生成树 分治 并查集
    P4366 [Code+#4]最短路 建图 最短路
    P1654 OSU! 期望概率DP
    7.26集训
    7.25集训
    7.23集训
    7.22集训
    7.21test
    7.12test
  • 原文地址:https://www.cnblogs.com/edisongz/p/6983183.html
Copyright © 2020-2023  润新知