项目有个需求: 裁剪图片, 针对头像, 下面是要求:
大家可以看到这张图片的圆角已经去除, 下面说说我在项目利用了两种方式实现此裁剪以及查看技术文档发现更高效裁剪方式, 下面一一讲解: 看下来大约需要 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) {
- // 自定义处理
- }
上面是三种方式, 可以解决图片裁剪的需求,
来源: https://www.cnblogs.com/guohai-stronger/p/9189461.html