iOS实现高效裁剪图片圆角算法教程

 更新时间:2018年06月16日 13:58:57   作者:国孩  
经常看到各种高效裁剪圆角的文章,正好之前做过一点数字图像处理,所以写个裁剪圆角的算法,下面这篇文章主要给大家介绍了关于iOS实现高效裁剪图片圆角算法的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考下

前言

项目有个需求:裁剪图片,针对头像,下面是要求:

 

大家可以看到这张图片的圆角已经去除,下面说说我在项目利用了两种方式实现此裁剪以及查看技术文档发现更高效裁剪方式,下面一一讲解:看下来大约需要15-20分钟。

在公共类中Util类中创建类方法

1.CGContext裁剪

//CGContext裁剪
+ (UIImage *)CGContextClip:(UIImage *)img cornerRadius:(CGFloat)c;

实现该方法:

// CGContext 裁剪
+ (UIImage *)CGContextClip:(UIImage *)img cornerRadius:(CGFloat)c{
 int w = img.size.width * img.scale;
 int h = img.size.height * img.scale;
 UIGraphicsBeginImageContextWithOptions(CGSizeMake(w, h), false, 1.0);
 CGContextRef context = UIGraphicsGetCurrentContext();
 CGContextMoveToPoint(context, 0, c);
 CGContextAddArcToPoint(context, 0, 0, c, 0, c);
 CGContextAddLineToPoint(context, w-c, 0);
 CGContextAddArcToPoint(context, w, 0, w, c, c);
 CGContextAddLineToPoint(context, w, h-c);
 CGContextAddArcToPoint(context, w, h, w-c, h, c);
 CGContextAddLineToPoint(context, c, h);
 CGContextAddArcToPoint(context, 0, h, 0, h-c, c);
 CGContextAddLineToPoint(context, 0, c);
 CGContextClosePath(context);
 
 // 先裁剪 context,再画图,就会在裁剪后的 path 中画
 CGContextClip(context);
 [img drawInRect:CGRectMake(0, 0, w, h)]; // 画图
 CGContextDrawPath(context, kCGPathFill);
 UIImage *ret = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
 
 return ret;
}

在该需要的地方调用如下:

[Util CGContextClip:image cornerRadius:radius];

2.UIBezierPath 裁剪

在Util.h类中声明

//UIBezierPath 裁剪
+ (UIImage *)UIBezierPathClip:(UIImage *)img cornerRadius:(CGFloat)c;

在Util.m实现方法

//UIBezierPath 裁剪
+ (UIImage *)UIBezierPathClip:(UIImage *)img cornerRadius:(CGFloat)c{
 int w = img.size.width * img.scale;
 int h = img.size.height * img.scale;
 CGRect rect = CGRectMake(0, 0, w, h);
 UIGraphicsBeginImageContextWithOptions(CGSizeMake(w, h), false, 1.0);
 [[UIBezierPath bezierPathWithRoundedRect:rect cornerRadius:c] addClip];
 [img drawInRect:rect];
 
 UIImage *ret = UIGraphicsGetImageFromCurrentImageContext();
 UIGraphicsEndImageContext();
 return ret;
}

3.空域处理的办法,写个裁剪圆角的算法

对于图像上的一个点(x, y),判断其在不在圆角矩形内,在的话 alpha 是原值,不在的话 alpha 设为 0 即可

 

遍历所有像素,判断每个像素在不在4个圆的圆内就行了,4个角,每个角有一个四分之一的圆。

一个优化就是,我不需要遍历全部的像素就能裁出圆角,只需要考虑类似左下角三角形的区域就行了,左下,左上,右上,右下,一共4个三角形区域(另外3个图中没画出),for循环的时候,就循环这个4个三角形区域就行了。

所以对于一幅 w * h 的图像,设圆角大小为 n,n <= min(w, h) / 2,其复杂度为 O(n) = 2(n^2),最坏的情况计算量也不会超过 wh / 2。

对于一个像素点(x, y),判断其在不在圆内的公式:
如果  (x-cx)^2 + (y-cy)^2 <= r^2  就表示点 (x, y) 在圆内,反之不在。通过测试:此算法效率可以提高几倍之上(时间)

在Util.h中声明:

+ (UIImage *)dealImage:(UIImage *)img cornerRadius:(CGFloat)c

在Util.m中实现:

+ (UIImage *)dealImage:(UIImage *)img cornerRadius:(CGFloat)c {
 // 1.CGDataProviderRef 把 CGImage 转 二进制流
 CGDataProviderRef provider = CGImageGetDataProvider(img.CGImage);
 void *imgData = (void *)CFDataGetBytePtr(CGDataProviderCopyData(provider));
 int width = img.size.width * img.scale;
 int height = img.size.height * img.scale;
 
 // 2.处理 imgData
// dealImage(imgData, width, height);
 cornerImage(imgData, width, height, c);
 
 // 3.CGDataProviderRef 把 二进制流 转 CGImage
 CGDataProviderRef pv = CGDataProviderCreateWithData(NULL, imgData, width * height * 4, releaseData);
 CGImageRef content = CGImageCreate(width , height, 8, 32, 4 * width, CGColorSpaceCreateDeviceRGB(), kCGBitmapByteOrder32Big | kCGImageAlphaPremultipliedLast, pv, NULL, true, kCGRenderingIntentDefault);
 UIImage *result = [UIImage imageWithCGImage:content];
 CGDataProviderRelease(pv); // 释放空间
 CGImageRelease(content);
 
 return result;
}

