这个图片遮罩代码为什么不起作用?

11

我有一段用于图像遮罩的代码。基本上,我只使用PNG图像。所以我有一个300x400的PNG图像,具有24位色彩(PNG-24)。我不确定它是否还有alpha通道。但其中没有透明度。

然后,有一个图像遮罩,它是没有alpha通道的PNG-8位图像。它只有黑色,灰度和白色。

我将它们都创建为UIImage,并将它们放入UIImageView中时,它们都能正确显示。

然后,我使用以下代码创建了一个包含遮罩操作结果的UIImage:

+ (UIImage*)maskImage:(UIImage*)image withMask:(UIImage*)maskImage {
 CGImageRef maskRef = maskImage.CGImage;
 CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
          CGImageGetHeight(maskRef),
          CGImageGetBitsPerComponent(maskRef),
          CGImageGetBitsPerPixel(maskRef),
          CGImageGetBytesPerRow(maskRef),
          CGImageGetDataProvider(maskRef), NULL, false);
 CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
 return [UIImage imageWithCGImage:masked];
}

这是我对此所做的:

 UIImage *image = [UIImage imageNamed:@"coloredImagePNG24.png"];
 UIImage *maskImage = [UIImage imageNamed:@"theMaskPNG8_Grayscale_NoAlpha.png"];
 UIImage *maskedImage = [MyGraphicUtils maskImage:image withMask:maskImage];
 UIImageView *testImageView = [[UIImageView alloc] initWithImage:maskedImage];
 testImageView.backgroundColor = [UIColor clearColor];
 testImageView.opaque = NO;

经过这么多步骤,coloredImagePNG24.png完全保持不变。没有遮罩发生。但是现在奇怪的事情是:如果我反过来使用这个图像作为遮罩,并将遮罩用作颜色图片,则会以灰度形式得到一些非常丑陋的东西(但被遮蔽了)。

有任何关于我的代码问题的想法吗?

更新:我刚刚搜索了另一个黑白png文件,以便将其用作遮罩。然后这一个起作用了!但是我自己制作的那个却不行。所以我认为代码存在很大的图像解码问题。我需要"标准化"图像到特定格式,这样才能正常工作。

7个回答

6
这段代码可能会对你有帮助。
- (UIImage*) maskImage:(UIImage *)image withMask:(UIImage *)maskImage {

    CGImageRef maskRef = maskImage.CGImage; 

    CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
        CGImageGetHeight(maskRef),
        CGImageGetBitsPerComponent(maskRef),
        CGImageGetBitsPerPixel(maskRef),
        CGImageGetBytesPerRow(maskRef),
        CGImageGetDataProvider(maskRef), NULL, false);

    CGImageRef masked = CGImageCreateWithMask([image CGImage], mask);
    return [UIImage imageWithCGImage:masked];

}

请参考此示例演示: 如何遮罩图像。该示例涉及IT技术相关内容。

7
只要使用UIImageView显示或手动绘制到新的图像上下文中,这个方法就可以起作用。如果你尝试使用UIImagePNGRepresentation(yourMaskedImage)将图像保存到文件中,你会失去蒙版。只是提供信息 - 我曾经因此苦恼了一段时间。另外,在代码末尾,你应该释放一些引用以避免内存泄漏: CGImageRelease(maskedImageRef); CGImageRelease(mask); - Alex the Ukrainian
浪费了好几个小时,而@AlextheUkrainian的评论竟然是答案。非常感谢! - mxcl
哎呀,我第一次收到人们的回复,内心感觉非常兴奋。:p 此外,您可以给评论点赞,这样有助于其他人了解哪些评论对您有帮助!@mxcl - Alex the Ukrainian
尽管我已经从谷歌下载了掩模和图像,但对我仍不起作用,但上述函数始终将掩模变量返回为掩模图像。 我在使用Swift 4。有什么想法吗? - iAviator

5

正如你所发现的那样,文件保存方式会有所不同,因为Core Graphics对于用于蒙版的位的格式非常讲究。

我发现从任意图像生成图像掩码的最佳和最可靠方法是这样做的:

  1. 创建一个位图图形上下文,该上下文的格式适用于图像掩码(不能使用UIGraphicsBeginImageContext()进行此操作)
  2. 将您的图像绘制到此位图图形上下文中
  3. 从位图图形上下文的位中创建图像掩码。

请尝试此功能:

CGImageRef createMaskWithImage(CGImageRef image)
{
    int maskWidth               = CGImageGetWidth(image);
    int maskHeight              = CGImageGetHeight(image);
    //  round bytesPerRow to the nearest 16 bytes, for performance's sake
    int bytesPerRow             = (maskWidth + 15) & 0xfffffff0;
    int bufferSize              = bytesPerRow * maskHeight;

    //  allocate memory for the bits 
    CFMutableDataRef dataBuffer = CFDataCreateMutable(kCFAllocatorDefault, 0);
    CFDataSetLength(dataBuffer, bufferSize);

    //  the data will be 8 bits per pixel, no alpha
    CGColorSpaceRef colourSpace = CGColorSpaceCreateDeviceGray();
    CGContextRef ctx            = CGBitmapContextCreate(CFDataGetMutableBytePtr(dataBuffer),
                                                        maskWidth, maskHeight,
                                                        8, bytesPerRow, colourSpace, kCGImageAlphaNone);
    //  drawing into this context will draw into the dataBuffer.
    CGContextDrawImage(ctx, CGRectMake(0, 0, maskWidth, maskHeight), image);
    CGContextRelease(ctx);

    //  now make a mask from the data.
    CGDataProviderRef dataProvider  = CGDataProviderCreateWithCFData(dataBuffer);
    CGImageRef mask                 = CGImageMaskCreate(maskWidth, maskHeight, 8, 8, bytesPerRow,
                                                        dataProvider, NULL, FALSE);

    CGDataProviderRelease(dataProvider);
    CGColorSpaceRelease(colourSpace);
    CFRelease(dataBuffer);

    return mask;
}

