UIImage剪裁、压缩、拉伸等处理

2018-08-02  本文已影响458人  cc_Jumper

1. 图片剪裁方法

直接调用如下系统现成的图片剪裁方法,封装成- (UIImage *)imageByCropToRect:(CGRect)rect接口

// CoreGraphics/CGImage.h
CG_EXTERN CGImageRef __nullable CGImageCreateWithImageInRect( CGImageRef cg_nullable image, CGRect rect) CG_AVAILABLE_STARTING(__MAC_10_4, __IPHONE_2_0);
// UIKit/UIImage.h
+ (UIImage *)imageWithCGImage:(CGImageRef)cgImage scale:(CGFloat)scale orientation:(UIImageOrientation)orientation NS_AVAILABLE_IOS(4_0);

/* 
  图片剪裁接口
 * 1.rect超过image.size则返回原图
 * 2.rect可以是size的任何一个子集,比如取中间半区域 CG
*/
- (UIImage *)imageByCropToRect:(CGRect)rect {
    rect.origin.x *= self.scale;
    rect.origin.y *= self.scale;
    rect.size.width *= self.scale;
    rect.size.height *= self.scale; // pt -> px (point -> pixel)
    if (rect.size.width <= 0 || rect.size.height <= 0) return nil;
    CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    UIImage *image = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);
    return image;
}
// 测试图片剪裁接口
- (void)test {
    UIImage *oriImg = [UIImage imageWithName:@"xxx.png"];
    CGFloat sw = oriImg.size.width;
    CGFloat sh = oriImg.size.height;
    // 剪裁中间区域,大小为原图片尺寸的一半
    CGRect halfCenterRect = CGRectMake(0.25 * sw, 0.25 * sh, 0.5 * sw, 0.5 * sh);
    // 剪裁右下脚区域,大小为原图片尺寸的四分之一
    CGRect rightBottomQuartRect = CGRectMake(0.75 * sw, 0.75 * sh, 0.25 * sw, 0.25 * sh);
    UIImage *image1 = [image imageByCropToRect:halfCenterRect];
    UIImage *image2 = [image imageByCropToRect:rightBottomQuartRect];
}

🤔思考:其实这里可以进一步将常用的剪裁区域封装成便捷函数,如下:

typedef NS_ENUM(NSInteger, UIImageCropStyle) {  // 裁切类型
    UIImageCropStyleLeft = 0,                   // 左半部分
    UIImageCropStyleRight,                      // 右半部分
    UIImageCropStyleCenter,                    // 中间部分
    UIImageCropStyleTop,                        // 上半部分
    UIImageCropStyleBottom,                    // 下半部分
};

- (UIImage *)imageByCropStyle:(UIImageCropStyle)style
{
    CGFloat cropX = 0, cropY = 0, cropWidth = self.size.width, cropHeight = self.size.height;

    if (style == UIImageCropStyleLeft)
    {
        cropWidth /= 2;
    }
    else if (style == UIImageCropStyleRight)
    {
        cropWidth /= 2;
        cropX = cropWidth;
    }
    else if (style == UIImageCropStyleCenter)
    {
        if (cropWidth > cropHeight)
        {
            cropX = (cropWidth - cropHeight)/2;
            cropWidth = cropHeight;
        }
        else if (cropWidth < cropHeight)
        {
            cropY = (cropHeight - cropWidth)/2;
            cropHeight = cropWidth;
        }
    }
    else if (style == UIImageCropStyleTop)
    {
        cropHeight /= 2;
    }
    else if (style == UIImageCropStyleBottom)
    {
        cropHeight /= 2;
        cropY = cropHeight;
    }

    return [self imageByCropToRect:CGRectMake(cropX, cropY, cropWidth, cropHeight)];
}

// 从长方形区域中剪裁中间最大正方形区域
- (UIImage *)imageByCropToSquare
{
    return [self imageByCropStyle:UIImageCropStyleCenter];
}

另一种裁剪思路: 使用UIImage的- (void)drawRect:(CGRect)rect方法设置rect区域超出设定的画布。如下图所示画布大小为蓝色区域,要使画布截取图片中间部分区域,则位置应满足如下图所示,(其中画布大小60 * 60,图片原始尺寸120 * 120)计算出左上角为{-30, -30},大小取图片大小,则drawRect = {-30, 30, 120, 120},按照下面代码即可截出中间区域。

image.png

2.图片拉伸/压缩

