iOS中使用blend改變圖片顏色

原文鏈接:https://onevcat.com/2013/04/using-blending-in-ios/

使用Blend處理圖片顏色

最近對(duì)Core Animation
和Core Graphics
的內(nèi)容東西比較感興趣衣摩,自己之前也在這塊相對(duì)薄弱昂验,趁此機(jī)會(huì)也想補(bǔ)習(xí)一下這塊的內(nèi)容,所以之后幾篇可能都會(huì)是對(duì)CA和CG學(xué)習(xí)的記錄的文章艾扮。
在應(yīng)用里一個(gè)很常見(jiàn)的需求是主題變換:同樣的圖標(biāo)既琴,同樣的素材,但是需要按照用戶喜愛(ài)變?yōu)椴煌念伾葑臁T趇OS5和6的SDK里部分標(biāo)準(zhǔn)控件引入了tintColor
甫恩,來(lái)滿足個(gè)性化界面的需求,但是Apple在這方面還遠(yuǎn)遠(yuǎn)做的不夠酌予。一是現(xiàn)在用默認(rèn)控件根本難以做出界面優(yōu)秀的應(yīng)用磺箕,二是tintColor
所覆蓋的并不夠全面,在很多情況下開(kāi)發(fā)者都無(wú)法使用其來(lái)完成個(gè)性化定義抛虫。解決辦法是什么松靡?最簡(jiǎn)單當(dāng)然是拜托設(shè)計(jì)師大大出圖,想要藍(lán)色主題建椰?那好击困,開(kāi)PS蓋個(gè)藍(lán)色圖層出一套藍(lán)色的UI;又想加粉色UI广凸,那好阅茶,再出一套粉色的圖然后導(dǎo)入Xcode。代碼上的話根據(jù)顏色需求使用image-blue或者image-pink這樣的名字來(lái)加載圖片谅海。
如果有一丁點(diǎn)重構(gòu)的意識(shí)脸哀,就會(huì)知道這不是一個(gè)很好的解決方案。工程中存在大量的冗余和重復(fù)(就算你要狡辯這些圖片顏色不同不算重復(fù)扭吁,你也會(huì)在內(nèi)心里知道這種狡辯是多么無(wú)力)撞蜂,這是非常致命的。想象一下如果你有10套主題界面侥袜,先不論應(yīng)用的體積會(huì)膨脹到多少蝌诡,光是想做一點(diǎn)修改就會(huì)痛苦萬(wàn)分,比如希望改一下某個(gè)按鈕的形狀枫吧,很好浦旱,設(shè)計(jì)師大大請(qǐng)重復(fù)地修改10遍,并出10套UI九杂,然后一系列的重命名颁湖,文件移動(dòng)和導(dǎo)入…一場(chǎng)災(zāi)難宣蠕。
當(dāng)然有其他辦法,因?yàn)檎f(shuō)白了就是tint不同的顏色到圖片上而已甥捺,如果我們能實(shí)現(xiàn)改變UIImage的顏色抢蚀,那我們就只需要一套UI,然后用代碼來(lái)改變UI的顏色就可以了镰禾,生活有木有一下光明起來(lái)呀皿曲。嗯,讓我們先從一張圖片開(kāi)始吧~下面是一張帶有alpha通道的圖片吴侦,原始顏色是純的灰色(當(dāng)然什么顏色都可以屋休,只不過(guò)我這個(gè)人現(xiàn)在暫時(shí)比較喜歡灰色而已)。
[圖片上傳中妈倔。博投。绸贡。(2)]
我們將用blending給這張圖片加上另一個(gè)純色作為tint盯蝴,并保持原來(lái)的alpha通道。用Core Graphics來(lái)做的話听怕,大概的想法很直接:
創(chuàng)建一個(gè)上下文用以畫(huà)新的圖片
將新的tintColor設(shè)置為填充顏色
將原圖片畫(huà)在創(chuàng)建的上下文中捧挺,并用新的填充色著色(注意保持alpha通道)
從當(dāng)前上下文中取得圖片并返回

