如何将UIImage / CGImageRef的alpha通道转换为掩码?

我怎样才能提取UIImage或CGImageRef的alpha通道,并将其转换成我可以使用CGImageMaskCreate的掩码?

例如:

示例图像

基本上,给任何图像,我不关心图像内的颜色。 我想要的只是创build一个代表alpha通道的灰度图像。 这个图像可以用来掩盖其他图像。

当你提供一个图标图像的时候,这个UIBarButtonItem的一个例子是。 根据苹果文件,它指出:

显示在栏上的图像来源于该图像。 如果这张图片太大而无法放在栏上,则会缩放。 通常,工具栏和导航栏图像的大小是20 x 20点。 源图像中的alpha值用于创build图像 – 不透明值将被忽略。

该UIBarButtonItem采取任何图像,只看阿尔法,而不是图像的颜色。

要按照条形button项目的方式为图标着色,您不需要传统的蒙版,您需要蒙版的反转 – 原始图像中的不透明像素用作最终着色的反转,而不是其他方式。

这是完成这个的一个方法。 把你的原始RBGA图像,并通过以下处理:

  • 将它绘制成仅用于Alpha通道的位图图像
  • 如上所述,颠倒每个像素的alpha值以获得相反的行为
  • 把这个倒置的alpha图像变成一个真正的蒙版
  • 用它。

例如

#define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) // Original RGBA image CGImageRef originalMaskImage = [[UIImage imageNamed:@"masktest.png"] CGImage]; float width = CGImageGetWidth(originalMaskImage); float height = CGImageGetHeight(originalMaskImage); // Make a bitmap context that's only 1 alpha channel // WARNING: the bytes per row probably needs to be a multiple of 4 int strideLength = ROUND_UP(width * 1, 4); unsigned char * alphaData = calloc(strideLength * height, sizeof(unsigned char)); CGContextRef alphaOnlyContext = CGBitmapContextCreate(alphaData, width, height, 8, strideLength, NULL, kCGImageAlphaOnly); // Draw the RGBA image into the alpha-only context. CGContextDrawImage(alphaOnlyContext, CGRectMake(0, 0, width, height), originalMaskImage); // Walk the pixels and invert the alpha value. This lets you colorize the opaque shapes in the original image. // If you want to do a traditional mask (where the opaque values block) just get rid of these loops. for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { unsigned char val = alphaData[y*strideLength + x]; val = 255 - val; alphaData[y*strideLength + x] = val; } } CGImageRef alphaMaskImage = CGBitmapContextCreateImage(alphaOnlyContext); CGContextRelease(alphaOnlyContext); free(alphaData); // Make a mask CGImageRef finalMaskImage = CGImageMaskCreate(CGImageGetWidth(alphaMaskImage), CGImageGetHeight(alphaMaskImage), CGImageGetBitsPerComponent(alphaMaskImage), CGImageGetBitsPerPixel(alphaMaskImage), CGImageGetBytesPerRow(alphaMaskImage), CGImageGetDataProvider(alphaMaskImage), NULL, false); CGImageRelease(alphaMaskImage); 

现在你可以使用finalMaskImage作为CGContextClipToMask等的掩码。

本Zotto的解决scheme是正确的,但有一种方法可以做到这一点,没有math或当地的复杂性,依靠CGImage为我们做的工作。

以下解决scheme使用Swift(v3)通过反转现有图像的Alpha通道从图像创build一个蒙版。 源图像中的透明像素将变得不透明,并且部分透明的像素将被反转以成比例地或多或less透明。

这个解决scheme的唯一要求是一个CGImage基础图像。 一个可以从UIImage.cgImage获得最多的UIImage 。 如果您要在CGContext自己渲染基础图像,请使用CGContext.makeImage()生成新的CGImage

代码

 let image: CGImage = // your image // Create a "Decode Array" which flips the alpha channel in // an image in ARGB format (premultiplied first). Adjust the // decode array as needed based on the pixel format of your // image data. // The tuples in the decode array specify how to clamp the // pixel color channel values when the image data is decoded. // // Tuple(0,1) means the value should be clamped to the range // 0 and 1. For example, a red value of 0.5888 (~150 out of // 255) would not be changed at all because 0 < 0.5888 < 1. // Tuple(1,0) flips the value, so the red value of 0.5888 // would become 1-0.5888=0.4112. We use this method to flip // the alpha channel values. let decode = [ CGFloat(1), CGFloat(0), // alpha (flipped) CGFloat(0), CGFloat(1), // red (no change) CGFloat(0), CGFloat(1), // green (no change) CGFloat(0), CGFloat(1) ] // blue (no change) // Create the mask `CGImage` by reusing the existing image data // but applying a custom decode array. let mask = CGImage(width: image.width, height: image.height, bitsPerComponent: image.bitsPerComponent, bitsPerPixel: image.bitsPerPixel, bytesPerRow: image.bytesPerRow, space: image.colorSpace!, bitmapInfo: image.bitmapInfo, provider: image.dataProvider!, decode: decode, shouldInterpolate: image.shouldInterpolate, intent: image.renderingIntent) 

而已! mask CGImage现在已经可以和context.clip(to: rect, mask: mask!)一起使用了context.clip(to: rect, mask: mask!)

演示

这是我的基本图像与透明背景上的不透明红色的“面具图像”: 图像,将成为透明背景上的红色文字图像蒙版

