ios-使用具有图像全分辨率的bezierpath遮罩图像

enter image description here
嗨,我有一个路径(形状)和一个高分辨率的图像.我将高分辨率图像设为在其上绘制路径的视图内的AspectFit,并且我希望使用该路径遮盖图像,但要以图像的完整分辨率而不是我们看到路径的分辨率来遮盖图像.问题是,当我不将其放大以进行高分辨率遮罩时,它会完美工作,但是当我这样做时,一切都搞砸了.面具被拉长了,起源毫无意义.

enter image description here

我所希望的是能够以相同的图像纵横比(在图像的全分辨率下)放大路径并正确放置它,以便它可以正确掩盖高分辨率图像.
我已经试过了:

Masking CGContext with a CGPathRef?

和这个

Creating mask with CGImageMaskCreate is all black (iphone)

和这个

Clip UIImage to UIBezierPath (not masking)

当我尝试掩盖高质量图像(大于屏幕分辨率)时,它们都无法正常工作

编辑我在github上发布了一个工作项目,以显示正常质量的蒙版(以屏幕的分辨率)和高质量的蒙版(以图像的分辨率)之间的问题.我真的很感谢您的帮助.
https://github.com/Reza-Abdolahi/HighResMasking

最佳答案
如果我正确理解您的问题:

>您拥有一个包含使用UIViewContentModeScaleAspectFit缩小(甚至放大)的图像的图像视图.
>您有一个贝塞尔曲线路径,其点位于该图像视图的几何形状(坐标系)中.

现在,您要以贝塞尔曲线路径遮盖的原始分辨率创建图像副本.

我们可以认为图像具有自己的几何形状,其原点位于图像的左上角,并且沿每个轴的一个单位是一个点.因此,我们需要做的是:

>创建足够大的图形渲染器以将图像绘制到其中,而无需缩放.此渲染器的几何就是图像的几何.
>将贝塞尔曲线路径从视图几何体转换为渲染器几何体.
>将转换后的路径应用于渲染器的剪辑区域.
>将图像(未变形)绘制到渲染器中.

第2步很困难,因为我们必须提出正确的CGAffineTransform.在适合方面的情况下,变换不仅需要缩放图像,而且还可能沿x轴或y轴(而不是两者)平移图像.但是让我们更笼统一点,并支持其他UIViewContentMode设置.这是一个类别,可让您要求UIImageView进行将视图几何图形中的点转换为图像几何图形中的点的转换:

@implementation UIImageView (ImageGeometry)

/**
 * Return a transform that converts points in my geometry to points in the
 * image's geometry. The origin of the image's geometry is at its upper
 * left corner, and one unit along each axis is one point in the image.
 */
- (CGAffineTransform)imageGeometryTransform {
    CGRect viewBounds = self.bounds;
    CGSize viewSize = viewBounds.size;
    CGSize imageSize = self.image.size;

    CGFloat xScale = imageSize.width / viewSize.width;
    CGFloat yScale = imageSize.height / viewSize.height;
    CGFloat tx, ty;
    switch (self.contentMode) {
        case UIViewContentModeScaleToFill: tx = 0; ty = 0; break;
        case UIViewContentModeScaleAspectFit:
            if (xScale > yScale) { tx = 0; ty = 0.5; yScale = xScale; }
            else if (xScale < yScale) { tx = 0.5; ty = 0; xScale = yScale; }
            else { tx = 0; ty = 0; }
            break;
        case UIViewContentModeScaleAspectFill:
            if (xScale < yScale) { tx = 0; ty = 0.5; yScale = xScale; }
            else if (xScale > yScale) { tx = 0.5; ty = 0; xScale = yScale; }
            else { tx = 0; ty = 0; imageSize = viewSize; }
            break;
        case UIViewContentModeCenter: tx = 0.5; ty = 0.5; xScale = yScale = 1; break;
        case UIViewContentModeTop: tx = 0.5; ty = 0; xScale = yScale = 1; break;
        case UIViewContentModeBottom: tx = 0.5; ty = 1; xScale = yScale = 1; break;
        case UIViewContentModeLeft: tx = 0; ty = 0.5; xScale = yScale = 1; break;
        case UIViewContentModeRight: tx = 1; ty = 0.5; xScale = yScale = 1; break;
        case UIViewContentModeTopLeft: tx = 0; ty = 0; xScale = yScale = 1; break;
        case UIViewContentModeTopRight: tx = 1; ty = 0; xScale = yScale = 1; break;
        case UIViewContentModeBottomLeft: tx = 0; ty = 1; xScale = yScale = 1; break;
        case UIViewContentModeBottomRight: tx = 1; ty = 1; xScale = yScale = 1; break;
        default: return CGAffineTransformIdentity; // Mode not supported by UIImageView.
    }

    tx *= (imageSize.width - xScale * (viewBounds.origin.x + viewSize.width));
    ty *= (imageSize.height - yScale * (viewBounds.origin.y + viewSize.height));
    CGAffineTransform transform = CGAffineTransformMakeTranslation(tx, ty);
    transform = CGAffineTransformScale(transform, xScale, yScale);
    return transform;
}

@end

有了这个,我们可以编写掩盖图像的代码.在我的测试应用程序中,我有一个UIImageView子类,名为PathEditingView,它处理贝塞尔曲线路径编辑.所以我的视图控制器创建这样的蒙版图像:

- (UIImage *)maskedImage {
    UIImage *image = self.pathEditingView.image;
    UIGraphicsImageRendererFormat *format = [[UIGraphicsImageRendererFormat alloc] init];
    format.scale = image.scale;
    format.prefersExtendedRange = image.imageRendererFormat.prefersExtendedRange;
    format.opaque = NO;
    UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:image.size format:format];
    return [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
        UIBezierPath *path = [self.pathEditingView.path copy];
        [path applyTransform:self.pathEditingView.imageGeometryTransform];
        CGContextRef gc = UIGraphicsGetCurrentContext();
        CGContextAddPath(gc, path.CGPath);
        CGContextClip(gc);
        [image drawAtPoint:CGPointZero];
    }];
}

它看起来像这样:

masking demo

当然,很难说输出图像是全分辨率的.让我们通过将输出图像裁剪到贝塞尔曲线路径的边界框来解决此问题:

- (UIImage *)maskedAndCroppedImage {
    UIImage *image = self.pathEditingView.image;
    UIBezierPath *path = [self.pathEditingView.path copy];
    [path applyTransform:self.pathEditingView.imageGeometryTransform];
    CGRect pathBounds = CGPathGetPathBoundingBox(path.CGPath);
    UIGraphicsImageRendererFormat *format = [[UIGraphicsImageRendererFormat alloc] init];
    format.scale = image.scale;
    format.prefersExtendedRange = image.imageRendererFormat.prefersExtendedRange;
    format.opaque = NO;
    UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:pathBounds.size format:format];
    return [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
        CGContextRef gc = UIGraphicsGetCurrentContext();
        CGContextTranslateCTM(gc, -pathBounds.origin.x, -pathBounds.origin.y);
        CGContextAddPath(gc, path.CGPath);
        CGContextClip(gc);
        [image drawAtPoint:CGPointZero];
    }];
}

蒙版和裁剪在一起看起来像这样:

masking and cropping demo

您可以在此演示中看到,输出图像比输入视图中可见的细节要详细得多,因为它是在输入图像的全分辨率下生成的.

转载注明原文:ios-使用具有图像全分辨率的bezierpath遮罩图像 - 代码日志