最麻煩的部分可能就是保持alpha通道了。UIImage的文檔中提供了使用blend繪圖的方法drawInRect:blendMode:alpha:
尿瞭,rect
和alpha
都沒(méi)什么問(wèn)題闽烙,但是blendMode
是個(gè)啥玩意兒啊…繼續(xù)看文檔,關(guān)于CGBlendMode
的文檔
声搁,里面有一大堆看不懂的枚舉值黑竞,比如這樣:
kCGBlendModeDestinationOverR = S*(1 - Da) + DAvailable in iOS 2.0 and later.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 resultS is the source color, and includes alphaD is the destination color, and includes alphaRa, Sa, and Da are the alpha components of R, S, and D

原來(lái)如此,R表示結(jié)果疏旨,S表示包含alpha的原色很魂,D表示包含alpha的目標(biāo)色,Ra檐涝,Sa和Da分別是三個(gè)的alpha遏匆。明白了這些以后,就可以開(kāi)始尋找我們所需要的blend模式了谁榜。相信你可以和我一樣幅聘,很快找到這個(gè)模式:
kCGBlendModeDestinationInR = D*SaAvailable in iOS 2.0 and later.Declared in CGContext.h.

結(jié)果 = 目標(biāo)色和原色透明度的加成,看起來(lái)正式所需要的窃植。啦啦啦帝蒿,還等什么呢,開(kāi)始動(dòng)手實(shí)現(xiàn)看看對(duì)不對(duì)吧~
為了以后使用方便巷怜,當(dāng)然是祭出Category陵叽,先創(chuàng)建一個(gè)UIImage的類別:
// UIImage+Tint.h#import <UIKit/UIKit.h>@interface UIImage (Tint)- (UIImage *) imageWithTintColor:(UIColor *)tintColor;@end

暫時(shí)先這樣狞尔,當(dāng)然我們也可以創(chuàng)建一個(gè)類方法直接完成從bundle讀取圖片然后加tintColor,但是很多時(shí)候并不如上面一個(gè)實(shí)例方法方便(比如想要從非bundle的地方獲取圖片)巩掺,這個(gè)問(wèn)題之后再說(shuō)偏序。那么就按照之前設(shè)想的步驟來(lái)實(shí)現(xiàn)吧:
// UIImage+Tint.m#import "UIImage+Tint.h"@implementation UIImage (Tint)- (UIImage *) imageWithTintColor:(UIColor *)tintColor{ //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. UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); [tintColor setFill]; CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height); UIRectFill(bounds); //Draw the tinted image in context [self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f]; UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return tintedImage;}@end

簡(jiǎn)單明了,沒(méi)有任何難點(diǎn)胖替。測(cè)試之:[[UIImage imageNamed:@"image"] imageWithTintColor:[UIColor orangeColor]];
研儒,得到的結(jié)果為:


使用kCGBlendModeDestinationIn模式的結(jié)果

嗯…怎么說(shuō)呢,雖然tintColor的顏色是變了独令,但是總覺(jué)得怪怪的端朵。仔細(xì)對(duì)比一下就會(huì)發(fā)現(xiàn),原來(lái)灰色圖里星星和周?chē)幕叶葷u變到了橙色的圖里好像都消失了:星星整個(gè)變成了橙色燃箭,周?chē)囊蝗ζ恋墓鈺炓矝](méi)有了冲呢,這是神馬情況啊…這種圖能交差的話那算見(jiàn)鬼了,會(huì)被設(shè)計(jì)和產(chǎn)品打死的吧招狸。對(duì)于無(wú)漸變的純色圖的圖來(lái)說(shuō)直接用上面的方法是沒(méi)問(wèn)題的敬拓,但是現(xiàn)在除了Metro的大色塊以外哪里無(wú)灰度漸變的設(shè)計(jì)啊…檢查一下使用的blend,R = D * Sa
裙戏,恍然大悟乘凸,我們雖然保留了原色的透明度,但是卻把它的所有的灰度信息弄丟了累榜。怎么辦营勤?繼續(xù)刨CGBlendMode
的文檔吧,那么多blend模式應(yīng)該總有我們需要的壹罚。功夫不負(fù)有心人葛作,kCGBlendModeOverlay
一副嗷嗷待選的樣子:
kCGBlendModeOverlayEither 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.Available in iOS 2.0 and later.Declared in CGContext.h.

