调整大小和裁剪图像居中

时间:2010-10-17 20:42:12

标签: iphone objective-c cocoa-touch ios

所以目前我正在尝试裁剪并调整图片大小以使其适合特定尺寸而不会失去比例。

一张小图片,以显示我的意思:

alt text

我和vocaro's categories玩了一下但是它们不能与png一起使用并且与gif有问题。此外,图像也不会被裁剪。

是否有人建议如何调整最佳方式或者可能有现有库/类别/其他内容的链接?

感谢所有提示!

p.s。:ios实现了“选择一个摘录”,以便我有正确的比例,只需要缩放它吗?!

3 个答案:

答案 0 :(得分:7)

此方法可以执行您想要的操作,并且是易用的UIImage类别。我调整大小然后裁剪,你可以很容易地切换代码,如果你想要裁剪然后调整大小。函数中的边界检查纯粹是说明性的。您可能想要做一些不同的事情,例如将crop crop相对于outputImage维度居中,但这应该让您足够接近以进行您需要的任何其他更改。

@implementation UIImage( resizeAndCropExample )

- (UIImage *) resizeToSize:(CGSize) newSize thenCropWithRect:(CGRect) cropRect {
    CGContextRef                context;
    CGImageRef                  imageRef;
    CGSize                      inputSize;
    UIImage                     *outputImage = nil;
    CGFloat                     scaleFactor, width;

    // resize, maintaining aspect ratio:

    inputSize = self.size;
    scaleFactor = newSize.height / inputSize.height;
    width = roundf( inputSize.width * scaleFactor );

    if ( width > newSize.width ) {
        scaleFactor = newSize.width / inputSize.width;
        newSize.height = roundf( inputSize.height * scaleFactor );
    } else {
        newSize.width = width;
    }

    UIGraphicsBeginImageContext( newSize );

    context = UIGraphicsGetCurrentContext();

    // added 2016.07.29, flip image vertically before drawing:
    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0, newSize.height);
    CGContextScaleCTM(context, 1, -1);
    CGContextDrawImage(context, CGRectMake(0, 0, newSize.width, newSize.height, self.CGImage);

//  // alternate way to draw
//  [self drawInRect: CGRectMake( 0, 0, newSize.width, newSize.height )];

    CGContextRestoreGState(context);

    outputImage = UIGraphicsGetImageFromCurrentImageContext();

    UIGraphicsEndImageContext();

    inputSize = newSize;

    // constrain crop rect to legitimate bounds
    if ( cropRect.origin.x >= inputSize.width || cropRect.origin.y >= inputSize.height ) return outputImage;
    if ( cropRect.origin.x + cropRect.size.width >= inputSize.width ) cropRect.size.width = inputSize.width - cropRect.origin.x;
    if ( cropRect.origin.y + cropRect.size.height >= inputSize.height ) cropRect.size.height = inputSize.height - cropRect.origin.y;

    // crop
    if ( ( imageRef = CGImageCreateWithImageInRect( outputImage.CGImage, cropRect ) ) ) {
        outputImage = [[[UIImage alloc] initWithCGImage: imageRef] autorelease];
        CGImageRelease( imageRef );
    }

    return outputImage;
}

@end

答案 1 :(得分:2)

我在我的一个应用程序中遇到了同样的问题并开发了这段代码:

+ (UIImage*)resizeImage:(UIImage*)image toFitInSize:(CGSize)toSize
{
    UIImage *result = image;
    CGSize sourceSize = image.size;
    CGSize targetSize = toSize;

    BOOL needsRedraw = NO;

    // Check if width of source image is greater than width of target image
    // Calculate the percentage of change in width required and update it in toSize accordingly.

    if (sourceSize.width > toSize.width) {

        CGFloat ratioChange = (sourceSize.width - toSize.width) * 100 / sourceSize.width;

        toSize.height = sourceSize.height - (sourceSize.height * ratioChange / 100);

        needsRedraw = YES;
    }

    // Now we need to make sure that if we chnage the height of image in same proportion
    // Calculate the percentage of change in width required and update it in target size variable.
    // Also we need to again change the height of the target image in the same proportion which we
    /// have calculated for the change.

    if (toSize.height < targetSize.height) {

        CGFloat ratioChange = (targetSize.height - toSize.height) * 100 / targetSize.height;

        toSize.height = targetSize.height;
        toSize.width = toSize.width + (toSize.width * ratioChange / 100);

        needsRedraw = YES;
    }

    // To redraw the image

    if (needsRedraw) {
        UIGraphicsBeginImageContext(toSize);
        [image drawInRect:CGRectMake(0.0, 0.0, toSize.width, toSize.height)];
        result = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
    }

    // Return the result

    return result;
}

您可以根据需要进行修改。

答案 2 :(得分:0)

