使用blend改变图片颜色

2024-03-24 08:40

本文主要是介绍使用blend改变图片颜色,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近对Core AnimationCore Graphics的内容东西比较感兴趣,自己之前也在这块相对薄弱,趁此机会也想补习一下这块的内容,所以之后几篇可能都会是对CA和CG学习的记录的文章。

在应用里一个很常见的需求是主题变换:同样的图标,同样的素材,但是需要按照用户喜爱变为不同的颜色。在iOS5和6的SDK里部分标准控件引入了tintColor,来满足个性化界面的需求,但是Apple在这方面还远远做的不够。一是现在用默认控件根本难以做出界面优秀的应用,二是tintColor所覆盖的并不够全面,在很多情况下开发者都无法使用其来完成个性化定义。解决办法是什么?最简单当然是拜托设计师大大出图,想要蓝色主题?那好,开PS盖个蓝色图层出一套蓝色的UI;又想加粉色UI,那好,再出一套粉色的图然后导入Xcode。代码上的话根据颜色需求使用image-blue或者image-pink这样的名字来加载图片。

如果有一丁点重构的意识,就会知道这不是一个很好的解决方案。工程中存在大量的冗余和重复(就算你要狡辩这些图片颜色不同不算重复,你也会在内心里知道这种狡辩是多么无力),这是非常致命的。想象一下如果你有10套主题界面,先不论应用的体积会膨胀到多少,光是想做一点修改就会痛苦万分,比如希望改一下某个按钮的形状,很好,设计师大大请重复地修改10遍,并出10套UI,然后一系列的重命名,文件移动和导入…一场灾难。

当然有其他办法,因为说白了就是tint不同的颜色到图片上而已,如果我们能实现改变UIImage的颜色,那我们就只需要一套UI,然后用代码来改变UI的颜色就可以了,生活有木有一下光明起来呀。嗯,让我们先从一张图片开始吧~下面是一张带有alpha通道的图片,原始颜色是纯的灰色(当然什么颜色都可以,只不过我这个人现在暂时比较喜欢灰色而已)。



我们将用blending给这张图片加上另一个纯色作为tint,并保持原来的alpha通道。用Core Graphics来做的话,大概的想法很直接:

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

最麻烦的部分可能就是保持alpha通道了。UIImage的文档中提供了使用blend绘图的方法drawInRect:blendMode:alpha:rectalpha都没什么问题,但是blendMode是个啥玩意儿啊…继续看文档,关于CGBlendMode的文档,里面有一大堆看不懂的枚举值,比如这样:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. kCGBlendModeDestinationOver  
  2. R = S*(1 - Da) + D  
  3. Available in iOS 2.0 and later.  
  4. Declared in CGContext.h.  

完全不懂..直接看之后的Discussion部分:

The blend mode constants introduced in OS X v10.5 represent the Porter-Duff blend modes. The symbols in the equations for these blend modes are:
R is the premultiplied result
S is the source color, and includes alpha
D is the destination color, and includes alpha
Ra, Sa, and Da are the alpha components of R, S, and D

原来如此,R表示结果,S表示包含alpha的原色,D表示包含alpha的目标色,Ra,Sa和Da分别是三个的alpha。明白了这些以后,就可以开始寻找我们所需要的blend模式了。相信你可以和我一样,很快找到这个模式:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. kCGBlendModeDestinationIn  
  2. R = D*Sa  
  3. Available in iOS 2.0 and later.  
  4. Declared in CGContext.h.  

结果 = 目标色和原色透明度的加成,看起来正式所需要的。啦啦啦,还等什么呢,开始动手实现看看对不对吧~

为了以后使用方便,当然是祭出Category,先创建一个UIImage的类别:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  UIImage+Tint.h  
  2.   
  3. #import <UIKit/UIKit.h>  
  4.   
  5. @interface UIImage (Tint)  
  6.   
  7. - (UIImage *) imageWithTintColor:(UIColor *)tintColor;  
  8.   
  9. @end  

