resize and crop image centered

前端 未结 3 550
半阙折子戏
半阙折子戏 2021-01-31 11:11

so currently i\'m trying to crop and resize a picture to fit it into a specific size without losing the ratio.

a small image to show what i mean:

相关标签:
3条回答
  • 2021-01-31 11:40

    Had the same task for preview image in a gallery. For fixed Crop-Area (Image for SwiftUI and Canvas Rect for Kotiln) I want to crop central content of image - by maximum of on of the image's side. (see explanation below)

    Here solutions for

    Swift

    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
    
        }
    

    and

    Kotlin

    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)
            }
    

    An explanation for those who want to understand algorithm. The main idea - we should stretch a Crop-Area proportionally(!) until the biggest side of it fits image. But there is one unacceptable case (L4 and P4) when Crop-Area exceeds Image-Area. So here we have only one way - change fit direction and stretch Crop-Area to the other side

    On Scheme I didn't centering of crop (for better understanding idea), but both of this solutions do this. Here result of getImageCropped:

    This SwiftUI code provides images above to test:

            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 solution works identically. Trust me)

    0 讨论(0)
  • 2021-01-31 11:43

    I have came across the same issue in one of my application and developed this piece of code:

    + (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;
    }
    

    You can modify it according to your needs.

    0 讨论(0)
  • 2021-01-31 11:55

    This method will do what you want and is a category of UIImage for ease of use. I went with resize then crop, you could switch the code around easily enough if you want crop then resize. The bounds checking in the function is purely illustrative. You might want to do something different, for example center the crop rect relative to the outputImage dimensions but this ought to get you close enough to make whatever other changes you need.

    @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
    
    0 讨论(0)
提交回复
热议问题