ios - 图像无法用作 Core Graphics 的 mask

标签 ios swift core-graphics mask masking

我在使用另一个 UIImage 作为掩码来掩蔽 UIImage 时遇到了一些问题。我试过使用 masking(_:) Core Graphics 函数(Objective C 中的 CGImageCreateWithMask),但蒙版图像的输出显示没有任何蒙版。它只显示正常图像。
我使用了两种方法来做到这一点:首先,通过传递掩码图像本身来应用。其次,通过使用 CGImage初始化器从该图像创建一个掩码并改为传递它。这是执行此操作的函数:

func performMask(_ originalImage:UIImage, maskImage:UIImage) -> UIImage? {
    guard let originalImageCGImage = originalImage.cgImage, let maskImageCGImage = maskImage.cgImage else { return nil }

    print("Method 1 - Image (UIImage):")
    print("\(String(describing: maskImage.cgImage))\n\n")

    print("Method 2 - Mask (CGImage):")

    let maskImage2 = CGImage(
        maskWidth: Int(maskImage.size.width * maskImage.scale),
        height: Int(maskImage.size.height * maskImage.scale),
        bitsPerComponent: maskImageCGImage.bitsPerComponent,
        bitsPerPixel: maskImageCGImage.bitsPerPixel,
        bytesPerRow: maskImageCGImage.bytesPerRow,
        provider: maskImageCGImage.dataProvider!,
        decode: nil,
        shouldInterpolate: true
    )

    print("\(String(describing: maskImage2))\n\n")

    let maskedImage     = originalImageCGImage.masking(maskImage.cgImage!)  // Output: Method 1
//  let maskedImage2    = originalImageCGImage.masking(maskImage2!)         // Output: Method 2

    return UIImage(cgImage: maskedImage!)
}
这是我得到的控制台输出,显示了上面的打印语句:
Method 1 - Image (UIImage):
Optional(<CGImage 0x104223890> (DP)
    <<CGColorSpace 0x281414960> (kCGColorSpaceICCBased; kCGColorSpaceModelMonochrome; Generic Gray Gamma 2.2 Profile)>
        width = 320, height = 320, bpc = 8, bpp = 8, row bytes = 640 
        kCGImageAlphaNone | 0 (default byte order)  | kCGImagePixelFormatPacked 
        is mask? No, has masking color? No, has soft mask? No, has matte? No, should interpolate? Yes)


Method 2 - Mask (CGImage):
Optional(<CGImage 0x104223e90> (DP)
    <(null)>
        width = 320, height = 320, bpc = 8, bpp = 8, row bytes = 640 
        kCGImageAlphaNone | 0 (default byte order)  | kCGImagePixelFormatPacked 
        is mask? Yes, has masking color? No, has soft mask? No, has matte? No, should interpolate? Yes)
我的一个理论是图像在 kCGColorSpaceModelMonochrome色彩空间是问题。 masking(_:) 的文档功能说:

"If the mask is an image, it must be in the DeviceGray color space, must not have an alpha component, and may not itself be masked by an image mask or a masking color."


但是,标题给出了不太严格的色彩空间要求:

"If `mask' is an image, then it must be in a monochrome color space (e.g. DeviceGray, GenericGray, etc...), may not have alpha, and may not itself be masked by an image mask or a masking color."


不管我做什么,我只能得到一个kCGColorSpaceModelMonochrome我的蒙版图像(不是 DeviceGray)的颜色空间 - 它应该基于后一个语句工作,但不是第一个语句。
掩码图像本身是从 UIBezierPath 生成的。使用 UIGraphicsImageRendererimage(actions: (UIGraphicsImageRendererContext) -> Void)功能,并且在调试器中对我来说看起来不错(这是一个不规则形状的黑白图像)-它只是不能用作掩码。
注意:我正在尝试从 UIGraphicsImageRenderer 中的 iOS 12 内存使用优化中受益。在 2018 年的 WWDC 演讲“图像和图形最佳实践”(29:15) 中有详细说明,但遗憾的是无法手动指定生成图像的色彩空间。这是一个功能,但我希望有一种方法可以覆盖它。
https://developer.apple.com/videos/play/wwdc2018/219/
有谁知道如何让这个面具操作正常工作?

最佳答案

是的,蒙版需要在颜色空间 DeviceGray 中。好消息是您可以在 DeviceGray 中轻松生成蒙版,只是无法使用 UIGraphicsImageRenderer .让我们实现一个在 DeviceGray 中生成掩码并接受允许您绘制任何您喜欢的掩码的闭包的类型。

struct MaskRenderer {
    let size: CGSize
    let scale: CGFloat

    var sizeInPixels: CGSize {
        return CGSize(width: size.width * scale, height: size.height * scale)
    }

    func image(actions: (CGContext) -> Void) -> UIImage? {
        let colorSpace = CGColorSpaceCreateDeviceGray()

        guard let context = CGContext.init(
            data: nil,
            width: Int(sizeInPixels.width),
            height: Int(sizeInPixels.height),
            bitsPerComponent: 8,
            bytesPerRow: 0,
            space: colorSpace,
            bitmapInfo: CGImageAlphaInfo.none.rawValue
            ) else { return nil }

        actions(context)

        guard let coreImageMask = context.makeImage() else { return nil }

        return UIImage(cgImage: coreImageMask)
    }
}
让我们也实现一个 UIImage将掩码应用于接收器的扩展,返回掩码图像:
extension UIImage {
    func withMask(_ imageMask: UIImage) -> UIImage? {
        guard let coreImage = cgImage else { return nil }
        guard let coreImageMask = imageMask.cgImage else { return nil }
        guard let coreMaskedImage = coreImage.masking(coreImageMask) else { return nil }

        return UIImage(cgImage: coreMaskedImage)
    }
}
现在我们可以绘制并将 mask 应用到任意图像:
func applyMask(to image: UIImage) -> UIImage? {
    let renderer = MaskRenderer(size: image.size, scale: image.scale)
    guard let imageMask = renderer.image(actions: { context in
        let rect = CGRect(origin: .zero, size: renderer.sizeInPixels)
            .insetBy(dx: 0, dy: renderer.sizeInPixels.height / 4)
        let path = UIBezierPath(ovalIn: rect)
        context.addPath(path.cgPath)
        context.setFillColor(gray: 1, alpha: 1)
        context.drawPath(using: .fillStroke)
    }) else { return nil }
    return image.withMask(imageMask)
}
您所要做的就是用您的替换贝塞尔路径。

关于ios - 图像无法用作 Core Graphics 的 mask ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62725896/

相关文章:

swift - 如何快速按钮突出显示和突出显示颜色为红色

swift - 将字典映射到数组到字符串

iphone - 理论上是: How is the alpha component premultiplied into the other components of an PNG in iPhone OS,,如何才能正确地对其进行预乘呢?

iphone - 使用AVAudioPlayer,我的声音(偶尔!)听起来很奇怪

iphone - 是否可以查看未越狱的 iPhone 上还有哪些其他应用程序?

ios - 一个非常基本的 swift 应用程序无法编译(直接来自 Apple 的源代码)

ios - 如何为 CAShapeLayer 绘制圆点描边图案?

ios - CAShapeLayer strokeColor 从图像模式 UIColor 到 CGColor 颠倒

ios - 我无法发布通知

ios - swiftUI 转换 : Scale from some frame - like iOS Homescreen is doing when opening an App