将 UIImage 剪辑到 UIBezierPath(不屏蔽)

Posted

技术标签:

【中文标题】将 UIImage 剪辑到 UIBezierPath(不屏蔽)【英文标题】:Clip UIImage to UIBezierPath (not masking) 【发布时间】:2016-11-26 05:25:25 【问题描述】:

我正在尝试根据给定的UIBezierPath 剪裁UIImage,但生成的图像保留了原始形状和大小。我希望形状和大小类似于路径,即新图像的可见内容。

看看下面这个例子:

以下是我用来屏蔽给定路径的图像的代码:

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

一种解决方案是在遮盖图像后对其进行裁剪,但理想情况下,我希望一次性尽可能简单有效地完成此操作。

任何解决方案/提示将不胜感激。

【问题讨论】:

不,继续裁剪它。不过,这很简单,只需调用CGImageCreateWithImageInRect 谢谢你的评论@Rob,你介意分享一个关于如何实现这个的快速代码sn-p吗? 不需要编码示例 Rob,我想通了。谢谢指点! 【参考方案1】:

感谢Rob,我能够使用CGImagecropping(to:) 方法实现解决方案。

这是一个两步过程,首先我使用给定的路径屏蔽图像,然后使用路径的边界裁剪结果。

以下是我实现剪辑UIBezierPathUIImage扩展的最终工作源代码:

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
    


【讨论】:

下面的objective c 版本只显示了您解决方案的裁剪部分,我们有没有可能拥有您所有解决方案的完整objective c 版本? @Aleksander 我真的很感激。【参考方案2】:

我在目标 C 中使用以下内容,将任何图像裁剪为非透明矩形。检查它是否有用

@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

【讨论】:

我还没有机会测试你的代码,但从逻辑上讲它似乎是有道理的。但是,理想情况下,我正在寻找一种更简单、更有效的解决方案。我不知道为什么它被否决了,有人愿意解释吗?

以上是关于将 UIImage 剪辑到 UIBezierPath(不屏蔽)的主要内容,如果未能解决你的问题,请参考以下文章

当滑块具有极端最大值时 UISlider MaximumTrackImage 剪辑

无法将嵌入的影片剪辑投射到影片剪辑类型

CSS,将内圈剪辑到图像

Android:将背景图像剪辑到屏幕边界

将多于一帧的影片剪辑添加到另一帧的下方到 VBOx 容器?

将 NSString 绘制到 UIImage