暂时先这样,当然我们也可以创建一个类方法直接完成从bundle读取图片然后加tintColor,但是很多时候并不如上面一个实例方法方便(比如想要从非bundle的地方获取图片),这个问题之后再说。那么就按照之前设想的步骤来实现吧:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  UIImage+Tint.m  
  2.   
  3. #import "UIImage+Tint.h"  
  4.   
  5. @implementation UIImage (Tint)  
  6. - (UIImage *) imageWithTintColor:(UIColor *)tintColor  
  7. {  
  8.     //We want to keep alpha, set opaque to NO; Use 0.0f for scale to use the scale factor of the device’s main screen.  
  9.     UIGraphicsBeginImageContextWithOptions(self.sizeNO0.0f);  
  10.     [tintColor setFill];  
  11.     CGRect bounds = CGRectMake(00self.size.widthself.size.height);  
  12.     UIRectFill(bounds);  
  13.   
  14.     //Draw the tinted image in context  
  15.     [self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f];  
  16.   
  17.     UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();  
  18.     UIGraphicsEndImageContext();  
  19.   
  20.     return tintedImage;  
  21. }  
  22. @end  

简单明了,没有任何难点。测试之: [[UIImage imageNamed:@"image"] imageWithTintColor:[UIColor orangeColor]]; ,得到的结果为:


嗯…怎么说呢,虽然tintColor的颜色是变了,但是总觉得怪怪的。仔细对比一下就会发现,原来灰色图里星星和周围的灰度渐变到了橙色的图里好像都消失了:星星整个变成了橙色,周围的一圈漂亮的光晕也没有了,这是神马情况啊…这种图能交差的话那算见鬼了,会被设计和产品打死的吧。对于无渐变的纯色图的图来说直接用上面的方法是没问题的,但是现在除了Metro的大色块以外哪里无灰度渐变的设计啊…检查一下使用的blend,R = D * Sa,恍然大悟,我们虽然保留了原色的透明度,但是却把它的所有的灰度信息弄丢了。怎么办?继续刨CGBlendMode的文档吧,那么多blend模式应该总有我们需要的。功夫不负有心人,kCGBlendModeOverlay一副嗷嗷待选的样子:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. kCGBlendModeOverlay  
  2. Either multiplies or screens the source image samples with the background image samples, depending on the background color. The result is to overlay the existing image samples while preserving the highlights and shadows of the background. The background color mixes with the source image to reflect the lightness or darkness of the background.  
  3. Available in iOS 2.0 and later.  
  4. Declared in CGContext.h.  

kCGBlendModeOverlay可以保持背景色的明暗,也就是灰度信息,听起来正是我们需要的。加入到声明中,并且添加相应的实现( 顺便重构一下原来的代码 :) ):

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  UIImage+Tint.h  
  2.   
  3. #import <UIKit/UIKit.h>  
  4.   
  5. @interface UIImage (Tint)  
  6.   
  7. - (UIImage *) imageWithTintColor:(UIColor *)tintColor;  
  8. - (UIImage *) imageWithGradientTintColor:(UIColor *)tintColor;  
  9.   
  10. @end  

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  UIImage+Tint.m  
  2.   
  3. #import "UIImage+Tint.h"  
  4.   
  5. @implementation UIImage (Tint)  
  6. - (UIImage *) imageWithTintColor:(UIColor *)tintColor  
  7. {  
  8.     return [self imageWithTintColor:tintColor blendMode:kCGBlendModeDestinationIn];  
  9. }  
  10.   
  11. - (UIImage *) imageWithGradientTintColor:(UIColor *)tintColor  
  12. {  
  13.     return [self imageWithTintColor:tintColor blendMode:kCGBlendModeOverlay];  
  14. }  
  15.   
  16. - (UIImage *) imageWithTintColor:(UIColor *)tintColor blendMode:(CGBlendMode)blendMode  
  17. {  
  18.     //We want to keep alpha, set opaque to NO; Use 0.0f for scale to use the scale factor of the device’s main screen.  
  19.     UIGraphicsBeginImageContextWithOptions(self.sizeNO0.0f);  
  20.     [tintColor setFill];  
  21.     CGRect bounds = CGRectMake(00self.size.widthself.size.height);  
  22.     UIRectFill(bounds);  
  23.   
  24.     //Draw the tinted image in context  
  25.     [self drawInRect:bounds blendMode:blendMode alpha:1.0f];  
  26.   
  27.     UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();  
  28.     UIGraphicsEndImageContext();  
  29.   
  30.     return tintedImage;  
  31. }  
  32.   
  33. @end  