kCGBlendModeOverlay可以保持背景色的明暗,也就是灰度信息猖凛,聽(tīng)起來(lái)正是我們需要的赂蠢。加入到聲明中,并且添加相應(yīng)的實(shí)現(xiàn)( 順便重構(gòu)一下原來(lái)的代碼 :) ):
// UIImage+Tint.h#import <UIKit/UIKit.h>@interface UIImage (Tint)- (UIImage *) imageWithTintColor:(UIColor *)tintColor;- (UIImage *) imageWithGradientTintColor:(UIColor *)tintColor;@end

// UIImage+Tint.m#import "UIImage+Tint.h"@implementation UIImage (Tint)- (UIImage *) imageWithTintColor:(UIColor *)tintColor{ return [self imageWithTintColor:tintColor blendMode:kCGBlendModeDestinationIn];}- (UIImage *) imageWithGradientTintColor:(UIColor *)tintColor{ return [self imageWithTintColor:tintColor blendMode:kCGBlendModeOverlay];}- (UIImage *) imageWithTintColor:(UIColor *)tintColor blendMode:(CGBlendMode)blendMode{ //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. UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); [tintColor setFill]; CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height); UIRectFill(bounds); //Draw the tinted image in context [self drawInRect:bounds blendMode:blendMode alpha:1.0f]; UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return tintedImage;}@end

完成形病,測(cè)試之…好吧客年,好尷尬,雖然顏色和周?chē)墓膺@次對(duì)了漠吻,但是透明度又沒(méi)了啊魂淡..一點(diǎn)不奇怪啊量瓜,因?yàn)閗CGBlendModeOverlay
本來(lái)就沒(méi)承諾給你保留原圖的透明度的說(shuō)。


使用kCGBlendModeOverlay模式的結(jié)果

那么..既然我們用kCGBlendModeOverlay
能保留灰度信息途乃,用kCGBlendModeDestinationIn
能保留透明度信息绍傲,那就兩個(gè)blendMode都用不就完事兒了么~嘗試之,如果在blend繪圖時(shí)不是kCGBlendModeDestinationIn
模式的話,則再用kCGBlendModeDestinationIn
畫(huà)一次:
// UIImage+Tint.m#import "UIImage+Tint.h"@implementation UIImage (Tint)- (UIImage *) imageWithTintColor:(UIColor *)tintColor{ return [self imageWithTintColor:tintColor blendMode:kCGBlendModeDestinationIn];}- (UIImage *) imageWithGradientTintColor:(UIColor *)tintColor{ return [self imageWithTintColor:tintColor blendMode:kCGBlendModeOverlay];}- (UIImage *) imageWithTintColor:(UIColor *)tintColor blendMode:(CGBlendMode)blendMode{ //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. UIGraphicsBeginImageContextWithOptions(self.size, NO, 0.0f); [tintColor setFill]; CGRect bounds = CGRectMake(0, 0, self.size.width, self.size.height); UIRectFill(bounds); //Draw the tinted image in context [self drawInRect:bounds blendMode:blendMode alpha:1.0f]; if (blendMode != kCGBlendModeDestinationIn) { [self drawInRect:bounds blendMode:kCGBlendModeDestinationIn alpha:1.0f]; } UIImage *tintedImage = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); return tintedImage;}@end

