当前位置:  开发笔记 > IOS > 正文

裁剪UIImage

如何解决《裁剪UIImage》经验,为你挑选了11个好方法。

我有一些调整图像大小的代码,所以我可以获得图像中心的缩放块 - 我使用它来获取UIImage并返回图像的小方形表示,类似于在专辑视图中看到的照片应用程序.(我知道我可以使用a UIImageView并调整裁剪模式以获得相同的结果,但这些图像有时会显示在中UIWebViews).

我已经开始注意到这段代码中的一些崩溃,我有点难过.我有两种不同的理论,我想知道是否有基础.

理论1)我通过绘制到目标大小的屏幕外图像上下文来实现裁剪.由于我想要图像的中心部分,我设置CGRect传递给的参数drawInRect大于我的图像上下文边界的东西.我希望这是犹太洁食,但我是否试图汲取我不应该触及的其他记忆?

理论2)我在后台线程中做了所有这些.我知道UIKit的部分内容仅限于主线程.我假设/希望绘制到屏幕外的视图不是其中之一.我错了吗?

(哦,我多么想念NSImage's drawInRect:fromRect:operation:fraction:方法.)



1> HitScan..:

更新2014-05-28:我写这篇文章的时候iOS 3左右是热门的新东西,我确信现在还有更好的方法可以做到这一点,可能是内置的.正如许多人所提到的,这种方法不考虑轮换; 阅读一些额外的答案,并传播一些upvote爱,以保持对这个问题的回答对每个人都有帮助.

原始回复:

我打算将我的回复复制/粘贴到其他地方的同一问题:

没有简单的类方法可以做到这一点,但有一个功能可以用来获得所需的结果:CGImageCreateWithImageInRect(CGImageRef, CGRect)将帮助你.

这是一个使用它的简短示例:

CGImageRef imageRef = CGImageCreateWithImageInRect([largeImage CGImage], cropRect);
// or use the UIImage wherever you like
[UIImageView setImage:[UIImage imageWithCGImage:imageRef]]; 
CGImageRelease(imageRef);


我想指出,在视网膜显示器上,您需要使用此方法将cropRect宽度和高度加倍.只是我碰到的东西.
使它适用于任何方向使用:``[UIImage imageWithCGImage:imageRef scale:largeImage.scale orientation:largeImage.imageOrientation];``
当`imageOrientation`被设置为除了向上的任何东西时,`CGImage`相对于'UIImage`旋转,并且裁剪矩形将是错误的.您可以相应地旋转裁剪矩形,但是新创建的`UIImage`将具有`imageOrientation`,以便裁剪的`CGImage`将在其内部旋转.
-1.它在某些图像方向下不起作用.
您应该添加对Retina的支持!!

2> Arne..:

要在保持相同比例和方向的同时裁剪视网膜图像,请在UIImage类别(iOS 4.0及更高版本)中使用以下方法:

- (UIImage *)crop:(CGRect)rect {
    if (self.scale > 1.0f) {
        rect = CGRectMake(rect.origin.x * self.scale,
                          rect.origin.y * self.scale,
                          rect.size.width * self.scale,
                          rect.size.height * self.scale);
    }

    CGImageRef imageRef = CGImageCreateWithImageInRect(self.CGImage, rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}


你可以在这里杀死条件,并且总是通过`self.scale`来复制,不是吗?
@Dschee是的.请参见http://stackoverflow.com/questions/7800174/does-arc-work-with-core-graphics-objects
实际上,4次乘法可能比评估一个条件更快:)乘法可以在现代CPU上很好地优化,而条件只能预测.但你是对的,这是一个偏好的问题.由于可读性的提高,我更喜欢更短/更简单的代码.
你是对的,迈克尔,但我认为上面的解决方案在非视网膜设备上要快一点,因为它只进行一次检查,而不是四次乘法加一项任务.在视网膜设备上,它只是一个布尔检查超过必要,所以这是一个个人偏好或目标适合的问题.
在启用ARC的情况下使用`CGImageRelease(imageRef);`是否正确?

3> Vilém Kurz..:

您可以制作UIImage类别并在任何需要的地方使用它.根据HitScans的回应和评论吼叫它.

@implementation UIImage (Crop)

- (UIImage *)crop:(CGRect)rect {

    rect = CGRectMake(rect.origin.x*self.scale, 
                      rect.origin.y*self.scale, 
                      rect.size.width*self.scale, 
                      rect.size.height*self.scale);       

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], rect);
    UIImage *result = [UIImage imageWithCGImage:imageRef 
                                          scale:self.scale 
                                    orientation:self.imageOrientation]; 
    CGImageRelease(imageRef);
    return result;
}