void releaseData(void *info, const void *data, size_t size) {
 free((void *)data);
}

// 在 img 上处理图片, 测试用
void dealImage(UInt32 *img, int w, int h) {
 int num = w * h;
 UInt32 *cur = img;
 for (int i=0; i<num; i++, cur++) {
 UInt8 *p = (UInt8 *)cur;
 // RGBA 排列
 // f(x) = 255 - g(x) 求负片
 p[0] = 255 - p[0];
 p[1] = 255 - p[1];
 p[2] = 255 - p[2];
 p[3] = 255;
 }
}

// 裁剪圆角
void cornerImage(UInt32 *const img, int w, int h, CGFloat cornerRadius) {
 CGFloat c = cornerRadius;
 CGFloat min = w > h ? h : w;
 
 if (c < 0) { c = 0; }
 if (c > min * 0.5) { c = min * 0.5; }
 
 // 左上 y:[0, c), x:[x, c-y)
 for (int y=0; y<c; y++) {
 for (int x=0; x<c-y; x++) {
  UInt32 *p = img + y * w + x; // p 32位指针,RGBA排列,各8位
  if (isCircle(c, c, c, x, y) == false) {
  *p = 0;
  }
 }
 }
 // 右上 y:[0, c), x:[w-c+y, w)
 int tmp = w-c;
 for (int y=0; y<c; y++) {
 for (int x=tmp+y; x<w; x++) {
  UInt32 *p = img + y * w + x;
  if (isCircle(w-c, c, c, x, y) == false) {
  *p = 0;
  }
 }
 }
 // 左下 y:[h-c, h), x:[0, y-h+c)
 tmp = h-c;
 for (int y=h-c; y<h; y++) {
 for (int x=0; x<y-tmp; x++) {
  UInt32 *p = img + y * w + x;
  if (isCircle(c, h-c, c, x, y) == false) {
  *p = 0;
  }
 }
 }
 // 右下 y~[h-c, h), x~[w-c+h-y, w)
 tmp = w-c+h;
 for (int y=h-c; y<h; y++) {
 for (int x=tmp-y; x<w; x++) {
  UInt32 *p = img + y * w + x;
  if (isCircle(w-c, h-c, c, x, y) == false) {
  *p = 0;
  }
 }
 }
}

// 判断点 (px, py) 在不在圆心 (cx, cy) 半径 r 的圆内
static inline bool isCircle(float cx, float cy, float r, float px, float py) {
 if ((px-cx) * (px-cx) + (py-cy) * (py-cy) > r * r) {
 return false;
 }
 return true;
}

// 其他图像效果可以自己写函数,然后在 dealImage: 中调用 otherImage 即可
void otherImage(UInt32 *const img, int w, int h) {
 // 自定义处理
}

上面是三种方式,可以解决图片裁剪的需求,

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

相关文章

  • MAC中显示隐藏文件和不显示隐藏文件的方法(超简单)

    MAC中显示隐藏文件和不显示隐藏文件的方法(超简单)

    下面小编就为大家分享一篇MAC中显示隐藏文件和不显示隐藏文件的方法(超简单),具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • iOS阅读器与直播的控件重叠滑动交互详解

    iOS阅读器与直播的控件重叠滑动交互详解

    这篇文章主要介绍了iOS阅读器与直播的控件发生重叠时滑动交互受到影响的问题,开发中某个控件出现在其它控件上方是和你常见的情况,但是如果影响到正常的交互这就会成为大问题,下面我们来了解一下
    2022-08-08
  • Stack View自定义间隙减少约束的数量

    Stack View自定义间隙减少约束的数量

    这篇文章主要为大家介绍了Stack View自定义间隙减少约束的数量示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • IOS多线程实现多图片下载(二)

    IOS多线程实现多图片下载(二)

    这篇文章主要介绍了IOS多线程实现多图片下载(二)的相关资料,需要的朋友可以参考下
    2016-03-03
  • IOS检测指定路径的文件是否存在

    IOS检测指定路径的文件是否存在

    本文给大家分享的是在IOS开发中检测指定文件是否存在的方法,给大家汇总了4种,十分实用,小伙伴们根据自己的需求自由选择吧。
    2015-05-05
  • iOS中将个别页面强制横屏其他页面竖屏

    iOS中将个别页面强制横屏其他页面竖屏

    这篇文章主要介绍了iOS中将个别页面强制横屏其他页面竖屏的实现思路,需要的朋友参考下吧
    2016-12-12
  • 仅需几行代码实现方便易用的状态栏指示器

    仅需几行代码实现方便易用的状态栏指示器

    本文通过仅仅数行代码实现了非常方便易用的状态栏指示器,比如微博项目的微博数提醒框,需要的朋友可以参考下
    2015-08-08
  • iOS实现带有缩放效果的自动轮播图

    iOS实现带有缩放效果的自动轮播图

    这篇文章主要为大家详细介绍了iOS带有缩放效果的自动轮播图,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-05-05
  • iOS 指纹解锁验证TouchID功能

    iOS 指纹解锁验证TouchID功能

    这篇文章主要介绍了iOS 指纹解锁验证TouchID功能,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-03-03
  • 详解IOS WebRTC的实现原理

    详解IOS WebRTC的实现原理

    WebRTC,源自网页实时通信(Web Real-Time Communication)缩写,它是支持网页浏览器进行实时语音对话或视频对话的技术。它为我们提供了视频会议的核心技术,包括音视频的采集、编解码、网络传输、显示等功能,并且还支持跨平台:windows,linux,mac,android,ios。
    2021-06-06

最新评论