iOSQuart2D绘图之UIImage简单使用

代码地址以下:
http://www.demodashi.com/demo/11609.htmlhtml

人生得意须尽欢,莫使金樽空对月。
   天生我材必有用,千金散尽还复来。面试

前记

说到UIImage你们都不会感到陌生,最近在研究UIImage,就顺便把以前工做中用到的一些category总结了一下,记录记录。数组

在这以前先看一下一些简答的效果图缓存

一、简单处理

12.png

二、GIF图片加载

12.gif

三、图片添加文字、图片及图片截屏、擦除

添加文字.png

添加图片.png

截图.png

wipe.gif

代码分析
一、图片圆角处理

关于图片的圆角处理,这个可能用的比较多点,固然你也能够用给UIIImageView设置圆角来达到目的,可是在性能上特别是用在tableview上的时候,就没那么好了。服务器

在这以前,咱们先来看看CGContextRef,这个怎么解释呢?个人理解就是画布,就至关于咱们在画画的时候的画板,咱们须要画图片、文字、线条都须要在这上面进行。
在了解这个以后,咱们就开始看代码ide

带圆圈的圆图函数

+ (UIImage*)gl_circleImage:(UIImage*)image withBorder:(CGFloat)border color:(UIColor *)color
{
    //经过本身建立一个context来绘制,一般用于对图片的处理
    //在retian屏幕上要使用这个函数,才能保证不失真
    //该函数会自动建立一个context,并把它push到上下文栈顶,坐标系也经处理和UIKit的坐标系相同
    UIGraphicsBeginImageContextWithOptions(CGSizeMake(image.size.width, image.size.height), NO, [UIScreen mainScreen].scale);
    //获取上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
    //设置宽度
    CGContextSetLineWidth(context, 4*border);
    //设置边框颜色
    CGContextSetStrokeColorWithColor(context, color.CGColor);

    //画椭圆 当宽和高同样的时候 为圆 此处设置可视范围
    CGContextAddEllipseInRect(context, rect);
    //剪切可视范围
    CGContextClip(context);

    //绘制图片
    [image drawInRect:rect];

    CGContextAddEllipseInRect(context, rect);
    // 绘制当前的路径 只描绘边框
    CGContextStrokePath(context);

    
    UIImage *newimg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newimg;
}

本想讲点什么,可是代码中已经有注释,就不说什么了,这里须要注意的是UIGraphicsBeginImageContextWithOptions这个函数,最后一个参数--缩放因子,和咱们平时用的倍图相关,好比@x,@2x,@3x,在4s上为1,后面的机型中,plus3,其他为2,在这里能够设置为0,这样会自动匹配。性能

不带圆圈的圆图动画

/**
 建立圆形图片

 @param image 原始图片
 @return 返回
 */
+ (UIImage *)gl_circleImage:(UIImage *)image;

因为和带圆圈的原图代码几乎一致,只是少了边框这部分代码,这里就不在贴出来了,后面会给出demothis

二、根据颜色建立图片

这个比较简单,最主要的是靠下面两个函数来进行实现

//设置填充颜色
    CGContextSetFillColorWithColor(context, color.CGColor);
    //直接按rect的范围覆盖
    CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));

在这里有两个函数,CGContextSetFillColorWithColorCGContextSetStrokeColorWithColor,是相对的两个函数,一个是设置覆盖区域的颜色,一个是设置边框的颜色,在使用的时候应该和其余函数对应一块儿使用。
下面仍是列出代码

+ (UIImage *)gl_imageWithColor:(UIColor *)color size:(CGSize)size{
    CGSize imageSize = size;
    //经过本身建立一个context来绘制,一般用于对图片的处理
    UIGraphicsBeginImageContextWithOptions(imageSize, NO, [UIScreen mainScreen].scale);
    //获取上下文
    CGContextRef context = UIGraphicsGetCurrentContext();
    //设置填充颜色
    CGContextSetFillColorWithColor(context, color.CGColor);
    //直接按rect的范围覆盖
    CGContextFillRect(context, CGRectMake(0, 0, size.width, size.height));
    UIImage *newimg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newimg;
}