为了演示通过上述algorithm运行时会发生什么情况,下面是一个简单地将生成的图像呈现在绿色背景上的示例。

 override func draw(_ rect: CGRect) { // Create decode array, flipping alpha channel let decode = [ CGFloat(1), CGFloat(0), CGFloat(0), CGFloat(1), CGFloat(0), CGFloat(1), CGFloat(0), CGFloat(1) ] // Create the mask `CGImage` by reusing the existing image data // but applying a custom decode array. let mask = CGImage(width: image.width, height: image.height, bitsPerComponent: image.bitsPerComponent, bitsPerPixel: image.bitsPerPixel, bytesPerRow: image.bytesPerRow, space: image.colorSpace!, bitmapInfo: image.bitmapInfo, provider: image.dataProvider!, decode: decode, shouldInterpolate: image.shouldInterpolate, intent: image.renderingIntent) let context = UIGraphicsGetCurrentContext()! // paint solid green background to highlight the transparent areas context.setFillColor(UIColor.green.cgColor) context.fill(rect) // render the mask image directly. The black areas will be masked. context.draw(mask!, in: rect) } 

遮罩直接渲染的图像,通过剪辑区域显示背景

现在我们可以使用该图像来掩盖任何呈现的内容。 下面是一个例子,我们在上一个例子中的绿色顶部渲染了一个蒙板渐变。

 override func draw(_ rect: CGRect) { let context = UIGraphicsGetCurrentContext()! // paint solid green background to highlight the transparent areas context.setFillColor(UIColor.green.cgColor) context.fill(rect) let mask: CGImage = // mask generation elided. See previous example. // Clip to the mask image context.clip(to: rect, mask: mask!) // Create a simple linear gradient let colors = [ UIColor.red.cgColor, UIColor.blue.cgColor, UIColor.orange.cgColor ] let gradient = CGGradient(colorsSpace: context.colorSpace, colors: colors as CFArray, locations: nil) // Draw the linear gradient around the clipping area context.drawLinearGradient(gradient!, start: CGPoint.zero, end: CGPoint(x: rect.size.width, y: rect.size.height), options: CGGradientDrawingOptions()) } 

最后的图像显示了原始图像文本被遮盖为绿色的渐变

(注意:你也可以交换CGImage代码来使用Accelerate Framework的vImage ,可能受益于该库中的vector处理优化,我还没有尝试过)。

我尝试了由quixoto提供的代码,但它不适合我,所以我改变了一点点。

问题是只绘制alpha通道不适合我,所以我通过首先获取原始图像的数据并在Alpha通道上工作来手动完成。

 #define ROUND_UP(N, S) ((((N) + (S) - 1) / (S)) * (S)) #import <stdlib.h> - (CGImageRef) createMaskWithImageAlpha: (CGContextRef) originalImageContext { UInt8 *data = (UInt8 *)CGBitmapContextGetData(originalImageContext); float width = CGBitmapContextGetBytesPerRow(originalImageContext) / 4; float height = CGBitmapContextGetHeight(originalImageContext); // Make a bitmap context that's only 1 alpha channel // WARNING: the bytes per row probably needs to be a multiple of 4 int strideLength = ROUND_UP(width * 1, 4); unsigned char * alphaData = (unsigned char * )calloc(strideLength * height, 1); CGContextRef alphaOnlyContext = CGBitmapContextCreate(alphaData, width, height, 8, strideLength, NULL, kCGImageAlphaOnly); // Draw the RGBA image into the alpha-only context. //CGContextDrawImage(alphaOnlyContext, CGRectMake(0, 0, width, height), originalMaskImage); // Walk the pixels and invert the alpha value. This lets you colorize the opaque shapes in the original image. // If you want to do a traditional mask (where the opaque values block) just get rid of these loops. for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { //unsigned char val = alphaData[y*strideLength + x]; unsigned char val = data[y*(int)width*4 + x*4 + 3]; val = 255 - val; alphaData[y*strideLength + x] = val; } } CGImageRef alphaMaskImage = CGBitmapContextCreateImage(alphaOnlyContext); CGContextRelease(alphaOnlyContext); free(alphaData); // Make a mask CGImageRef finalMaskImage = CGImageMaskCreate(CGImageGetWidth(alphaMaskImage), CGImageGetHeight(alphaMaskImage), CGImageGetBitsPerComponent(alphaMaskImage), CGImageGetBitsPerPixel(alphaMaskImage), CGImageGetBytesPerRow(alphaMaskImage), CGImageGetDataProvider(alphaMaskImage), NULL, false); CGImageRelease(alphaMaskImage); return finalMaskImage; } 

你可以像这样调用这个函数

  CGImageRef originalImage = [image CGImage]; CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef bitmapContext = CGBitmapContextCreate(NULL, CGImageGetWidth(originalImage), CGImageGetHeight(originalImage), 8, CGImageGetWidth(originalImage)*4, colorSpace, kCGImageAlphaPremultipliedLast); CGContextDrawImage(bitmapContext, CGRectMake(0, 0, CGBitmapContextGetWidth(bitmapContext), CGBitmapContextGetHeight(bitmapContext)), originalImage); CGImageRef finalMaskImage = [self createMaskWithImageAlpha:bitmapContext]; //YOUR CODE HERE CGContextRelease(bitmapContext); CGImageRelease(finalMaskImage);