如何在 iPhone 中调色透明的 PNG 图像?

我知道可以通过在矩形图像上绘制 CGContextFillRect 并设置混合模式来着色。但是,我不知道如何在透明图像(如图标)上进行着色。这必须是可能的,因为 SDK 在这样的选项卡酒吧自己做。有人能提供一个片段吗?

更新:

自从我最初提出这个问题以来,已经有很多很好的建议。一定要通读所有的答案,找出最适合你的答案。

更新(二零一五年四月三十日) :

有了 iOS 7.0,我现在只需要做以下事情,就可以满足我最初问题的需要了。但是如果你有更复杂的情况,检查所有的答案。

UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];
42075 次浏览

UIImageView (或任何视图)的背景颜色是 RGBA。颜色中的 alpha 可以做你需要做的事情,而不需要发明新的东西。

在搜索之后,到目前为止我得到的最好的解决方案是使用混合模式和剪辑蒙版的组合来实现透明 PNG 的着色:

CGContextSetBlendMode (context, kCGBlendModeMultiply);


CGContextDrawImage(context, rect, myIconImage.CGImage);


CGContextClipToMask(context, rect, myIconImage.CGImage);


CGContextSetFillColorWithColor(context, tintColor);


CGContextFillRect(context, rect);

更新: 使用下面的代码为 Swift UIColor 扩展提供了一个 要点


如果你有一个 灰度图像灰度图像和想要 白色变成有色的颜色kCGBlendModeMultiply是走的方式。使用此方法,您不能让高光比着色颜色更亮。

相反,如果你有一个 非灰度图像非灰度图像或者你有 高光 阴影,应该保留,混合模式 kCGBlendModeColor是去的方式。白色将保持白色,黑色将保持黑色,因为轻盈的图像被保留。这种模式只是为着色-它是相同的 Photoshop 的 Color图层混合模式(免责声明: 略有不同的结果可能会发生)。

注意着色 alpha 像素在 iOS 和 Photoshop 中都不能正常工作——半透明的黑色像素不会保持黑色。为了解决这个问题,我更新了下面的答案,花了很长时间才找到答案。

也可以使用混合模式 kCGBlendModeSourceIn/DestinationIn而不是 CGContextClipToMask

如果要创建 UIImage,以下每个代码段都可以用以下代码包围:

UIGraphicsBeginImageContextWithOptions (myIconImage.size, NO, myIconImage.scale); // for correct resolution on retina, thanks @MobileVet
CGContextRef context = UIGraphicsGetCurrentContext();


CGContextTranslateCTM(context, 0, myIconImage.size.height);
CGContextScaleCTM(context, 1.0, -1.0);


CGRect rect = CGRectMake(0, 0, myIconImage.size.width, myIconImage.size.height);


// image drawing code here


UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();

下面是用 kCGBlendModeColor着色透明图像的代码:

// draw black background to preserve color of transparent pixels
CGContextSetBlendMode(context, kCGBlendModeNormal);
[[UIColor blackColor] setFill];
CGContextFillRect(context, rect);


// draw original image
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);


// tint image (loosing alpha) - the luminosity of the original image is preserved
CGContextSetBlendMode(context, kCGBlendModeColor);
[tintColor setFill];
CGContextFillRect(context, rect);


// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

如果你的图像没有半透明像素,你也可以用 kCGBlendModeLuminosity反过来做:

// draw tint color
CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);


// replace luminosity of background (ignoring alpha)
CGContextSetBlendMode(context, kCGBlendModeLuminosity);
CGContextDrawImage(context, rect, myIconImage.CGImage);


// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

如果你不关心亮度,因为你只是得到一个应该用颜色着色的 alpha 通道的图像,你可以用一种更有效的方式:

// draw tint color
CGContextSetBlendMode(context, kCGBlendModeNormal);
[tintColor setFill];
CGContextFillRect(context, rect);


// mask by alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeDestinationIn);
CGContextDrawImage(context, rect, myIconImage.CGImage);

或者反过来:

// draw alpha-mask
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, rect, myIconImage.CGImage);


// draw tint color, preserving alpha values of original image
CGContextSetBlendMode(context, kCGBlendModeSourceIn);
[tintColor setFill];
CGContextFillRect(context, rect);

玩得开心!

不是我的工作,但我成功地使用了这种方法:

Http://coffeeshopped.com/2010/09/iphone-how-to-dynamically-color-a-uiimage

