Clip UIImage to UIBezierPath (not masking)

ε祈祈猫儿з 提交于 2019-12-22 08:25:04

问题


I'm trying to clip a UIImage based on a given UIBezierPath, but the resulting image retains the original shape and size. I want the shape and size to resemble the path, i.e. the visible content of the new image.

Take a look at this following example:

The following is the code I am using to mask an image given a path:

func imageByApplyingClippingBezierPath(_ path: UIBezierPath) -> UIImage {
    UIGraphicsBeginImageContextWithOptions(CGSize(
        width: size.width,
        height: size.height
    ), false, 0.0)
    let context = UIGraphicsGetCurrentContext()!
    context.saveGState()

    path.addClip()
    draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

    let newImage = UIGraphicsGetImageFromCurrentImageContext()!

    context.restoreGState()
    UIGraphicsEndImageContext()

    return newImage
}

One solution would be to crop the image after masking it, but ideally I want this to be done as simple and efficiently as possible in one go.

Any solution/ tips would be appreciated.


回答1:


Thanks to Rob I was able to implement a solution using the cropping(to:) method of CGImage.

This is a two step process where first I mask the image using the given path, and then crop the result using the bounds of the path.

The following is my final working source code for implementing a clipping UIBezierPath UIImage extension:

extension UIImage {

    func imageByApplyingClippingBezierPath(_ path: UIBezierPath) -> UIImage {
        // Mask image using path
        let maskedImage = imageByApplyingMaskingBezierPath(path)

        // Crop image to frame of path
        let croppedImage = UIImage(cgImage: maskedImage.cgImage!.cropping(to: path.bounds)!)
        return croppedImage
    }

    func imageByApplyingMaskingBezierPath(_ path: UIBezierPath) -> UIImage {
        // Define graphic context (canvas) to paint on
        UIGraphicsBeginImageContext(size)
        let context = UIGraphicsGetCurrentContext()!
        context.saveGState()

        // Set the clipping mask
        path.addClip()
        draw(in: CGRect(x: 0, y: 0, width: size.width, height: size.height))

        let maskedImage = UIGraphicsGetImageFromCurrentImageContext()!

        // Restore previous drawing context
        context.restoreGState()
        UIGraphicsEndImageContext()

        return maskedImage
    }

}



回答2:


I am using following in objective C, for cropping any image to non-transperant rect. check if its helpful

@implementation UIImage (cropTransperant)
- (UIImage *) CropimageByTrimmingTransparentPixels {
    int rows = self.size.height;
    int cols = self.size.width;
    int bytesPerRow = cols*sizeof(uint8_t);

    if ( rows < 2 || cols < 2 ) {
        return self;
    }


    uint8_t *bitmapData = calloc(rows*cols, sizeof(uint8_t));


    CGContextRef contextRef = CGBitmapContextCreate(bitmapData, cols, rows, 8, bytesPerRow, NULL, kCGImageAlphaOnly);


    CGImageRef cgImage = self.CGImage;
    CGRect rect = CGRectMake(0, 0, cols, rows);
    CGContextDrawImage(contextRef, rect, cgImage);

    //get all non-transparent pixels in every row and every column
    uint16_t *rowSum = calloc(rows, sizeof(uint16_t));
    uint16_t *colSum = calloc(cols, sizeof(uint16_t));

    //loop through all pixels
    for ( int row = 0; row < rows; row++) {
        for ( int col = 0; col < cols; col++)
        {
            if ( bitmapData[row*bytesPerRow + col] ) {  //when you get non transperant pixel
                rowSum[row]++;
                colSum[col]++;
            }
        }
    }

       UIEdgeInsets crop = UIEdgeInsetsMake(0, 0, 0, 0); //croprect, initialize with 0,0,0,

    for ( int i = 0; i<rows; i++ ) {        //get top
        if ( rowSum[i] > 0 ) {
            crop.top = i; break;
        }
    }

    for ( int i = rows; i >= 0; i-- ) {     //get bottom
        if ( rowSum[i] > 0 ) {
            crop.bottom = MAX(0, rows-i-1); break;
        }
    }

    for ( int i = 0; i<cols; i++ ) {        //get left
        if ( colSum[i] > 0 ) {
            crop.left = i; break;
        }
    }

    for ( int i = cols; i >= 0; i-- ) {     //get right
        if ( colSum[i] > 0 ) {
            crop.right = MAX(0, cols-i-1); break;
        }
    }

    free(bitmapData);
    free(colSum);
    free(rowSum);

    if ( crop.top == 0 && crop.bottom == 0 && crop.left == 0 && crop.right == 0 ) {

        return self;
    }
    else {

        rect.origin.x += crop.left;
        rect.origin.y += crop.top;
        rect.size.width -= crop.left + crop.right;
        rect.size.height -= crop.top + crop.bottom;

        //crop  image to calcualted rect
        CGImageRef newImage = CGImageCreateWithImageInRect(cgImage, rect);


        return [UIImage imageWithCGImage:newImage];
    }
}

@end


来源:https://stackoverflow.com/questions/40815247/clip-uiimage-to-uibezierpath-not-masking

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!