@end

你可以这样使用它:

UIImage *imageToCrop = ;
CGRect cropRect = ;   

//for example
//CGRectMake(0, 40, 320, 100);

UIImage *croppedImage = [imageToCrop crop:cropRect];


不应该`[[UIScreen mainScreen] scale]`只是`self.scale`?图像的比例可能与屏幕的比例不同.

4> Sergii Rudch..:

这是我的UIImage裁剪实现,它遵循imageOrientation属性.所有方向都经过了彻底的测试.

inline double rad(double deg)
{
    return deg / 180.0 * M_PI;
}

UIImage* UIImageCrop(UIImage* img, CGRect rect)
{
    CGAffineTransform rectTransform;
    switch (img.imageOrientation)
    {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -img.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -img.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -img.size.width, -img.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, img.scale, img.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([img CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:img.scale orientation:img.imageOrientation];
    CGImageRelease(imageRef);
    return result;
}


警告`c99`中函数'rad'的隐式声明无效可以通过替换:rad(90),rad(-90),rad(-180) - > M_PI_2,-M_PI_2,-_M_PI来删除

5> Maxim Shoust..:

Swift 3版

func cropImage(imageToCrop:UIImage, toRect rect:CGRect) -> UIImage{

    let imageRef:CGImage = imageToCrop.cgImage!.cropping(to: rect)!
    let cropped:UIImage = UIImage(cgImage:imageRef)
    return cropped
}


let imageTop:UIImage  = UIImage(named:"one.jpg")! // add validation

在此输入图像描述

借助这个桥梁功能CGRectMake- > CGRect(对此答案的回答@rob mayoff):

 func CGRectMake(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) -> CGRect {
    return CGRect(x: x, y: y, width: width, height: height)
}

用法是:

if var image:UIImage  = UIImage(named:"one.jpg"){
   let  croppedImage = cropImage(imageToCrop: image, toRect: CGRectMake(
        image.size.width/4,
        0,
        image.size.width/2,
        image.size.height)
    )
}

输出:

在此输入图像描述


如果'UIImage.CGImage`返回`nil`,你需要'保护'第一行.当'let`完美地工作时,为什么要使用`var`.

6> colinta..:

抬头:所有这些答案都假设一个CGImage背景图像对象.

image.CGImage如果UIImage由a支持,则返回nil,如果CIImage使用a创建此图像,则会返回nil CIFilter.

在这种情况下,您可能必须在新的上下文中绘制图像,并返回该图像().

UIImage* crop(UIImage *image, rect) {
    UIGraphicsBeginImageContextWithOptions(rect.size, false, [image scale]);
    [image drawAtPoint:CGPointMake(-rect.origin.x, -rect.origin.y)];
    cropped_image = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return cropped_image;
}


这正是我所需要的,因此在所有场景中都能比其他任何解决方案都更好!
第一行中的image.size应该是rect.size UIGraphicsBeginImageContextWithOptions(rect.size,false,image.scale);
谢谢布雷特,肯定就在那里.我更新了代码以包含此修复程序.
因为我们正在裁剪; 换句话说,'rect'arg说"我希望新图像的左上角开始,比如说,[10,10]".为此,我们绘制图像,使[10,10]处于新图像的原点.

7> awolf..:

这里没有任何答案100%正确处理所有比例和旋转问题.这是迄今为止所有内容的综合,是iOS7/8的最新版本.它意味着作为UIImage类别中的方法包含在内.

- (UIImage *)croppedImageInRect:(CGRect)rect
{
    double (^rad)(double) = ^(double deg) {
        return deg / 180.0 * M_PI;
    };

    CGAffineTransform rectTransform;
    switch (self.imageOrientation) {
        case UIImageOrientationLeft:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(90)), 0, -self.size.height);
            break;
        case UIImageOrientationRight:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-90)), -self.size.width, 0);
            break;
        case UIImageOrientationDown:
            rectTransform = CGAffineTransformTranslate(CGAffineTransformMakeRotation(rad(-180)), -self.size.width, -self.size.height);
            break;
        default:
            rectTransform = CGAffineTransformIdentity;
    };
    rectTransform = CGAffineTransformScale(rectTransform, self.scale, self.scale);

    CGImageRef imageRef = CGImageCreateWithImageInRect([self CGImage], CGRectApplyAffineTransform(rect, rectTransform));
    UIImage *result = [UIImage imageWithCGImage:imageRef scale:self.scale orientation:self.imageOrientation];
    CGImageRelease(imageRef);

    return result;
}



8> Mark Leonard..:

Swift版本awolf的答案,对我有用:

public extension UIImage {
    func croppedImage(inRect rect: CGRect) -> UIImage {
        let rad: (Double) -> CGFloat = { deg in
            return CGFloat(deg / 180.0 * .pi)
        }
        var rectTransform: CGAffineTransform
        switch imageOrientation {
        case .left:
            let rotation = CGAffineTransform(rotationAngle: rad(90))
            rectTransform = rotation.translatedBy(x: 0, y: -size.height)
        case .right:
            let rotation = CGAffineTransform(rotationAngle: rad(-90))
            rectTransform = rotation.translatedBy(x: -size.width, y: 0)
        case .down:
            let rotation = CGAffineTransform(rotationAngle: rad(-180))
            rectTransform = rotation.translatedBy(x: -size.width, y: -size.height)
        default:
            rectTransform = .identity
        }
        rectTransform = rectTransform.scaledBy(x: scale, y: scale)
        let transformedRect = rect.applying(rectTransform)
        let imageRef = cgImage!.cropping(to: transformedRect)!
        let result = UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
        return result
    }
}



9> Jordan..:
CGSize size = [originalImage size];
int padding = 20;
int pictureSize = 300;
int startCroppingPosition = 100;
if (size.height > size.width) {
    pictureSize = size.width - (2.0 * padding);
    startCroppingPosition = (size.height - pictureSize) / 2.0; 
} else {
    pictureSize = size.height - (2.0 * padding);
    startCroppingPosition = (size.width - pictureSize) / 2.0;
}
// WTF: Don't forget that the CGImageCreateWithImageInRect believes that 
// the image is 180 rotated, so x and y are inverted, same for height and width.
CGRect cropRect = CGRectMake(startCroppingPosition, padding, pictureSize, pictureSize);
CGImageRef imageRef = CGImageCreateWithImageInRect([originalImage CGImage], cropRect);
UIImage *newImage = [UIImage imageWithCGImage:imageRef scale:1.0 orientation:originalImage.imageOrientation];
[m_photoView setImage:newImage];
CGImageRelease(imageRef);

我见过的大多数响应只处理(0,0)(x,y)的位置.好吧,这是一个案例,但我希望我的裁剪操作集中.我花了一段时间才弄明白的是WTF评论之后的界限.

让我们以纵向拍摄的图像为例:

    原始图像高度高于其宽度(Woo,到目前为止并不令人惊讶!)

    CGImageCreateWithImageInRect方法在其自己的世界中想象的图像不是真正的肖像,而是景观(这也是为什么如果你不在imageWithCGImage构造函数中使用orientation参数,它将显示为180旋转).

    所以,你应该想象它是一个风景,(0,0)位置是图像的右上角.

希望它有意义!如果没有,请尝试使用不同的值,在选择cropRect的正确x,y,宽度和高度时,您会看到逻辑被反转.



10> Epic Byte..:

快速扩展

extension UIImage {
    func crop(var rect: CGRect) -> UIImage {
        rect.origin.x*=self.scale
        rect.origin.y*=self.scale
        rect.size.width*=self.scale
        rect.size.height*=self.scale

        let imageRef = CGImageCreateWithImageInRect(self.CGImage, rect)
        let image = UIImage(CGImage: imageRef, scale: self.scale, orientation: self.imageOrientation)!
        return image
    }
}



11> neoneye..:
swift3
extension UIImage {
    func crop(rect: CGRect) -> UIImage? {
        var scaledRect = rect
        scaledRect.origin.x *= scale
        scaledRect.origin.y *= scale
        scaledRect.size.width *= scale
        scaledRect.size.height *= scale
        guard let imageRef: CGImage = cgImage?.cropping(to: scaledRect) else {
            return nil
        }
        return UIImage(cgImage: imageRef, scale: scale, orientation: imageOrientation)
    }
}

推荐阅读
农大军乐团_697
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有