用blend修改图片的颜色

现在存在的问题

如果一个APP中可以根据用户喜好,更改APP中图片的颜色,则设计师必须重复修改图片,开发者图片文件重命名、移动和导入无用功较多,下载安装包较大。

解决方案

用blending给这张图片加上另一个纯色作为tint,并保持原来的alpha通道,然后结合Core Graphics。步骤如下:

1.创建一个上下文用以画新的图片
2.将新的tintColor设置为填充颜色
3.将原图片画在创建的上下文中,并用新的填充色着色(注意保持alpha通道)
4.从当前上下文中取得图片并返回

代码示例

UIImage+tint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#import "UIImage+tint.h"
@implementation UIImage (tint)
- (UIImage *)imageWithTintColor:(UIColor *)tintColor blendMode:(CGBlendMode)blendMode
{
UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f);
[tintColor setFill];//填充颜色
CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height);
UIRectFill(bounds);
//设置绘画透明混合模式和透明度
[self drawInRect:bounds blendMode:blendMode alpha:1.0f];
if (blendMode == kCGBlendModeOverlay) {
//保留透明度信息
[self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f];
}
UIImage * tintedImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return tintedImage;
}
@end

ViewController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#import "ViewController.h"
#import "UIImage+tint.h"
@interface ViewController ()
@property (nonatomic, strong) UIImageView * imageView;
@property (nonatomic, strong) UILabel * msgLabel;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self createUI];
}
- (void)createUI
{
_imageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"image.png"]];
_imageView.center = self.view.center;
_imageView.userInteractionEnabled = YES;
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(changeBlendMode:)];
tap.numberOfTouchesRequired = 1;
tap.numberOfTapsRequired = 1;
[_imageView addGestureRecognizer:tap];
[self.view addSubview:_imageView];
_msgLabel = [[UILabel alloc] initWithFrame:CGRectMake(20, CGRectGetMaxY(_imageView.frame) + 20, CGRectGetWidth(self.view.frame) - 40, 120)];
_msgLabel.numberOfLines = 0;
_msgLabel.text = @"在屏幕中绘图时设置透明度;点击图片切换为混合模式绘制的图片";
_msgLabel.textAlignment = NSTextAlignmentCenter;
_msgLabel.textColor = [UIColor blackColor];
_msgLabel.layer.borderColor = [UIColor grayColor].CGColor;
_msgLabel.layer.borderWidth = 1.0;
[self.view addSubview:_msgLabel];
}
- (void)changeBlendMode:(UIGestureRecognizer*)gesture
{
static CGBlendMode blendMode = kCGBlendModeNormal;
NSString *strMsg;
switch (blendMode) {
case kCGBlendModeNormal:
strMsg = @"kCGBlendModeNormal:正常;也是默认的模式。前景图会覆盖背景图";
break;
case kCGBlendModeMultiply:
strMsg = @"kCGBlendModeMultiply:正片叠底;混合了前景和背景的颜色,最终颜色比原来的都暗";
break;
case kCGBlendModeScreen:
strMsg = @"kCGBlendModeScreen:滤色;把前景和背景图的颜色先反过来,然后混合";
break;
case kCGBlendModeOverlay:
strMsg = @"kCGBlendModeOverlay:覆盖:能保留灰度信息,结合kCGBlendModeDestinationIn能保留透明度信息,在imageWithBlendMode方法中两次执行drawInRect方法实现我们的基本需求";
break;
case kCGBlendModeDarken:
strMsg = @"kCGBlendModeDarken:变暗";
break;
case kCGBlendModeLighten:
strMsg = @"kCGBlendModeLighten:变亮";
break;
case kCGBlendModeColorDodge:
strMsg = @"kCGBlendModeColorDodge:颜色变淡";
break;
case kCGBlendModeColorBurn:
strMsg = @"kCGBlendModeColorBurn:颜色加深";
break;
case kCGBlendModeHardLight:
strMsg = @"kCGBlendModeHardLight:强光";
break;
case kCGBlendModeSoftLight:
strMsg = @"kCGBlendModeSoftLight:柔光";
break;
case kCGBlendModeDifference:
strMsg = @"kCGBlendModeDifference:插值";
break;
case kCGBlendModeExclusion:
strMsg = @"kCGBlendModeExclusion:排除";
break;
case kCGBlendModeHue:
strMsg = @"kCGBlendModeHue:色调";
break;
case kCGBlendModeSaturation:
strMsg = @"kCGBlendModeSaturation:饱和度";
break;
case kCGBlendModeColor:
strMsg = @"kCGBlendModeColor:颜色";
break;
case kCGBlendModeLuminosity:
strMsg = @"kCGBlendModeLuminosity:亮度";
break;
//Apple额外定义的枚举
//R: premultiplied result, 表示混合结果
//S: Source, 表示源颜色(Sa对应透明度值: 0.0-1.0)
//D: destination colors with alpha, 表示带透明度的目标颜色(Da对应透明度值: 0.0-1.0)
case kCGBlendModeClear:
strMsg = @"kCGBlendModeClear:R = 0";
break;
case kCGBlendModeCopy:
strMsg = @"kCGBlendModeCopy:R = S";
break;
case kCGBlendModeSourceIn:
strMsg = @"kCGBlendModeSourceIn:R = S * Da";
break;
case kCGBlendModeSourceOut:
strMsg = @"kCGBlendModeSourceOut:R = S * (1 - Da)";
break;
case kCGBlendModeSourceAtop:
strMsg = @"kCGBlendModeSourceAtop:R = S * Da + D * (1 - Da)";
break;
case kCGBlendModeDestinationOver:
strMsg = @"kCGBlendModeDestinationOver:R = S * (1 - Da) + D";
break;
case kCGBlendModeDestinationIn:
strMsg = @"kCGBlendModeDestinationIn: R = D * Sa;保留透明度信息";
break;
case kCGBlendModeDestinationOut:
strMsg = @"kCGBlendModeDestinationOut: R = D * (1 - Sa)";
break;
case kCGBlendModeDestinationAtop:
strMsg = @"kCGBlendModeDestinationAtop: R = S * (1 - Da) + D * Sa";
break;
case kCGBlendModeXOR:
strMsg = @"kCGBlendModeXOR: R = S * (1 - Da) + D * (1 - Sa)";
break;
case kCGBlendModePlusDarker:
strMsg = @"KCGBlendModePlusDarker: R = MAX(0, (1 - D) + (1 - S))";
break;
case kCGBlendModePlusLighter:
strMsg = @"kCGBlendModePlusLighter: R = MIN(1, S + D)(最后一种混合模式)";
break;
default:
break;
}
_imageView.image = [[UIImage imageNamed:@"image.png"] imageWithTintColor:[UIColor orangeColor] blendMode:blendMode];
_msgLabel.text = strMsg;
blendMode ++;
if (blendMode > kCGBlendModePlusLighter) {
blendMode = kCGBlendModeNormal;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
@end

注:用kCGBlendModeOverlay能保留灰度信息,用kCGBlendModeDestinationIn能保留透明度信息。
因为每次使用UIImage+tint的方法绘图时,都使用了CG的绘制方法,这就意味着每次调用都会是用到CPU的Offscreen drawing,大量使用的话可能导致性能的问题。对于这里的UIImage+tint的实现,可以写一套缓存的机制,来确保大量重复的元素只在load的时候blend一次,之后将其缓存在内存中以快速读取。这是一个权衡的问题,在时间和空间中做出正确的平衡和选择是程序设计的乐趣所在。

参考链接
iOS中使用blend改变图片颜色