3
CGContextDrawImage函数不会按照你的预期工作。特别地:

对于每个分量采用2、4或8位的图像掩码,采用如下公式进行缩放,将每个分量映射到0到1的范围内:
1 / (2 ^ 分量位数 - 1)
例如,4位掩码的值的范围从0到15。这些值通过1/15进行缩放,以使每个分量范围从0到1。重新缩放为0或1的分量值与1位图像掩码的行为相同。重新调整为0到1之间的值会作为反向alpha来起作用。也就是说,使用填充颜色时,其似乎具有(1-MaskSampleValue)的α值。例如,如果8位掩码的样本值缩放到0.8,则当前填充颜色绘制时就好像它具有0.2的α值,即(1-0.8)。


我猜测这个函数正在以不同的格式重新解释您的RGB数据,这会扭曲颜色值。您尝试直接使用掩码CGImage了吗?

快速编辑:我的意思是“直接编写”:

+ (UIImage*)maskImage:(UIImage*)image withMask:(UIImage*)maskImage
{
    CGImageRef masked = CGImageCreateWithMask([image CGImage], [maskImage CGImage]);
    return [UIImage imageWithCGImage:masked];
}

2
以下代码对我有效:

以下代码对我有效

    +(UIImage*)maskImageExt:(UIImage *)image withMask:(UIImage *)maskImage {
      CGImageRef maskRef = maskImage.CGImage;
      CGImageRef imageRef = image.CGImage;

      CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef),
                                    CGImageGetHeight(maskRef),
                                    CGImageGetBitsPerComponent(maskRef),
                                    CGImageGetBitsPerPixel(maskRef),
                                    CGImageGetBytesPerRow(maskRef),
                                    CGImageGetDataProvider(maskRef), NULL, true);

CGImageRef maskImageRef = CGImageCreateWithMask([image CGImage], mask);

CGContextRef context = CGBitmapContextCreate(nil,
                                             CGImageGetWidth(imageRef),
                                             CGImageGetHeight(imageRef),
                                             CGImageGetBitsPerComponent(imageRef),
                                             CGImageGetBytesPerRow(imageRef),
                                             CGImageGetColorSpace(imageRef),
                                             CGImageGetBitmapInfo(imageRef));
CGRect imageRect = CGRectMake(0, 0, CGImageGetWidth(imageRef), CGImageGetHeight(imageRef));
CGContextDrawImage(context, imageRect, maskImageRef);
CGImageRef maskedImageRef = CGBitmapContextCreateImage(context);
UIImage *maskedImage = [UIImage imageWithCGImage:maskedImageRef];

CGImageRelease(mask);
CGContextRelease(context);
CGImageRelease(maskedImageRef);

return maskedImage;

}


0

看看这种方式是否更好:

+ (UIImage*)maskImage:(UIImage*)image withMask:(UIImage*)maskImage {
   UIGraphicsBeginImageContext(image.size);
   CGImageRef maskRef = maskImage.CGImage;
   CGImageRef mask = CGImageMaskCreate(CGImageGetWidth(maskRef), CGImageGetHeight(maskRef), CGImageGetBitsPerComponent(maskRef), CGImageGetBitsPerPixel(maskRef), CGImageGetBytesPerRow(maskRef), CGImageGetDataProvider(maskRef), NULL, false);
   CGImageRef masked = CGImageCreateWithMask(image.CGImage, mask);
   UIImage* result = [UIImage imageWithData:UIImagePNGRepresentation(UIGraphicsGetImageFromCurrentImageContext())];
   UIGraphicsEndImageContext();
   CGImageRelease(mask);
   CGImageRelease(masked);
   return result;
}

1
尽管看起来很漂亮,但在我的情况下,它只会使图像完全透明。请参见我上面的更新。我发现了一些奇怪的东西。 - Thanks
1
我使用了上述代码的变体,其中包含在Photoshop中创建的蒙版,转换为“灰度”,并导出为24位PNG。 - Ramin
如果您想使用上述代码,请确保包含蒙版的图像文件是24位PNG,且没有alpha通道。例如,Pixelmator并不会告诉您这个信息。您需要使用Photoshop或Gimp。 - Alex the Ukrainian

0
Dave,我听说PNG图像具有Alpha通道,可以影响这样的代码。
我刚刚谷歌了PNG Alpha通道,并找到了这个链接:链接文本 因此,请确保您使用的PNG具有正确设置的Alpha通道。

-6

使用核心图形创建任何类型的图像时,必须具有Alpha通道。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接