完成,测试之…好吧,好尴尬,虽然颜色和周围的光这次对了,但是透明度又没了啊魂淡..一点不奇怪啊,因为 kCGBlendModeOverlay 本来就没承诺给你保留原图的透明度的说。


那么..既然我们用kCGBlendModeOverlay能保留灰度信息,用kCGBlendModeDestinationIn能保留透明度信息,那就两个blendMode都用不就完事儿了么~尝试之,如果在blend绘图时不是kCGBlendModeDestinationIn模式的话,则再用kCGBlendModeDestinationIn画一次:

[objc]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. //  UIImage+Tint.m  
  2.   
  3. #import "UIImage+Tint.h"  
  4.   
  5. @implementation UIImage (Tint)  
  6. - (UIImage *) imageWithTintColor:(UIColor *)tintColor  
  7. {  
  8.     return [self imageWithTintColor:tintColor blendMode:kCGBlendModeDestinationIn];  
  9. }  
  10.   
  11. - (UIImage *) imageWithGradientTintColor:(UIColor *)tintColor  
  12. {  
  13.     return [self imageWithTintColor:tintColor blendMode:kCGBlendModeOverlay];  
  14. }  
  15.   
  16. - (UIImage *) imageWithTintColor:(UIColor *)tintColor blendMode:(CGBlendMode)blendMode  
  17. {  
  18.     //We want to keep alpha, set opaque to NO; Use 0.0f for scale to use the scale factor of the device’s main screen.  
  19.     UIGraphicsBeginImageContextWithOptions(self.sizeNO0.0f);  
  20.     [tintColor setFill];  
  21.     CGRect bounds = CGRectMake(00self.size.widthself.size.height);  
  22.     UIRectFill(bounds);  
  23.   
  24.     //Draw the tinted image in context  
  25.     [self drawInRect:bounds blendMode:blendMode alpha:1.0f];  
  26.   
  27.     if (blendMode != kCGBlendModeDestinationIn) {  
  28.         [self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f];  
  29.     }  
  30.   
  31.     UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext();  
  32.     UIGraphicsEndImageContext();  
  33.   
  34.     return tintedImage;  
  35. }  
  36.   
  37. @end  

结果如下:


已经很完美了,这样的话只要在代码里设定一下颜色,我们就能够很轻易地使用同样一套UI,将其blend为需要的颜色,来实现素材的重用了。唯一需要注意的是,因为每次使用UIImage+Tint的方法绘图时,都使用了CG的绘制方法,这就意味着每次调用都会是用到CPU的Offscreen drawing,大量使用的话可能导致性能的问题(主要对于iPhone 3GS或之前的设备,可能同时处理大量这样的绘制调用的能力会有不足)。关于CA和CG的性能的问题,打算在之后用一篇文章来介绍一下。对于这里的UIImage+Tint的实现,可以写一套缓存的机制,来确保大量重复的元素只在load的时候blend一次,之后将其缓存在内存中以快速读取。当然这是一个权衡的问题,在时间和空间中做出正确的平衡和选择,也正是程序设计的乐趣所在。

这篇文章中作为示例的工程和UIImage+Tint可以在Github上找到,您可以随意玩弄..我相信也会是个来研究每种blend的特性的好机会~


转自:http://onevcat.com/2013/04/using-blending-in-ios/

这篇关于使用blend改变图片颜色的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:https://blog.csdn.net/RIXIAOYUE/article/details/48161289
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/841051