通过使用 kCGBlendModeOverlay,我可以得到非常接近 Apple 导航栏中的色调的结果。在这篇文章 https://stackoverflow.com/a/4684876/229019中,我采用了优秀的@Fabb 答案,并结合了@omz 方法,我得到了这个解决方案,其中包含了我所期待的结果:

- (UIImage *)tintedImageUsingColor:(UIColor *)tintColor;
{
UIGraphicsBeginImageContextWithOptions (self.size, NO, [[UIScreen mainScreen] scale]);


CGContextRef context = UIGraphicsGetCurrentContext();
CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);


// draw original image
[self drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0f];


// tint image (loosing alpha).
// kCGBlendModeOverlay is the closest I was able to match the
// actual process used by apple in navigation bar
CGContextSetBlendMode(context, kCGBlendModeOverlay);
[tintColor setFill];
CGContextFillRect(context, rect);


// mask by alpha values of original image
[self drawInRect:rect blendMode:kCGBlendModeDestinationIn alpha:1.0f];


UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}

下面是一个用透明度给几张灰度图片着色的例子:

Rendered Example:

  • 第一行是有色的苹果工具栏[ UIColor orangeColor ]。
  • 第二行是同样的渐变,以几种颜色开始,以清晰的颜色(= 实际的渐变) ,并以同样的橙色结束。
  • 第三个是一个透明的简单圆圈(亚麻布是背景颜色)
  • 第四行是一个复杂的黑暗嘈杂的纹理

首先,我要感谢法布他的出色的解决方案,帮助我完成我的任务,调色半透明的图标。因为我需要一个 C # (Monotouch)的解决方案,我不得不翻译他的代码。这是我想到的。只要复制粘贴到您的代码,并添加您的半透明图像和您的完成。

所以,所有的学分都归 Fabb,这只是为了启动 C # 用户:)

//TINT COLOR IMAGE
UIImageView iImage = new UIImageView(new RectangleF(12, 14, 24,24));
iImage.ContentMode = UIViewContentMode.ScaleAspectFit;
iImage.Image = _dataItem.Image[0] as UIImage;


UIGraphics.BeginImageContextWithOptions(iImage.Bounds.Size, false, UIScreen.MainScreen.Scale);
CGContext context = UIGraphics.GetCurrentContext();


context.TranslateCTM(0, iImage.Bounds.Size.Height);
context.ScaleCTM(1.0f, -1.0f);


RectangleF rect = new RectangleF(0,0, iImage.Bounds.Width, iImage.Bounds.Height);


// draw black background to preserve color of transparent pixels
context.SetBlendMode(CGBlendMode.Normal);
UIColor.Black.SetFill();
context.FillRect(rect);


// draw original image
context.SetBlendMode(CGBlendMode.Normal);
context.DrawImage(rect, iImage.Image.CGImage);


// tint image (loosing alpha) - the luminosity of the original image is preserved
context.SetBlendMode(CGBlendMode.Color);
UIColor.Orange.SetFill();
context.FillRect(rect);


// mask by alpha values of original image
context.SetBlendMode(CGBlendMode.DestinationIn);
context.DrawImage(rect, iImage.Image.CGImage);


UIImage coloredImage = UIGraphics.GetImageFromCurrentImageContext();
UIGraphics.EndImageContext();


iImage = new UIImageView(coloredImage);
iImage.Frame = new RectangleF(12, 14, 24,24);
//END TINT COLOR IMAGE


cell.Add(iImage);

注意,在 Fabb 公认的答案中,制作 UIImage 的“周围”代码给出了视网膜屏幕上图像的错误分辨率。修复、改变:

UIGraphicsBeginImageContext(myIconImage.size);

致:

UIGraphicsBeginImageContextWithOptions(myIconImage.size, NO, 0.0);

最后一个设置为0.0的参数是 scale,根据苹果的文档:

”如果指定值为0.0,则缩放因子设置为缩放 设备的主屏幕因素”。

没有评论的许可,编辑似乎有点粗鲁,所以我在回答中提到了这一点。 以防有人遇到同样的问题。

我想在自定义的 UIButton 子类中对图像视图进行阴影处理,而其他解决方案没有做到我想要的效果。我需要加深“色彩”的图像颜色。下面介绍如何使用 CoreImage 改变亮度。