对图库中的预览图像执行相同的任务。对于固定的裁切区域(用于SwiftUI的图像和用于Kotiln的Canvas Rect),我要裁切图像的中心内容-通过最大程度地裁切图像侧面。 (请参见下面的说明)

这里的解决方案

快速

func getImageCropped(srcImage : UIImage, sizeToCrop : CGSize) -> UIImage{

                let ratioImage = Double(srcImage.cgImage!.width )  / Double(srcImage.cgImage!.height )
                let ratioCrop  = Double(sizeToCrop.width)          / Double(sizeToCrop.height)

                let cropRect: CGRect = {
                    if(ratioCrop > 1.0){
                        // crop LAND  -> fit image HORIZONTALLY

                        let widthRatio = CGFloat(srcImage.cgImage!.width) / CGFloat(sizeToCrop.width)

                        var cropWidth  = Int(sizeToCrop.width  * widthRatio)
                        var cropHeight = Int(sizeToCrop.height * widthRatio)
                        var cropX      = 0
                        var cropY      = srcImage.cgImage!.height / 2 - cropHeight / 2

                        // [L1] [L2]        : OK

                        if(ratioImage > 1.0) {
                            // image LAND

                             // [L3]        : OK

                            if(cropHeight > srcImage.cgImage!.height) {

                                // [L4]     : Crop-Area exceeds Image-Area > change crop orientation to PORTrait

                                let heightRatio = CGFloat(srcImage.cgImage!.height) / CGFloat(sizeToCrop.height)

                                cropWidth  = Int(sizeToCrop.width  * heightRatio)
                                cropHeight = Int(sizeToCrop.height * heightRatio)
                                cropX      = srcImage.cgImage!.width / 2 - cropWidth / 2
                                cropY      = 0
                            }
                        }

                        return CGRect(x: cropX, y: cropY, width: cropWidth, height: cropHeight)
                    }
                    else if(ratioCrop < 1.0){
                        // crop PORT  -> fit image VERTICALLY

                        let heightRatio = CGFloat(srcImage.cgImage!.height) / CGFloat(sizeToCrop.height)

                        var cropWidth  = Int(sizeToCrop.width  * heightRatio)
                        var cropHeight = Int(sizeToCrop.height * heightRatio)
                        var cropX      = srcImage.cgImage!.width / 2 - cropWidth / 2
                        var cropY      = 0

                        // [P1] [P2]        : OK

                        if(ratioImage < 1.0) {
        //                  // image  PORT

                            // [P3]        : OK

                            if(cropWidth > srcImage.cgImage!.width) {

                                // [L4]     : Crop-Area exceeds Image-Area > change crop orientation to LANDscape

                                let widthRatio = CGFloat(srcImage.cgImage!.width) / CGFloat(sizeToCrop.width)

                                cropWidth  = Int(sizeToCrop.width  * widthRatio)
                                cropHeight = Int(sizeToCrop.height  * widthRatio)
                                cropX      = 0
                                cropY      = srcImage.cgImage!.height / 2 - cropHeight / 2
                            }
                        }
                        return CGRect(x: cropX, y: cropY, width: cropWidth, height: cropHeight)
                    }
                    else {
                        // CROP CENTER SQUARE

                        var fitSide = 0

                        var cropX = 0
                        var cropY = 0

                        if (ratioImage > 1.0){
                            // crop LAND  -> fit image HORIZONTALLY     !!!!!!
                            fitSide = srcImage.cgImage!.height
                            cropX = srcImage.cgImage!.width / 2 - fitSide / 2
                        }
                        else if (ratioImage < 1.0){
                            // crop PORT  -> fit image VERTICALLY
                            fitSide = srcImage.cgImage!.width
                            cropY = srcImage.cgImage!.height / 2 - fitSide / 2
                        }
                        else{
                            // ImageAre and GropArea are square both
                            fitSide = srcImage.cgImage!.width
                        }

                        return CGRect(x: cropX, y: cropY, width: fitSide, height: fitSide)
                    }

                }()

                let imageRef = srcImage.cgImage!.cropping(to: cropRect)

                let cropped : UIImage = UIImage(cgImage: imageRef!, scale: 0, orientation: srcImage.imageOrientation)

                return cropped

    }

科特林

data class RectCrop(val x: Int, val y: Int, val width: Int, val height: Int)    

