zoukankan      html  css  js  c++  java
  • YYKit探究之YYImage(一)

    1.读取图片数据

    + (nullable YYImage *)imageNamed:(NSString *)name; // no cache!
    + (nullable YYImage *)imageWithContentsOfFile:(NSString *)path;
    + (nullable YYImage *)imageWithData:(NSData *)data;
    + (nullable YYImage *)imageWithData:(NSData *)data scale:(CGFloat)scale;

    这4种类方法用于获取图片数据/路径。前三种方法的实现最后都归结为 imageWithData:(NSData *)data scale:(CGFloat)scale 这个方法。让我们看看imageWithData:(NSData *)data scale:(CGFloat)scale 这个方法的实现逻辑。

    2.由实例方法演变而来。

    + (YYImage *)imageWithData:(NSData *)data scale:(CGFloat)scale {
        return [[self alloc] initWithData:data scale:scale];
    }

    具体看实例方法的逻辑。

    - (instancetype)initWithData:(NSData *)data scale:(CGFloat)scale {
        if (data.length == 0) return nil;
      //获取屏幕分辨率,传入解码decoder
    if (scale <= 0) scale = [UIScreen mainScreen].scale;
      //创建信号量,参数:信号量的初值,如果小于0则会返回NULL,crate的value表示,最多几个资源可访问 _preloadedLock
    = dispatch_semaphore_create(1); @autoreleasepool {
      //  解析图片数据,见2 YYImageDecoder
    *decoder = [YYImageDecoder decoderWithData:data scale:scale];
      //对当前的图片进行解码操作,见3 YYImageFrame
    *frame = [decoder frameAtIndex:0 decodeForDisplay:YES];
      //获取图片第一帧 UIImage
    *image = frame.image; if (!image) return nil;
      //Image初始化 self
    = [self initWithCGImage:image.CGImage scale:decoder.scale orientation:image.imageOrientation]; if (!self) return nil; _animatedImageType = decoder.type;
      //  动图的处理方法
    if (decoder.frameCount > 1) { _decoder = decoder; _bytesPerFrame = CGImageGetBytesPerRow(image.CGImage) * CGImageGetHeight(image.CGImage); _animatedImageMemorySize = _bytesPerFrame * decoder.frameCount; } self.isDecodedForDisplay = YES; } return self; }

    2.1,解析图片数据

    + (instancetype)decoderWithData:(NSData *)data scale:(CGFloat)scale {
        if (!data) return nil;
        YYImageDecoder *decoder = [[YYImageDecoder alloc] initWithScale:scale];
        [decoder updateData:data final:YES];
        if (decoder.frameCount == 0) return nil;
        return decoder;
    }

    这一步主要看updateData里面做了什么

    - (BOOL)updateData:(NSData *)data final:(BOOL)final {
        BOOL result = NO;
        //对线程上锁,此时其他线程阻塞等待该线程释放锁
        pthread_mutex_lock(&_lock);
        //这里先判断图片格式YYImageDetectType((__bridge CFDataRef)data),再根据格式获取图片帧
        result = [self _updateData:data final:final];
    //   //执行完后释放锁
        pthread_mutex_unlock(&_lock);
        return result;
    }

    2.2.获取图片帧数据

      updateData里面除了判断图片格式,是否是动图,还要更新源(可以是data,也可以是路径),下面以updataSourceAPNG为例,剖析实现过程

    [self _updateSourceImageIO]; // decode first frame
        if (_frameCount == 0) return; // png decode failed
        if (!_finalized) return; // ignore multi-frame before finalized
            //_data.bytes 提供存储apng的内存对应的指针,获取动图的数据信息
        yy_png_info *apng = yy_png_info_create(_data.bytes, (uint32_t)_data.length);
        if (!apng) return; // apng decode failed
        if (apng->apng_frame_num == 0 ||
            (apng->apng_frame_num == 1 && apng->apng_first_frame_is_cover)) {
            yy_png_info_release(apng);
            return; // no animation
        }
        if (_source) { // apng decode succeed, no longer need image souce
            CFRelease(_source);
            _source = NULL;
        }

      其中_data.bytes 提供存储apng的内存对应的指针,获取动图的数据信息,接下来就是对动图各个帧数据处理

      uint32_t canvasWidth = apng->header.width;
        uint32_t canvasHeight = apng->header.height;
        NSMutableArray *frames = [NSMutableArray new];
        BOOL needBlend = NO;
        uint32_t lastBlendIndex = 0;
      //PNG动画的逐帧信息返回到Frames数组中
    for (uint32_t i = 0; i < apng->apng_frame_num; i++) { _YYImageDecoderFrame *frame = [_YYImageDecoderFrame new]; [frames addObject:frame]; yy_png_frame_info *fi = apng->apng_frames + i; frame.index = i; frame.duration = yy_png_delay_to_seconds(fi->frame_control.delay_num, fi->frame_control.delay_den); frame.hasAlpha = YES; frame.width = fi->frame_control.width; frame.height = fi->frame_control.height; frame.offsetX = fi->frame_control.x_offset; frame.offsetY = canvasHeight - fi->frame_control.y_offset - fi->frame_control.height; BOOL sizeEqualsToCanvas = (frame.width == canvasWidth && frame.height == canvasHeight); BOOL offsetIsZero = (fi->frame_control.x_offset == 0 && fi->frame_control.y_offset == 0); frame.isFullSize = (sizeEqualsToCanvas && offsetIsZero); switch (fi->frame_control.dispose_op) { case YY_PNG_DISPOSE_OP_BACKGROUND: { frame.dispose = YYImageDisposeBackground; } break; case YY_PNG_DISPOSE_OP_PREVIOUS: { frame.dispose = YYImageDisposePrevious; } break; default: { frame.dispose = YYImageDisposeNone; } break; } switch (fi->frame_control.blend_op) { case YY_PNG_BLEND_OP_OVER: { frame.blend = YYImageBlendOver; } break; default: { frame.blend = YYImageBlendNone; } break; } if (frame.blend == YYImageBlendNone && frame.isFullSize) { frame.blendFromIndex = i; if (frame.dispose != YYImageDisposePrevious) lastBlendIndex = i; } else { if (frame.dispose == YYImageDisposeBackground && frame.isFullSize) { frame.blendFromIndex = lastBlendIndex; lastBlendIndex = i + 1; } else { frame.blendFromIndex = lastBlendIndex; } } if (frame.index != frame.blendFromIndex) needBlend = YES; } _width = canvasWidth; _height = canvasHeight; _frameCount = frames.count; _loopCount = apng->apng_loop_num; _needBlend = needBlend; _apngSource = apng; dispatch_semaphore_wait(_framesLock, DISPATCH_TIME_FOREVER); _frames = frames; dispatch_semaphore_signal(_framesLock); }

    3.各个帧数据进行解码和显示,获取imageRef的步骤如3.1所示

      //解码,从GPU中拿到imageRef
            CGImageRef imageRef = [self _newUnblendedImageAtIndex:index extendToCanvas:extendToCanvas decoded:&decoded];
            if (!imageRef) return nil;
            if (decodeForDisplay && !decoded) {
                CGImageRef imageRefDecoded = YYCGImageCreateDecodedCopy(imageRef, YES);
                if (imageRefDecoded) {
                    CFRelease(imageRef);
                    imageRef = imageRefDecoded;
                    decoded = YES;
                }
            }
        //获取当前帧 UIImage
    *image = [UIImage imageWithCGImage:imageRef scale:_scale orientation:_orientation]; CFRelease(imageRef); if (!image) return nil; image.isDecodedForDisplay = decoded; frame.image = image; return frame;

    3.1,获取imageRef

     //开始解码
            CGImageRef imageRef = CGImageSourceCreateImageAtIndex(_source, index, (CFDictionaryRef)@{(id)kCGImageSourceShouldCache:@(YES)});
            if (imageRef && extendToCanvas) {
                size_t width = CGImageGetWidth(imageRef);
                size_t height = CGImageGetHeight(imageRef);
                if (width == _width && height == _height) {
                    CGImageRef imageRefExtended = YYCGImageCreateDecodedCopy(imageRef, YES);
                    if (imageRefExtended) {
                        CFRelease(imageRef);
                        imageRef = imageRefExtended;
                        if (decoded) *decoded = YES;
                    }
                } else {
                    //没有就绘制
                    CGContextRef context = CGBitmapContextCreate(NULL, _width, _height, 8, 0, YYCGColorSpaceGetDeviceRGB(), kCGBitmapByteOrder32Host | kCGImageAlphaPremultipliedFirst);
                    if (context) {
                        //获取像素信息
                        CGContextDrawImage(context, CGRectMake(0, _height - height, width, height), imageRef);
                        CGImageRef imageRefExtended = CGBitmapContextCreateImage(context);
                        CFRelease(context);
                        if (imageRefExtended) {
                            CFRelease(imageRef);
                            imageRef = imageRefExtended;
                            if (decoded) *decoded = YES;
                        }
                    }
                }
            }
            return imageRef;

    YYImage处理图片流程

    1.用imageWithData:(NSData *)data scale:(CGFloat)scale获取图片源数据。

    2.解析图片数据,分为两步,判断图片格式(如果是动图,就开启loop),以及返回当前帧的信息yy_png_info_create(_data.bytes, (uint32_t)_data.length)。

    3.根据帧信息开始解码CGImageSourceCreateImageAtIndex,并展示出来。

  • 相关阅读:
    ie10 css3 transition 过渡
    asp.net之一步一个脚印
    MyEclipse安装aptana插件的问题(link方式)转
    MyEclipse的Aptana插件中配置ZenCoding成功
    PS CS6 安装错误:FATAL: Payload '{3F0238754A5246059DB6A88D4A813E8D} Camera Profiles Installer 6.0.98.0' information not found in Media_db.
    PC客户端的开发插件Eclipse RCP
    Silverlight获取焦点
    广而告知
    wpf窗体定位
    surface开发教程[ScatterView控件] 二.
  • 原文地址:https://www.cnblogs.com/vkSwift/p/13887346.html
Copyright © 2011-2022 走看看