resize and crop image centered

前端 未结 3 560
半阙折子戏
半阙折子戏 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)

提交回复
热议问题