服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - IOS - iOS实现高效裁剪图片圆角算法教程

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

2021-05-03 16:24国孩 IOS

经常看到各种高效裁剪圆角的文章,正好之前做过一点数字图像处理,所以写个裁剪圆角的算法,下面这篇文章主要给大家介绍了关于iOS实现高效裁剪图片圆角算法的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以

前言

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

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

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

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

1.cgcontext裁剪

?
1
2
//cgcontext裁剪
+ (uiimage *)cgcontextclip:(uiimage *)img cornerradius:(cgfloat)c;

实现该方法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 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;
}

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

?
1
[util cgcontextclip:image cornerradius:radius];

2.uibezierpath 裁剪

在util.h类中声明

?
1
2
//uibezierpath 裁剪
+ (uiimage *)uibezierpathclip:(uiimage *)img cornerradius:(cgfloat)c;

在util.m实现方法

?
1
2
3
4
5
6
7
8
9
10
11
12
13
//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 即可

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

遍历所有像素,判断每个像素在不在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中声明:

?
1
+ (uiimage *)dealimage:(uiimage *)img cornerradius:(cgfloat)c

在util.m中实现:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
+ (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) {
 // 自定义处理
}

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

总结

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

原文链接:https://www.cnblogs.com/guohai-stronger/p/9189461.html

延伸 · 阅读

精彩推荐
  • IOS解析iOS开发中的FirstResponder第一响应对象

    解析iOS开发中的FirstResponder第一响应对象

    这篇文章主要介绍了解析iOS开发中的FirstResponder第一响应对象,包括View的FirstResponder的释放问题,需要的朋友可以参考下...

    一片枫叶4662020-12-25
  • IOSiOS中tableview 两级cell的展开与收回的示例代码

    iOS中tableview 两级cell的展开与收回的示例代码

    本篇文章主要介绍了iOS中tableview 两级cell的展开与收回的示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...

    J_Kang3862021-04-22
  • IOSIOS开发之字典转字符串的实例详解

    IOS开发之字典转字符串的实例详解

    这篇文章主要介绍了IOS开发之字典转字符串的实例详解的相关资料,希望通过本文能帮助到大家,让大家掌握这样的方法,需要的朋友可以参考下...

    苦练内功5832021-04-01
  • IOS关于iOS自适应cell行高的那些事儿

    关于iOS自适应cell行高的那些事儿

    这篇文章主要给大家介绍了关于iOS自适应cell行高的那些事儿,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的...

    daisy6092021-05-17
  • IOSIOS 屏幕适配方案实现缩放window的示例代码

    IOS 屏幕适配方案实现缩放window的示例代码

    这篇文章主要介绍了IOS 屏幕适配方案实现缩放window的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要...

    xiari5772021-06-01
  • IOSiOS布局渲染之UIView方法的调用时机详解

    iOS布局渲染之UIView方法的调用时机详解

    在你刚开始开发 iOS 应用时,最难避免或者是调试的就是和布局相关的问题,下面这篇文章主要给大家介绍了关于iOS布局渲染之UIView方法调用时机的相关资料...

    windtersharp7642021-05-04
  • IOSiOS通过逆向理解Block的内存模型

    iOS通过逆向理解Block的内存模型

    自从对 iOS 的逆向初窥门径后,我也经常通过它来分析一些比较大的应用,参考一下这些应用中某些功能的实现。这个探索的过程乐趣多多,不仅能满足自...

    Swiftyper12832021-03-03
  • IOSiOS 雷达效果实例详解

    iOS 雷达效果实例详解

    这篇文章主要介绍了iOS 雷达效果实例详解的相关资料,需要的朋友可以参考下...

    SimpleWorld11022021-01-28