結(jié)果如下:
[圖片上傳中烫饼。猎塞。。(5)]
已經(jīng)很完美了杠纵,這樣的話只要在代碼里設(shè)定一下顏色荠耽,我們就能夠很輕易地使用同樣一套UI,將其blend為需要的顏色比藻,來(lái)實(shí)現(xiàn)素材的重用了铝量。唯一需要注意的是,因?yàn)槊看问褂肬IImage+Tint
的方法繪圖時(shí)银亲,都使用了CG的繪制方法慢叨,這就意味著每次調(diào)用都會(huì)是用到CPU的Offscreen drawing,大量使用的話可能導(dǎo)致性能的問(wèn)題(主要對(duì)于iPhone 3GS或之前的設(shè)備务蝠,可能同時(shí)處理大量這樣的繪制調(diào)用的能力會(huì)有不足)拍谐。關(guān)于CA和CG的性能的問(wèn)題,打算在之后用一篇文章來(lái)介紹一下馏段。對(duì)于這里的UIImage+Tint
的實(shí)現(xiàn)轩拨,可以寫(xiě)一套緩存的機(jī)制,來(lái)確保大量重復(fù)的元素只在load的時(shí)候blend一次毅弧,之后將其緩存在內(nèi)存中以快速讀取气嫁。當(dāng)然這是一個(gè)權(quán)衡的問(wèn)題当窗,在時(shí)間和空間中做出正確的平衡和選擇够坐,也正是程序設(shè)計(jì)的樂(lè)趣所在。
這篇文章中作為示例的工程和UIImage+Tint可以在Github上找到崖面,您可以隨意玩弄..我相信也會(huì)是個(gè)來(lái)研究每種blend的特性的好機(jī)會(huì)~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末元咙,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子巫员,更是在濱河造成了極大的恐慌庶香,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件简识,死亡現(xiàn)場(chǎng)離奇詭異赶掖,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)七扰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,218評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)奢赂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)承边,“玉大人秦效,你說(shuō)我怎么就攤上這事墨辛〖舾觯” “怎么了蚁飒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,162評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)瑰钮。 經(jīng)常有香客問(wèn)我邻辉,道長(zhǎng),這世上最難降的妖魔是什么毕箍? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,470評(píng)論 1 283
  • 正文 為了忘掉前任弛房,我火速辦了婚禮,結(jié)果婚禮上而柑,老公的妹妹穿的比我還像新娘庭再。我一直安慰自己,他們只是感情好牺堰,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,550評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布拄轻。 她就那樣靜靜地躺著,像睡著了一般伟葫。 火紅的嫁衣襯著肌膚如雪恨搓。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,806評(píng)論 1 290
  • 那天筏养,我揣著相機(jī)與錄音斧抱,去河邊找鬼。 笑死渐溶,一個(gè)胖子當(dāng)著我的面吹牛辉浦,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茎辐,決...
    沈念sama閱讀 38,951評(píng)論 3 407
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼宪郊,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了拖陆?” 一聲冷哼從身側(cè)響起弛槐,我...
    開(kāi)封第一講書(shū)人閱讀 37,712評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎依啰,沒(méi)想到半個(gè)月后乎串,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,166評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡速警,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,510評(píng)論 2 327
  • 正文 我和宋清朗相戀三年叹誉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闷旧。...
    茶點(diǎn)故事閱讀 38,643評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡长豁,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鸠匀,到底是詐尸還是另有隱情蕉斜,我是刑警寧澤,帶...
    沈念sama閱讀 34,306評(píng)論 4 330
  • 正文 年R本政府宣布,位于F島的核電站宅此,受9級(jí)特大地震影響机错,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜父腕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,930評(píng)論 3 313
  • 文/蒙蒙 一弱匪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧璧亮,春花似錦萧诫、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,745評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至群扶,卻和暖如春及刻,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背竞阐。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,983評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工缴饭, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骆莹。 一個(gè)月前我還...
    沈念sama閱讀 46,351評(píng)論 2 360
  • 正文 我出身青樓颗搂,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親幕垦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子丢氢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,509評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容