图像无法用作 Core Graphics 的遮罩
Image fails to work as a mask with Core Graphics
我在使用另一个 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
颜色 space 的图像是问题所在。 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."
然而,headers给出了一个不那么严格的颜色space要求:
"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."
无论我做什么,我只能为我的蒙版图像(不是 DeviceGray)获得 kCGColorSpaceModelMonochrome
颜色 space - 应该工作基于后一个声明,而不是第一个。
遮罩图像本身是使用 UIGraphicsImageRenderer
的 image(actions: (UIGraphicsImageRendererContext) -> Void)
函数从 UIBezierPath
生成的,我在调试器中看起来没问题(它是 irregularly-shaped black-and-white 图片)- 它只是不能用作遮罩。
注意:我正试图从 iOS 12 内存使用优化中获益 UIGraphicsImageRenderer
,详见 2018 年 WWDC 演讲 'Image and Graphics Best Practices'( 29:15),但遗憾的是无法手动指定生成图像的颜色 space。这是一项功能,但我希望有一种方法可以覆盖它。
https://developer.apple.com/videos/play/wwdc2018/219/
有谁知道如何让这个掩码操作正常工作?
是的,面具需要颜色 space 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)
}
}
现在我们可以绘制蒙版并将其应用于任意图像:
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)
}
您所要做的就是用您自己的路径替换贝塞尔曲线路径。
我在使用另一个 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
颜色 space 的图像是问题所在。 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."
然而,headers给出了一个不那么严格的颜色space要求:
"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."
无论我做什么,我只能为我的蒙版图像(不是 DeviceGray)获得 kCGColorSpaceModelMonochrome
颜色 space - 应该工作基于后一个声明,而不是第一个。
遮罩图像本身是使用 UIGraphicsImageRenderer
的 image(actions: (UIGraphicsImageRendererContext) -> Void)
函数从 UIBezierPath
生成的,我在调试器中看起来没问题(它是 irregularly-shaped black-and-white 图片)- 它只是不能用作遮罩。
注意:我正试图从 iOS 12 内存使用优化中获益 UIGraphicsImageRenderer
,详见 2018 年 WWDC 演讲 'Image and Graphics Best Practices'( 29:15),但遗憾的是无法手动指定生成图像的颜色 space。这是一项功能,但我希望有一种方法可以覆盖它。
https://developer.apple.com/videos/play/wwdc2018/219/
有谁知道如何让这个掩码操作正常工作?
是的,面具需要颜色 space 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)
}
}
现在我们可以绘制蒙版并将其应用于任意图像:
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)
}
您所要做的就是用您自己的路径替换贝塞尔曲线路径。