- (UIImage *)imageByResizeToSize:(CGSize)size {
    if (size.width <= 0 || size.height <= 0) return nil;
    UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
    [self drawInRect:CGRectMake(0, 0, size.width, size.height)];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

// 加强,可设置contentMode及剪裁
- (UIImage *)imageByResizeToSize:(CGSize)size contentMode:(UIViewContentMode)contentMode {
    if (size.width <= 0 || size.height <= 0) return nil;
    UIGraphicsBeginImageContextWithOptions(size, NO, self.scale);
    [self drawInRect:CGRectMake(0, 0, size.width, size.height) withContentMode:contentMode clipsToBounds:NO];
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return image;
}

- (void)drawInRect:(CGRect)rect withContentMode:(UIViewContentMode)contentMode clipsToBounds:(BOOL)clips{
    CGRect drawRect = YYCGRectFitWithContentMode(rect, self.size, contentMode);
    if (drawRect.size.width == 0 || drawRect.size.height == 0) return;
    if (clips) {
        CGContextRef context = UIGraphicsGetCurrentContext();
        if (context) {
            CGContextSaveGState(context);
            CGContextAddRect(context, rect);
            CGContextClip(context);
            [self drawInRect:drawRect];
            CGContextRestoreGState(context);
        }
    } else {
        [self drawInRect:drawRect];
    }
}
CGRect YYCGRectFitWithContentMode(CGRect rect, CGSize size, UIViewContentMode mode) {
    rect = CGRectStandardize(rect);
    size.width = size.width < 0 ? -size.width : size.width;
    size.height = size.height < 0 ? -size.height : size.height;
    CGPoint center = CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect));
    switch (mode) {
        case UIViewContentModeScaleAspectFit:
        case UIViewContentModeScaleAspectFill: {
            if (rect.size.width < 0.01 || rect.size.height < 0.01 ||
                size.width < 0.01 || size.height < 0.01) {
                rect.origin = center;
                rect.size = CGSizeZero;
            } else {
                CGFloat scale;
                if (mode == UIViewContentModeScaleAspectFit) {
                    if (size.width / size.height < rect.size.width / rect.size.height) {
                        scale = rect.size.height / size.height;
                    } else {
                        scale = rect.size.width / size.width;
                    }
                } else {
                    if (size.width / size.height < rect.size.width / rect.size.height) {
                        scale = rect.size.width / size.width;
                    } else {
                        scale = rect.size.height / size.height;
                    }
                }
                size.width *= scale;
                size.height *= scale;
                rect.size = size;
                rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5);
            }
        } break;
        case UIViewContentModeCenter: {
            rect.size = size;
            rect.origin = CGPointMake(center.x - size.width * 0.5, center.y - size.height * 0.5);
        } break;
        case UIViewContentModeTop: {
            rect.origin.x = center.x - size.width * 0.5;
            rect.size = size;
        } break;
        case UIViewContentModeBottom: {
            rect.origin.x = center.x - size.width * 0.5;
            rect.origin.y += rect.size.height - size.height;
            rect.size = size;
        } break;
        case UIViewContentModeLeft: {
            rect.origin.y = center.y - size.height * 0.5;
            rect.size = size;
        } break;
        case UIViewContentModeRight: {
            rect.origin.y = center.y - size.height * 0.5;
            rect.origin.x += rect.size.width - size.width;
            rect.size = size;
        } break;
        case UIViewContentModeTopLeft: {
            rect.size = size;
        } break;
        case UIViewContentModeTopRight: {
            rect.origin.x += rect.size.width - size.width;
            rect.size = size;
        } break;
        case UIViewContentModeBottomLeft: {
            rect.origin.y += rect.size.height - size.height;
            rect.size = size;
        } break;
        case UIViewContentModeBottomRight: {
            rect.origin.x += rect.size.width - size.width;
            rect.origin.y += rect.size.height - size.height;
            rect.size = size;
        } break;
        case UIViewContentModeScaleToFill:
        case UIViewContentModeRedraw:
        default: {
            rect = rect;
        }
    }
    return rect;
}

3.使用drawRect画出的图片模糊

如果是对@2x的图片直接使用UIGraphicsBeginImageContext(size)会使图像变得模糊,需要使用UIGraphicsBeginImageContextWithOptions(size, NO, [UIScreen mainScreen].scale)接口,下面是示例代码

UIView *targetView = xxx;
UIColor *bgColor = xxx;

// 想要在targetView上面覆盖一个image,有固定背景色,中间有固定大小logo图,
// 这样子绘制不会可以让image随着targetView变化,但是中间部分logo大小不变
CGFloat w = CGRectGetWidth(targetView.frame);
CGFloat h = w / ratio;
UIImage *image1 = [UIImage yyk_imageWithColor:bgColor size:CGSizeMake(w, h)];
UIImage *image2 = [UIImage imageNamed:@"logo"];
CGRect smallRect = CGRectMake((w - image2.size.width) / 2, (h - image2.size.height) / 2, image2.size.width, image2.size.height);
//        UIGraphicsBeginImageContext(image1.size); 此方法绘制的图片可能会变模糊,使用下面接口不会
UIGraphicsBeginImageContextWithOptions(image1.size, NO, [UIScreen mainScreen].scale);
[image1 drawInRect:CGRectMake(0, 0, w, h)];
[image2 drawInRect:smallRect];
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
上一篇下一篇

猜你喜欢

热点阅读