Custom buttons with shaded images on button press

  1. 确保将 CoreImage.Framework 添加到项目的库中

  2. 图像阴影方法

    - (UIImage *)shadeImage:(UIImage *)image {
    CIImage *inputImage = [CIImage imageWithCGImage:image.CGImage];
    
    
    CIContext *context = [CIContext contextWithOptions:nil];
    
    
    CIFilter *filter = [CIFilter filterWithName:@"CIColorControls"
    keysAndValues:kCIInputImageKey, inputImage,
    @"inputBrightness", [NSNumber numberWithFloat:-.5], nil];
    CIImage *outputImage = [filter outputImage];
    
    
    CGImageRef cgImage = [context createCGImage:outputImage fromRect:[outputImage extent]];
    UIImage *newImage = [UIImage imageWithCGImage:cgImage scale:image.scale orientation:image.imageOrientation];
    CGImageRelease(cgImage);
    
    
    return newImage;
    }
    
  3. You'll want to store a copy of the context as an ivar, rather than recreate it.

您可以创建一个 UIImage 类别,并像下面这样做:

- (instancetype)tintedImageWithColor:(UIColor *)tintColor {
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0);
CGContextRef context = UIGraphicsGetCurrentContext();


CGRect rect = (CGRect){ CGPointZero, self.size };
CGContextSetBlendMode(context, kCGBlendModeNormal);
[self drawInRect:rect];


CGContextSetBlendMode(context, kCGBlendModeSourceIn);
[tintColor setFill];
CGContextFillRect(context, rect);


UIImage *image  = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return image;
}

在 iOS7中,他们在 UIImageView 中引入了 tintColor 属性,在 UIImage 中引入了 renderingMode 属性

我在这个方法上取得了很大的成功,因为我尝试的其他方法对于某些颜色组合会导致半透明像素的颜色扭曲。这在性能方面也应该更好一些。

+ (UIImage *) imageNamed:(NSString *) name withTintColor: (UIColor *) tintColor {


UIImage *baseImage = [UIImage imageNamed:name];


CGRect drawRect = CGRectMake(0, 0, baseImage.size.width, baseImage.size.height);


UIGraphicsBeginImageContextWithOptions(baseImage.size, NO, 0);
CGContextRef context = UIGraphicsGetCurrentContext();


CGContextTranslateCTM(context, 0, baseImage.size.height);
CGContextScaleCTM(context, 1.0, -1.0);


// draw original image
CGContextSetBlendMode(context, kCGBlendModeNormal);
CGContextDrawImage(context, drawRect, baseImage.CGImage);


// draw color atop
CGContextSetFillColorWithColor(context, tintColor.CGColor);
CGContextSetBlendMode(context, kCGBlendModeSourceAtop);
CGContextFillRect(context, drawRect);


UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();


return tintedImage;
}

在 iOS 7.0中,你也可以这样做来给基本的 UIImageView 着色:

UIImage *iconImage = [[UIImage imageNamed:@"myImageName"] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
UIImageView *icon = [[UIImageView alloc] initWithImage:iconImage];
icon.tintColor = [UIColor redColor];

没有答案可以帮助我解决堆栈溢出问题: 我们的设计师用不同的表单、不同的 alpha 值(和“ alpha-hole”)绘制 UI 元素。在大多数情况下,这是32位 PNG 文件与 alpha 通道,其中包括黑色和白色像素(所有可能的强度)。在给这样一张照片着色之后,我不得不得到这样一个着色的结果: 白色像素越来越多,而暗色像素越来越少。所有这些都是从阿尔法频道来看的。我为我的 UIImage 类别编写了这个方法。也许它的效率不高,但它像时钟一样工作:)在这里:

- (UIImage *)imageTintedWithColor:(UIColor *)color {
UIGraphicsBeginImageContextWithOptions(self.size, NO, self.scale);
CGContextRef context = UIGraphicsGetCurrentContext();


CGRect rect = CGRectMake(0, 0, self.size.width, self.size.height);


CGContextSetBlendMode(context, kCGBlendModeCopy);
[color setFill];
CGContextFillRect(context, rect);


[self drawInRect:rect blendMode:kCGBlendModeXOR alpha:1.0];


CGContextSetBlendMode(context, kCGBlendModeXOR);
CGContextFillRect(context, rect);


[self drawInRect:rect blendMode:kCGBlendModeMultiply alpha:1.0];


UIImage *coloredImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();


return coloredImage;
}