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

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

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) {
// ⾃定义处理
}
上⾯是三种⽅式,可以解决图⽚裁剪的需求,
总结
以上就是这篇⽂章的全部内容了,希望本⽂的内容对⼤家的学习或者⼯作具有⼀定的参考学习价值,如果有疑问⼤家可以留⾔交流,谢谢⼤家对的⽀持。

相关文档
最新文档