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) {
 // 自定义处理
}

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

总结

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

(0)

相关推荐

  • IOS面试大全之常见算法

    这篇文字给大家分享了IOS面试中熟悉常见的算法,下面来一起看看吧. 1. 对以下一组数据进行降序排序(冒泡排序)."24,17,85,13,9,54,76,45,5,63" int main(int argc, char *argv[]) { int array[10] = {24, 17, 85, 13, 9, 54, 76, 45, 5, 63}; int num = sizeof(array)/sizeof(int); for(int i = 0; i < num-1; i

  • iOS常用加密算法介绍和代码实践

    iOS系统库中定义了软件开发中常用的加解密算法,接口为C语言形式.具体包括了以下几个大类: #include <CommonCrypto/CommonCryptor.h> //常用加解密算法 #include <CommonCrypto/CommonDigest.h> //摘要算法 #include <CommonCrypto/CommonHMAC.h> #include <CommonCrypto/CommonKeyDerivation.h> #inclu

  • iOS中MD5加密算法的介绍和使用

    前言 软件开发过程中,对数据进行加密是保证数据安全的重要手段,常见的加密有Base64加密和MD5加密.Base64加密是可逆的,MD5加密目前来说一般是不可逆的. MD5生成的是固定的128bit,即128个0和1的二进制位,而在实际应用开发中,通常是以16进制输出的,所以正好就是32位的16进制,说白了也就是32个16进制的数字. MD5主要特点是 不可逆,相同数据的MD5值肯定一样,不同数据的MD5值不一样(也不是绝对的,但基本是不能一样的). MD5算法还具有以下性质: 1.压缩性:任意

  • iOS中排列组合算法的使用小结

    前言 最近在项目中用到了排列组合计算,虽然比较简单,但是整个学习过程还是要记录下来的,以便以后可以吸取经验. 一般来说,排列组合就等于搜索. 注意点: 1.去重复:规定子集顺序必须升序: 2.候选数组的结果处理.必须深拷贝,否则最后的结果集里全是空的(加了一堆指针). 3.在写递归的时候(DFS:深度优先搜索),思路是先把以 1 开头的都找出来,再把 2 开头的都找出来 -- 所有在递归之前做过的事情,之后都要把它抹回来.递归做的事情能一句话描述清楚.递归就是不断地把规模变小,但是都做的一件事情

  • ios使用OC写算法之递归实现八皇后

    八皇后算法介绍 知道国际象棋的朋友们应该知道里面的皇后是最厉害的角色,她可以上下左右通吃,和中国象棋里面的车(ju 一声)一样,但是她比车更强大,她可以在斜线上也做到通吃,而我们的八皇后问题其实简单来说就是如何能够在 8×8 的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后 八皇后算法思路解析 既然任意一个皇后都无法吃掉其他的皇后,也就是说任两个皇后都不能处于同一条横行.纵行或斜线上,我们将棋盘当做一个二维数组,将皇后的位置标记为1 而其他位置默认都为0,这样我们就可以使用

  • iOS算法教程之分段截取常数示例

    前言 本文主要给大家介绍了关于iOS算法之分段截取常数的相关内容,分享出来供大家参考学习价值,下面话不多说了,来一起看看详细的介绍吧. 一.错位分段相加,递归合并的过程 #include intHamming_weight_3(intn ) { n = (n&0x55555555) + ((n>>1)&0x55555555); n = (n&0x33333333) + ((n>>2)&0x33333333); n = (n&0x0f0f0f0

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

    前言 项目有个需求:裁剪图片,针对头像,下面是要求: 大家可以看到这张图片的圆角已经去除,下面说说我在项目利用了两种方式实现此裁剪以及查看技术文档发现更高效裁剪方式,下面一一讲解:看下来大约需要15-20分钟. 在公共类中Util类中创建类方法 1.CGContext裁剪 //CGContext裁剪 + (UIImage *)CGContextClip:(UIImage *)img cornerRadius:(CGFloat)c; 实现该方法: // CGContext 裁剪 + (UIImag

  • iOS中实现动态区域裁剪图片功能实例

    前言 相信大家应该都有所体会,裁剪图片功能在很多上传图片的场景里都需要用到,一方面应用服务器可能对图片的尺寸大小有限制,因而希望上传的图片都是符合规定的,另一方面,用户可能希望只上传图片中的部分内容,突出图片中关键的信息.而为了满足用户多种多样的裁剪需求,就需要裁剪图片时能支持由用户动态地改变裁剪范围.裁剪尺寸等. 动态裁剪图片的基本过程大致可以分为以下几步 显示图片与裁剪区域 支持移动和缩放图片 支持手势改变裁剪区域 进行图片裁剪并获得裁剪后的图片 显示图片与裁剪区域 显示图片 在裁剪图片之前

  • 如何在iOS中高效的加载图片详解

    目录 前言 图片的渲染流程 DataBuffer SD源码分析 ImageBuffer 占用内存大小 Xcode测试 如何减少图像占用内存 向下采样 SD源码分析解码过程 选择正确的图片渲染格式 渲染格式 如何正确的选择渲染格式 减少后备存储器的使用 减少或者不使用 draw(rect:) 方法 如何在列表中加载图片 线程爆炸 总结 前言 在iOS开发中,图片(UIImage)是我们在开发中,占用手机内存比较大的对象,如果在运行过程中,内存占用过大,对电池寿命会造成影响,如果超过了内存占用的最大

  • Android编程实现调用系统图库与裁剪图片功能

    本文实例讲述了Android编程实现调用系统图库与裁剪图片功能.分享给大家供大家参考,具体如下: 在Android开发中,调用系统图库和裁剪照片是很常见的需求.相对于自己实现这种功能,直接调用系统具有诸多优点,如不用考虑屏幕适配,不用担心性能问题,等等.因此,对于一般的需求,建议直接调用系统的功能,简便高效! 首先上效果图:    一.只调用系统图库(不裁剪),返回用户选择的图片.(只支持单选,如需多选则需要自己实现,可参考Android编程实现仿QQ照片选择器(按相册分类显示,多选添加)源码.

  • iOS中5种图片缩略技术及性能的深入探讨

    前言 图像是每个应用程序不可缺少的一部分.调整图像大小是所有开发人员经常遇到的问题.iOS有5中图片缩略技术,但是我们应该在项目中选择哪种技术呢?尤其是面对高精度图片的缩略时,方式不当可能会出现OOM.现在我们开始一一去看看这5中图片缩略技术吧,完整代码在这里ImageResizing (本地下载). UIKit UIGraphicsBeginImageContextWithOptions & UIImage -drawInRect: 用于图像大小调整的最高级API可以在UIKit框架中找到.给

  • IOS 中CALayer绘制图片的实例详解

    IOS 中CALayer绘制图片的实例详解 CALayer渲染内容图层.与UIImageView相比,不具有事件响应功能,且UIImageView是管理内容. 注意事项:如何使用delegate对象执行代理方法进行绘制,切记需要将delegate设置为nil,否则会导致异常crash. CALayer绘制图片与线条效果图: 代码示例: CGPoint position = CGPointMake(160.0, 200.0); CGRect bounds = CGRectMake(0.0, 0.0

  • JavaWeb实现裁剪图片上传完整代码

    本文实例为大家分享了JavaWeb实现裁剪图片上传完整案例,供大家参考,具体内容如下 实现思路 •使用jcrop插件手机要裁剪图片的坐标  •将收集到的参数传递到后台,在后台使用java图形对象绘制图像进行裁剪 ◦后台处理流程: 1.将上传的图片按按照比例进行压缩后上传到文件服务器,并且将压缩后的图片保存在本地临时目录中. 2.将压缩后的图片回显到页面,使用jcrop进行裁剪,手机裁剪坐标(x,y,width,height) ■@paramx 目标切片起点坐标X ■@param y 目标切片起点

  • php实现高效获取图片尺寸的方法

    本文实例讲述了php实现高效获取图片尺寸的方法.分享给大家供大家参考.具体分析如下: php 获取图片尺寸的方法我们可以使用 getimagesize 获取图片尺寸,但是效率是很低的,首先需要获取整个的图片信息,然后再进行操作,下面的例子更科学算法更好,我们一起来看看吧. 方法可以用于快速获取图片尺寸信息,获取JPEG格式图片的尺寸信息,并且不需要下载读取整个图片,经测试这个函数不是对所有JPEG格式的图片都有效. 1.获取JPEG格式图片的尺寸信息,代码如下: 复制代码 代码如下: <?php

  • Android开发实现图片圆角的方法

    本文讲述了Android开发实现图片圆角的方法.分享给大家供大家参考,具体如下: Bitmap myCoolBitmap = ... ; // <-- Your bitmap you want rounded int w = myCoolBitmap.getWidth(), h = myCoolBitmap.getHeight(); Bitmap rounder = Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_8888); Canvas canvas =

  • iOS 如何高效的使用多线程

    一.多线程简述 线程是程序执行流的最小单元,一个线程包括:独有ID,程序计数器 (Program Counter),寄存器集合,堆栈.同一进程可以有多个线程,它们共享进程的全局变量和堆数据. 这里的 PC (Program Counter) 指向的是当前的指令地址,通过 PC 的更新来运行我们的程序,一个线程同一时刻只能执行一条指令.当然我们知道线程和进程都是虚拟的概念,实际上 PC 是 CPU 核心中的寄存器,它是实际存在的,所以也可以说一个 CPU 核心同一时刻只能执行一个线程. 不管是多处

随机推荐