fun getImageCroppedShort(srcBitmap: Bitmap, sizeToCrop: Size):Bitmap {



            val ratioImage = srcBitmap.width.toFloat()  / srcBitmap.height.toFloat()
            val ratioCrop  = sizeToCrop.width.toFloat() / sizeToCrop.height.toFloat()


    //    1. choose fit size
            val cropRect: RectCrop =

                if(ratioCrop > 1.0){
    //              crop  LAND

                    val widthRatio = srcBitmap.width.toFloat() / sizeToCrop.width.toFloat()

                    var cropWidth = (sizeToCrop.width  * widthRatio).toInt()
                    var cropHeight= (sizeToCrop.height * widthRatio).toInt()
                    var cropX          = 0
                    var cropY     = srcBitmap.height / 2 - cropHeight / 2

                    if(ratioImage > 1.0) {
    //                  image LAND

                        if(cropHeight > srcBitmap.height) {

                            val heightRatio = srcBitmap.height.toFloat() / sizeToCrop.height.toFloat()

                            cropWidth  = (sizeToCrop.width  * heightRatio).toInt()
                            cropHeight = (sizeToCrop.height * heightRatio).toInt()
                            cropX      = srcBitmap.width / 2 - cropWidth / 2
                            cropY      = 0
                        }
                    }

                    RectCrop(cropX, cropY, cropWidth, cropHeight)
                }
                else if(ratioCrop < 1.0){
    //              crop  PORT

                    val heightRatio = srcBitmap.height.toFloat() / sizeToCrop.height.toFloat()

                    var cropWidth = (sizeToCrop.width  * heightRatio).toInt()
                    var cropHeight= (sizeToCrop.height * heightRatio).toInt()
                    var cropX     = srcBitmap.width / 2 - cropWidth / 2
                    var cropY          = 0

                    if(ratioImage < 1.0) {
    //                  image  PORT

                        if(cropWidth > srcBitmap.width) {

                            val widthRatio = srcBitmap.width.toFloat() / sizeToCrop.width.toFloat()

                            cropWidth  = (sizeToCrop.width  * widthRatio).toInt()
                            cropHeight = (sizeToCrop.height * widthRatio).toInt()
                            cropX      = 0
                            cropY      = srcBitmap.height / 2 - cropHeight / 2
                        }
                    }

                    RectCrop(cropX, cropY, cropWidth, cropHeight)
                }
                else {
                    // CROP CENTER SQUARE

                    var fitSide = 0

                    var cropX = 0
                    var cropY = 0

                    if (ratioImage > 1.0){
                        fitSide = srcBitmap.height
                        cropX = srcBitmap.width/ 2 - fitSide / 2
                    }
                    else if (ratioImage < 1.0){
                        fitSide = srcBitmap.width
                        cropY = srcBitmap.height / 2 - fitSide / 2
                    }
                    else{
                        fitSide = srcBitmap.width
                    }

                    RectCrop(cropX, cropY, fitSide, fitSide)
                }

            return Bitmap.createBitmap(
                srcBitmap,
                cropRect.x,
                cropRect.y,
                cropRect.width,
                cropRect.height)
        }

对那些想了解算法的人的解释。主要思想-我们应该按比例拉伸作物面积(!),直到最大面积适合图像为止。但是当裁切区域超过图像区域时,有一种不可接受的情况(L4和P4)。因此,这里只有一种方法-更改适合的方向,将作物面积拉伸到另一侧

enter image description here

在“方案”中,我没有将作物居中(为了更好地理解主意),但是这两个解决方案都可以做到这一点。这是getImageCropped的结果:

Landscape crop Portrait crop Square crop

此SwiftUI代码提供了上面的图像进行测试:

        var body: some View {

            // IMAGE LAND
            let ORIG_NAME = "image_land.jpg"
            let ORIG_W = 400.0
            let ORIG_H = 265.0

            //  > crop Land
            let cropW = 400.0
            let cropH = 200.0

            //  > crop Port
//            let cropW = 50.0
//            let cropH = 265.0

            //  > crop Center Square
//            let cropW = 265.0
//            let cropH = 265.0



            // IMAGE PORT
//            let ORIG_NAME = "image_port.jpg"
//            let ORIG_W = 350.0
//            let ORIG_H = 500.0

            //  > crop Land
//            let cropW = 350.0
//            let cropH = 410.0

            //  > crop Port
//            let cropW = 190.0
//            let cropH = 500.0

            //  > crop Center Square
//            let cropW = 350.0
//            let cropH = 350.0



            let imageOriginal = UIImage(named: ORIG_NAME)!
            let imageCropped  = self.getImageCroppedShort(srcImage: imageOriginal, sizeToCrop: CGSize(width: cropW, height: cropH))

            return VStack{
                HStack{
                    Text("ImageArea \nW:\(Int(ORIG_W)) \nH:\(Int(ORIG_H))").font(.body)
                    Text("CropArea \nW:\(Int(cropW)) \nH:\(Int(cropH))").font(.body)
                }
                ZStack{

                    Image(uiImage: imageOriginal)
                        .resizable()
                        .opacity(0.4)

                    Image(uiImage: imageCropped)
                        .resizable()
                        .frame(width: CGFloat(cropW), height: CGFloat(cropH))
                }
                .frame(width: CGFloat(ORIG_W), height: CGFloat(ORIG_H))
                .background(Color.black)
            }
        }

Kotlin解决方案的工作原理相同。相信我)