相关文章

mybatis-plus QueryWrapper中or,and的使用及说明

《mybatis-plusQueryWrapper中or,and的使用及说明》使用MyBatisPlusQueryWrapper时,因同时添加角色权限固定条件和多字段模糊查询导致数据异常展示,排查发... 目录QueryWrapper中or,and使用列表中还要同时模糊查询多个字段经过排查这就导致只要whe

Python使用openpyxl读取Excel的操作详解

《Python使用openpyxl读取Excel的操作详解》本文介绍了使用Python的openpyxl库进行Excel文件的创建、读写、数据操作、工作簿与工作表管理,包括创建工作簿、加载工作簿、操作... 目录1 概述1.1 图示1.2 安装第三方库2 工作簿 workbook2.1 创建:Workboo

使用Go实现文件复制的完整流程

《使用Go实现文件复制的完整流程》本案例将实现一个实用的文件操作工具:将一个文件的内容完整复制到另一个文件中,这是文件处理中的常见任务,比如配置文件备份、日志迁移、用户上传文件转存等,文中通过代码示例... 目录案例说明涉及China编程知识点示例代码代码解析示例运行练习扩展小结案例说明我们将通过标准库 os

postgresql使用UUID函数的方法

《postgresql使用UUID函数的方法》本文给大家介绍postgresql使用UUID函数的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录PostgreSQL有两种生成uuid的方法。可以先通过sql查看是否已安装扩展函数,和可以安装的扩展函数

如何使用Lombok进行spring 注入

《如何使用Lombok进行spring注入》本文介绍如何用Lombok简化Spring注入,推荐优先使用setter注入,通过注解自动生成getter/setter及构造器,减少冗余代码,提升开发效... Lombok为了开发环境简化代码,好处不用多说。spring 注入方式为2种,构造器注入和setter

MySQL中比较运算符的具体使用

《MySQL中比较运算符的具体使用》本文介绍了SQL中常用的符号类型和非符号类型运算符,符号类型运算符包括等于(=)、安全等于(=)、不等于(/!=)、大小比较(,=,,=)等,感兴趣的可以了解一下... 目录符号类型运算符1. 等于运算符=2. 安全等于运算符<=>3. 不等于运算符<>或!=4. 小于运

使用zip4j实现Java中的ZIP文件加密压缩的操作方法

《使用zip4j实现Java中的ZIP文件加密压缩的操作方法》本文介绍如何通过Maven集成zip4j1.3.2库创建带密码保护的ZIP文件,涵盖依赖配置、代码示例及加密原理,确保数据安全性,感兴趣的... 目录1. zip4j库介绍和版本1.1 zip4j库概述1.2 zip4j的版本演变1.3 zip4

Python 字典 (Dictionary)使用详解

《Python字典(Dictionary)使用详解》字典是python中最重要,最常用的数据结构之一,它提供了高效的键值对存储和查找能力,:本文主要介绍Python字典(Dictionary)... 目录字典1.基本特性2.创建字典3.访问元素4.修改字典5.删除元素6.字典遍历7.字典的高级特性默认字典

使用Python构建一个高效的日志处理系统

《使用Python构建一个高效的日志处理系统》这篇文章主要为大家详细讲解了如何使用Python开发一个专业的日志分析工具,能够自动化处理、分析和可视化各类日志文件,大幅提升运维效率,需要的可以了解下... 目录环境准备工具功能概述完整代码实现代码深度解析1. 类设计与初始化2. 日志解析核心逻辑3. 文件处

一文详解如何使用Java获取PDF页面信息

《一文详解如何使用Java获取PDF页面信息》了解PDF页面属性是我们在处理文档、内容提取、打印设置或页面重组等任务时不可或缺的一环,下面我们就来看看如何使用Java语言获取这些信息吧... 目录引言一、安装和引入PDF处理库引入依赖二、获取 PDF 页数三、获取页面尺寸(宽高)四、获取页面旋转角度五、判断