因为根据颜色来绘制圆形图片代码和上面几乎一致,只是将其中的
CGContextFillRect换成了CGContextAddEllipseInRectCGContextFillPath,这里就不在详细讲解

三、给图片设置圆角

这个可能用的比较多点,好比当美工不给我切图的时候,咱们又须要使图片其中的几个角有圆角,那么这时候这个方法就派上用场了。

在这里,主要是经过方法CGContextAddPathUIBezierPath一块儿来实现,详细看到这里,你们都知道怎么用的了,下面咱们就来看源码

+ (UIImage*)gl_cornerImage:(UIImage*)image corner:(CGFloat)corner rectCorner:(UIRectCorner)rectCorner
{
    CGSize imageSize = image.size;
    UIGraphicsBeginImageContextWithOptions(imageSize, NO, [UIScreen mainScreen].scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGRect rect = CGRectMake(0,
                             0,
                             imageSize.width,
                             imageSize.height);
    UIBezierPath *path = [UIBezierPath bezierPathWithRoundedRect:rect
                                               byRoundingCorners:rectCorner
                                                     cornerRadii:CGSizeMake(corner,
                                                                            corner)];
    //添加路径
    CGContextAddPath(context, [path CGPath]);
    //剪切可视范围
    CGContextClip(context);
    [image drawInRect:rect];
    
    UIImage *newimg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newimg;
}
四、压缩图片

说到压缩图片,这个应该是用的最多的了,好比有一天忽然项目经理给你说,咱们上传的这个图片啊,太大了,不只浪费流量还特别慢,并且服务器压力还不小....大家客户端处理下把,最好在250kb左右,可是又不能裁剪图片,这个时候,这个方法就能大展身手了。
其主要思路是经过一个while循环,不停的去缩小咱们所要的图片,固然计算图片的大小,咱们用的是UIImageJPEGRepresentation,虽然这个和真实图片的大小仍是有那么点偏差,可是通常状况下仍是没什么问题的,下面咱们来看源码

+ (UIImage*)gl_compressImage:(UIImage *)image maxSize:(CGFloat)maxSize maxSizeWithKB:(CGFloat)maxSizeKB
{    
    if (maxSize <= 0) {
        return nil;
    }
    
    if (maxSizeKB <= 0) {
        return nil;
    }

    CGSize compressSize = image.size;
    //获取缩放比 进行比较 
    CGFloat widthScale = compressSize.width*1.0 / maxSize;
    CGFloat heightScale = compressSize.height*1.0 / maxSize;
    
    if (widthScale > 1 && widthScale > heightScale) {
        compressSize = CGSizeMake(image.size.width/widthScale, image.size.height/widthScale);
    }
    else if (heightScale > 1 && heightScale > widthScale){
        compressSize = CGSizeMake(image.size.width/heightScale, image.size.height/heightScale);
    }
    
    //建立图片上下文 并获取剪切尺寸后的图片
    UIGraphicsBeginImageContextWithOptions(compressSize, NO, 1);
    CGRect rect = {CGPointZero,compressSize};
    [image drawInRect:rect];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    //循环缩小图片大小
    NSData *imageData = UIImageJPEGRepresentation(newImage, 1.0);
    //获取当前图片的大小
    CGFloat currentImageSizeOfKB = imageData.length/1024.0;
    
    //压缩比例
    CGFloat compress = 0.9;
    
    while (currentImageSizeOfKB > maxSizeKB && compress > 0.1) {
        imageData = UIImageJPEGRepresentation(newImage, compress);
        currentImageSizeOfKB = imageData.length/1024.0;
        compress -= 0.1;
    }
    return [UIImage imageWithData:imageData];
}
五、加载GIF图片

说到加载动态图片,需求量虽然不大,但仍是偶尔会用到,这里咱们有一个很关键的API---+ (nullable UIImage *)animatedImageWithImages:(NSArray<UIImage *> *)images duration:(NSTimeInterval)duration,经过这个函数,咱们能够简单的来播放动态图片。下面咱们要作的就是怎么获得GIF图片的图片资源和其播放时间duration

关于图片源,在ImageIo中有这么一个类CGImageSource,其中的CGImageSourceRef也就是咱们所说的图片源,查看API,咱们能够获得三种方式:

一、CGImageSourceCreateWithURL
基于一个URL连接来读取图片信息。这个方法也是苹果推荐的方法,由于有些时候咱们想获取照片的信息,但不须要将照片加载到内存中(由于这是不必的),因此只须要给出照片的URL地址。
二、CGImageSourceCreateWithData
这个方法是基于一个NSData对象来获取照片信息。因此若是想使用此方法,必需将一个UIImage对象转换成NSData对象,例如:

NSString *filePath = [[NSBundle mainBundle]pathForResource:@"24" ofType:@"jpg"];
    NSData *data = [NSData dataWithContentsOfFile:filePath];
    CGImageSourceRef imageSource = CGImageSourceCreateWithData((__bridge CFDataRef)data, nil);

三、CGImageSourceCreateWithDataProvider
这种方法主要是须要将一个CGImageRef生成一个CGDataProvider。而参数"options"选项是一个字典,用于建立图片源时提供的附加属性,例如是否对图片进行缓存等。

在有了获得图片源的方法后,咱们就能够经过图片源来获得其中的相关信息,最主要的是动画时间,具体代码以下

static CGFloat frameDuration(NSInteger index,CGImageSourceRef source)
{
    //获取每一帧的信息
    CFDictionaryRef frameProperties = CGImageSourceCopyPropertiesAtIndex(source,index, nil);
    //转换为dic
    NSDictionary *framePropertiesDic = (__bridge NSDictionary *)frameProperties;
    //获取每帧中关于GIF的信息
    NSDictionary *gifProperties = framePropertiesDic[(__bridge NSString *)kCGImagePropertyGIFDictionary];
    /*
     苹果官方文档中的说明
     kCGImagePropertyGIFDelayTime
     The amount of time, in seconds, to wait before displaying the next image in an animated sequence
     
     kCGImagePropertyGIFUnclampedDelayTime
     The amount of time, in seconds, to wait before displaying the next image in an animated sequence. This value may be 0 milliseconds or higher. Unlike the kCGImagePropertyGIFDelayTime property, this value is not clamped at the low end of the range.
     
     看了翻译瞬间蒙了 感受同样 可是kCGImagePropertyGIFDelayTime 可能为0  因此我以为能够先判断kCGImagePropertyGIFDelayTime
     */
    CGFloat duration = 0.1;
    
    NSNumber *unclampedPropdelayTime = gifProperties[(__bridge NSString *)kCGImagePropertyGIFUnclampedDelayTime];
    NSNumber *delayTime = gifProperties[(__bridge NSString *)kCGImagePropertyGIFDelayTime];
    
    if (unclampedPropdelayTime) {
        duration = unclampedPropdelayTime.floatValue;
    }else{
        if (delayTime) {
            duration = delayTime.floatValue;
        }
    }
    
    CFRelease(frameProperties);
    
    return duration;
}

在有了动态图片时间后,咱们来看看后续动态图片的实现

//动态图片处理
static UIImage *animatedImageWithAnimateImageSource(CGImageSourceRef imageSource)
{
    if (imageSource) {
        //获得图片资源的数量
        size_t imageCount = CGImageSourceGetCount(imageSource);
        
        //最终图片资源
        UIImage *resultImage = nil;
        
        //动态图片时间
        NSTimeInterval duration = 0.0;
        //取图片资源
        NSMutableArray *images = [NSMutableArray arrayWithCapacity:imageCount];
        
        for (size_t i = 0; i < imageCount; i ++) {
            //此处用到了create  后面记得释放
            CGImageRef cgImage = CGImageSourceCreateImageAtIndex(imageSource, i, NULL);
            
            if (cgImage) {
                //将图片加入到数组中
                [images addObject:[UIImage imageWithCGImage:cgImage scale:[UIScreen mainScreen].scale orientation:UIImageOrientationUp]];
            }
            
            duration += frameDuration(i, imageSource);
            
            //释放掉 否则会内存泄漏
            CGImageRelease(cgImage);
        }
        
        if (duration == 0.0) {
            duration = 0.1 * imageCount;
        }
        
        
        resultImage = [UIImage animatedImageWithImages:images duration:duration];
        
        CFRelease(imageSource);
        
        return resultImage;
    }
    return nil;
}

这样的话,咱们就能够加载咱们所须要的动态图片了。在上述代码中,须要注意的几个地方
一、使用了create和copy这样的函数,咱们须要手动对其内存进行管理
二、在封装方法的时候,我只封装了用图片url和图片二进制文件以及图片资源地址的方法,须要注意的是,在使用图片二进制文件的时候,须要特别注意二进制文件的转化,不能使用UIImageJPEGRepresentation,亲测不对,不知道gif用此方法为何不行,建议经过路径的方式来获取data,如NSData *data = [NSData dataWithContentsOfFile:imagePath],上面是核心代码,其余部分,能够参考后面的demo

六、图片添加文字或者图片

这个比较简单,最主要的方法就是经过drawInRect来将文字或者图片画到当前的画布上,这里就只列一个添加文字的代码出来,添加图片的后面能够参考demo

+ (UIImage *)gl_addTitleAboveImage:(UIImage *)image addTitleText:(NSString *)text
                   attributeDic:(NSDictionary *)attributeDic point:(CGPoint)point
{
    UIGraphicsBeginImageContextWithOptions(image.size, NO, [UIScreen mainScreen].scale);
    
    CGRect imageRect = CGRectMake(0, 0, image.size.width, image.size.height);
    
    [image drawInRect:imageRect];
    
    [text drawAtPoint:point withAttributes:attributeDic];
    
    //获取上下文中的新图片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    
    return newImage;
}

须要注意的是函数UIGraphicsBeginImageContextWithOptions中的最后一个参数,须要根据当前屏幕来,不然添加的文字和图片可能会出现模糊,如在5S上面,将其设为1

七、图片截屏或者擦除

在这两个功能中,最主要用到的函数是renderInContextCGContextClearRect,第一个主要是用来截屏用的,渲染当前图片,与之接近的还有一个函数drawInContext,固然,咱们不能用这个函数,由于这个函数不能渲染图片,只针对layer。然后面的一个函数主要是用来清除该区域的。经过上面两个函数,咱们就可以轻松的实现擦除功能,就如橡皮擦功能。
代码以下

+ (UIImage *)gl_wipeImageWithView:(UIView *)view movePoint:(CGPoint)point brushSize:(CGSize)size
{
    //开启上下文
    UIGraphicsBeginImageContext(view.bounds.size);
    
    CGContextRef context = UIGraphicsGetCurrentContext();
    //此方法不能渲染图片 只针对layer
    //[view.layer drawInContext:context];
    
    //以point为中心,而后size的一半向两边延伸  坐画笔  橡皮擦
    CGRect clearRect = CGRectMake(point.x - size.width/2.0, point.y - size.width/2.0, size.width, size.height);
    
    //渲染图片
    [view.layer renderInContext:context];
    //清除该区域
    CGContextClearRect(context, clearRect);
    //获得新图片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    UIGraphicsEndImageContext();
    //避免内存泄漏
    view.layer.contents = nil;
    
    return newImage;
}

移动路径的代码就不在这里贴了,demo中有的,截屏的核心代码也就不贴了,很简单的,能够查看demo

项目文件截图:

写在最后

其实上面的这些都是比较简单的,主要是经过CGContextRef及其中的一些方法进行实习的,真正让人东西的,仍是图形的处理,如添加马赛克、图片滤镜等,今天面试就问道了滤镜,当场就蒙了,后面我会努力研究下,但愿到时候也能有好的东西分享给你们。iOSQuart2D绘图之UIImage简单使用

代码地址以下:
http://www.demodashi.com/demo/11609.html

注:本文著做权归做者,由demo大师代发,拒绝转载,转载须要做者受权

相关文章
相关标签/搜索