文章源于視覺哥哥凱凱的需求壳炎。在實(shí)現(xiàn)觸發(fā)特效的時(shí)搁痛,在視覺稿中使用了10% 的蒙板疊加在原色值上,而不是給出混合后的色值长搀,開發(fā)上并不好處理。于是鸡典,我決定憑著我程序員般縝密的思維與邏輯性的口舌一定要說服他源请,這個需求沒有必要做!
來到凱凱座位彻况,經(jīng)過一輪深入淺出的表達(dá)谁尸。凱凱似乎已經(jīng)認(rèn)識到了蒙板對于開發(fā)的不便利性。“這個效果纽甘,是不是做不了良蛮。你如果做不了的話。就用混合色值吧悍赢【鐾” 我轉(zhuǎn)念一想,怎么能說自己不行左权,下午先自己悄悄的看看資料吧皮胡。“不過安卓那邊實(shí)現(xiàn)沒問題啊∩统伲” 凱凱隨后默默的給我補(bǔ)了一刀屡贺。同時(shí)解釋視覺效果一般都是通過多個圖層混疊實(shí)現(xiàn)的。如果能實(shí)現(xiàn)瀑梗,會更利于視覺和開發(fā)統(tǒng)一視覺規(guī)范……
經(jīng)過凱凱的一番說服烹笔,我瞬間認(rèn)識到了這項(xiàng)任務(wù)的重要性和必要性,以及自己身上沉甸甸的使命感抛丽。既然安卓能做谤职,iOS 肯定也能做! 這個需求看來也是很有必要耙谙省允蜈!嗯冤吨?在么好像有哪里不對?
所謂圖層混合模式就是指一個層與其下圖層的色彩疊加方式饶套,在這之前我們所使用的是正常模式漩蟆,除了正常以外,還有很多種混合模式妓蛮,它們都可以產(chǎn)生迥異的合成效果怠李。
打開 Photoshop疊層菜單,可以發(fā)現(xiàn)除去最后幾欄其他都是RBG色值的混合操作蛤克。
從上到下數(shù),除去正常, 幾個分區(qū)可以分為: 變暗型, 變亮型, 溶合型,色差型, 明亮度
其中前四種 變暗型, 變亮型, 溶合型,色差型 均為前后兩個圖層的 RGB 混合捺癞,通過簡單的 RGB 混合公式即可實(shí)現(xiàn)。
為了美觀构挤,我使用外鏈的素材。并且為了對比明顯筋现,圖片只會疊加下方部分70%唐础。而上方30%作為原圖對比。
通用說明
公式指代
d: 混合后的結(jié)果色透明度矾飞。
A: 為上方疊層的色值或透明度一膨。
B:為原圖層色值或透明度,也就是下方的 base 層凰慈。
(因?yàn)橥愋突旌?R汞幢,G,B微谓,Alpha 的圖層混合計(jì)算一致,在各個值得的計(jì)算中输钩。A和 B 均指代為對應(yīng)的色值)如正底疊片公式:
C =A*B
等價(jià)于
Cr =Ar*Br
Cg =Ag*Bg,
Cb =Ab*Bb
為了方便計(jì)算豺型,公式中的數(shù)值均百分比,即 0.0 ~ 1.0 买乃。需要乘上255才是真正的色值姻氨。
需要說明的是,Photoshop 補(bǔ)色的概念即為顏色至飽和的差值剪验。簡單來說肴焊,1-A 即為 A 的補(bǔ)色,1-B 即為 B 的補(bǔ)色功戚。
Opacity 不透明度計(jì)算
C = dA+(1-d)B
A代表了上面圖層像素的色彩值(A=像素值/255)娶眷,d表示該層的透明度,B代表下面圖層像素的色彩值(B=像素值/255)啸臀,C代表了混合像素的色彩值(真實(shí)的結(jié)果像素值應(yīng)該為255*C)届宠。該公式也應(yīng)用于層蒙板,在這種情況下,d代表了蒙板圖層中給定位置像素的亮度豌注,下同伤塌,不再敘述。
效果實(shí)現(xiàn)
第一步: 工具類函數(shù)的準(zhǔn)備
extension UIColor {
// MARK: 處理函數(shù)
func blendProcedure(
coverColor: UIColor,
alpha: CGFloat,
procedureBlock: ((_ baseValue: CGFloat,_ topValue: CGFloat) -> CGFloat)?
) -> UIColor {
let baseCompoment = self.rgbaTuple()
let topCompoment = coverColor.rgbaTuple()
// 該層透明度
let mixAlpha = alpha * topCompoment.a + (1.0 - alpha) * baseCompoment.a
// RGB 值
let mixR = procedureBlock?(
baseCompoment.r / 255.0,
topCompoment.r / 255.0)
?? (baseCompoment.r) / 255.0
let mixG = procedureBlock?(
baseCompoment.g / 255.0,
topCompoment.g / 255.0)
?? (baseCompoment.g) / 255.0
let mixB = procedureBlock?(
baseCompoment.b / 255.0,
topCompoment.b / 255.0)
?? baseCompoment.b / 255.0
return UIColor.init(red: fitIn(mixR),
green: fitIn(mixG),
blue: fitIn(mixB),
alpha: mixAlpha)
}
// 防止越界
func fitIn(_ value: CGFloat, ceil: CGFloat = 255) -> CGFloat { return max(min(value,ceil),0) }
func fitIn(_ value: Double, ceil: CGFloat = 255) -> CGFloat { return fitIn(CGFloat(value), ceil: ceil) }
// 返回 RBGA
func rgbaTuple() -> (r: CGFloat, g: CGFloat, b: CGFloat,a: CGFloat) {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
self.getRed(&r, green: &g, blue: &b, alpha: &a)
r = r * 255
g = g * 255
b = b * 255
return ((r),(g),(b),a)
}
}
第二步:開始實(shí)現(xiàn)
通用型
1.前景疊圖 (Alpha Bend)
如果你發(fā)現(xiàn)視覺稿里沒有標(biāo)注特效類型轧铁,不知道視覺要你用的是什么效果每聪,那肯定就是這個了。別問我為什么 : )
計(jì)算公式:
C = A * A.alpha + B * (1 - B.alpha)
C.alpha = 1.0
代碼實(shí)現(xiàn):
// Alpha Blending 前景色疊圖
func blendAlpha(coverColor: UIColor) -> UIColor {
let c1 = coverColor.rgbaTuple()
let c2 = self.rgbaTuple()
let c1r = CGFloat(c1.r)
let c1g = CGFloat(c1.g)
let c1b = CGFloat(c1.b)
let c2r = CGFloat(c2.r)
let c2g = CGFloat(c2.g)
let c2b = CGFloat(c2.b)
// 前景色疊圖公式
let r = c1r * c1.a + c2r * (1 - c1.a)
let g = c1g * c1.a + c2g * (1 - c1.a)
let b = c1b * c1.a + c2b * (1 - c1.a)
return UIColor.init(red: r/255.0, green: g/255.0, blue: b/255.0, alpha: 1.0)
}
變暗型
2.變暗(Darken)
在該模式下齿风,對混合的兩個圖層相對應(yīng)區(qū)域RGB通道中的顏色亮度值進(jìn)行比較药薯,在混合圖層中,比基色圖層暗的像素保留聂宾,亮的像素用基色圖層中暗的像素替換果善。總的顏色灰度級降低系谐,造成變暗的效果巾陕。
計(jì)算公式:
B<=A,則 C=B纪他。
B>=A鄙煤,則 C=A。
代碼實(shí)現(xiàn):
/// Darken 變暗 B<=A: C=B; B>=A: C=A
func blendDarken(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return ($0 <= $1) ? $0 : $1 }
}
該模式通過比較上下層像素后取相對較暗的像素作為輸出茶袒,注意梯刚,每個不同的顏色通道的像素都是獨(dú)立的進(jìn)行比較,色彩值相對較小的作為輸出結(jié)果薪寓,下層表示疊放次序位于下面的那個圖層亡资,即B。上層表示疊放次序位于上面的那個圖層向叉,即A锥腻。
3.正片疊底(Multiply)
將上下兩層圖層像素顏色的灰度級進(jìn)行乘法計(jì)算,獲得灰度級更低的顏色而成為合成后的顏色母谎,圖層合成后的效果簡單地說是低灰階的像素顯現(xiàn)而高灰階不顯現(xiàn)瘦黑。
計(jì)算公式:
C=A*B
代碼實(shí)現(xiàn):
/// Multiply 正片疊底 C = A*B
func blendMultiply(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return $0 * $1 }
}
該效果將兩層像素的標(biāo)準(zhǔn)色彩值(基于0..1之間)相乘后輸出,其效果可以形容成:兩個幻燈片疊加在一起然后放映奇唤,透射光需要分別通過這兩個幻燈片幸斥,從而被削弱了兩次。
4.顏色加深(Color Burn)
使用這種模式時(shí)咬扇,會加暗圖層的顏色值甲葬,加上的顏色越亮,效果越細(xì)膩冗栗。讓底層的顏色變暗演顾,有點(diǎn)類似于正片疊底供搀,但不同的是,它會根據(jù)疊加的像素顏色相應(yīng)增加對比度钠至。和白色混合沒有效果葛虐。
計(jì)算公式:
C=1-(1-B)/A
代碼實(shí)現(xiàn):
/// Color Burn 顏色加深 C=1-(1-B)/A
func blendColorBurn(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return 1 - (1 - $0) / $1 }
}
該模式如果上層越暗,則下層獲取的光越少棉钧,如果上層為全黑色屿脐,則下層越黑,如果上層為全白色宪卿,則根本不會影響下層的诵。結(jié)果最亮的地方不會高于下層的像素值。
5.線性加深(Linear Burn)
和顏色加深模式一樣佑钾,線性加深模式通過降低亮度西疤,讓底色變暗以反映混合色彩。和白色混合沒有效果休溶。
計(jì)算公式:
C=A+B-1
代碼實(shí)現(xiàn):
/// Linear Burn 線性加深 C=A+B-1
func blendLinearBurn(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return ($1 + $0) - 1.0 }
}
如果上下層的像素值之和小于255代赁,輸出結(jié)果將會是純黑色。如果將上層反相兽掰,結(jié)果將是純粹的數(shù)學(xué)減芭碍。
變亮型
6.變亮(Lighten)
在該模式與變暗模式相反,是對混合的兩個圖層相對應(yīng)區(qū)域RGB通道中的顏色亮度值進(jìn)行比較孽尽,取較高的的像素點(diǎn)為混合之后的顏色恭金,使得總的顏色灰度的亮度升高撞羽,造成變亮的效果。用黑色合成圖像時(shí)無作用满俗,用白色時(shí)則仍為白色器腋。
計(jì)算公式:
B<=A: C=A
B>A: C=B
代碼實(shí)現(xiàn):
/// Lighten 變亮 B>=A: C=B; B<=A: C=A
func blendLighten(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return ($0 >= $1) ? $0 : $1 }
}
該模式是取色彩值較大的(也就是較亮的)作為輸出結(jié)果溶其。
7.濾色(Screen)
它與正片疊底模式相反斗躏,將上下兩層圖層像素顏色的灰度級進(jìn)行乘法計(jì)算柱告,獲得灰度級更高的顏色而成為合成后的顏色,圖層合成后的效果簡單地說是高灰階的像素顯現(xiàn)而低灰階不顯現(xiàn)(即淺色出現(xiàn)婆瓜,深色不出現(xiàn)),產(chǎn)生的圖像更加明亮贡羔。
計(jì)算公式:
C=1-(1-A)*(1-B)
代碼實(shí)現(xiàn):
/// Screen 濾色 C=1-(1-A)*(1-B), 也可以寫成 1-C=(1-A)*(1-B)
func blendScreen(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return 1 - (1 - $1) * (1 - $0) }
}
上下層像素的標(biāo)準(zhǔn)色彩值反相后相乘后輸出廉白,輸出結(jié)果比兩者的像素值都將要亮(就好像兩臺投影機(jī)分別對其中一個圖層進(jìn)行投影后,然后投射到同一個屏幕上)乖寒。如果兩個圖層反相后猴蹂,采用Multiply模式混合,則將和對這兩個圖層采用Screen模式混合后反相的結(jié)果完全一樣楣嘁。
8.顏色減淡(Color Dodge)
使用這種模式時(shí)磅轻,會加亮圖層的顏色值珍逸,加上的顏色越暗,效果越細(xì)膩聋溜。與顏色加深剛好相反谆膳,通過降低對比度,加亮底層顏色來反映混合色彩撮躁。與黑色混合沒有任何效果漱病。
計(jì)算公式:
C=B/(1-A)
代碼實(shí)現(xiàn):
/// Color Dodge 顏色減淡 C=B/(1-A)
func blendColorDodge(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $1 >= 1.0 { return $1 }
else { return min(1.0, $0 / (1 - $1)) }
}
}
該模式下,上層的亮度決定了下層的暴露程度把曼。如果上層越亮杨帽,下層獲取的光越多,也就是越亮嗤军。如果上層是純黑色注盈,也就是沒有亮度,則根本不會影響下層叙赚。如果上層是純白色老客,則下層除了像素為255的地方暴露外,其他地方全部為白色纠俭。結(jié)果最黑的地方不會低于下層的像素值沿量。
9.線性減淡(Linear Dodge)
類似于顏色減淡模式。但是通過增加亮度來使得底層顏色變亮冤荆,以此獲得混合色彩朴则。與黑色混合沒有任何效果。
計(jì)算公式:
C=A+B
代碼實(shí)現(xiàn):
/// Linear Dodge 線性減淡 C=A+B
func blendLinearDodge(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return min(1, $1 + $0) }
}
將上下層的色彩值相加钓简。結(jié)果將更亮乌妒。其中基色與混合色的數(shù)值大于255,系統(tǒng)就默認(rèn)為最大值也就是255外邓。
10.疊加(Overlay)
疊加模式比較復(fù)雜撤蚊,它是根據(jù)基色圖層的色彩來決定混合色圖層的像素是進(jìn)行正片疊底還是進(jìn)行濾色,一般來說损话,發(fā)生變化的都是中間色調(diào)侦啸,高色和暗色區(qū)域基本保持不變。像素是進(jìn)行正片疊底(Multiply)混合還是屏幕(Screen)混合丧枪,取決于基色層顏色光涂。顏色會被混合,但基色層顏色的高光與陰影部分的亮度細(xì)節(jié)就會被保留拧烦。
計(jì)算公式:
B<=0.5: C=2*A*B
B>0.5: C=1-2*(1-A)*(1-B)
代碼實(shí)現(xiàn):
/// Overlay 疊加 B<=0.5: C=2*A*B; B>0.5: C=1-2*(1-A)*(1-B)
func blendOverlay(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $0 <= 0.5 { return 2 * $1 * $0 }
else { return 1 - 2 * (1 - $1) * (1 - $0) }
}
}
依據(jù)下層色彩值的不同,該模式可能是Multiply齐佳,也可能是Screen模式本鸣。上層決定了下層中間色調(diào)偏移的強(qiáng)度。如果上層為0.5,則結(jié)果將完全為下層像素的值饲宛。如果上層比0.5暗,則下層的中間色調(diào)的將向暗地方偏移,如果上層比0.5亮絮重,則下層的中間色調(diào)的將向亮地方偏移青伤。
11.柔光(Soft Light)
將混合色圖層以柔光的方式加到基色圖層狠角,當(dāng)基色圖層的灰階趨于高或低丰歌,則會調(diào)整圖層合成結(jié)果的階調(diào)趨于中間的灰階調(diào),而獲得色彩較為柔和的合成效果偷仿。形成的結(jié)果是:圖像的中亮色調(diào)區(qū)域變得更亮羡玛,暗色區(qū)域變得更暗,圖像反差增大類似于柔光燈的照射圖像的效果让歼。變暗還是提亮畫面顏色谋右,取決于混合層顏色信息补箍。產(chǎn)生的效果類似于為圖像打上一盞散射的聚光燈。如果混合層顏色(光源)亮度高于0.5坑雅,基色層會被照亮(變淡)。如果混合層顏色(光源)亮度低于0.5裹粤,基色層會變暗终蒂,就好像被燒焦了似的。
計(jì)算公式:
A<=0.5: C=(2*A-1)*(B-B*B)+B
A>0.5: C=(2*A-1)*(sqrt(B)-B)+B
代碼實(shí)現(xiàn):
/// Soft Light 柔光 A<=0.5: C=(2*A-1)*(B-B*B)+B; A>0.5: C=(2*A-1)*(sqrt(B)-B)+B
func blendSoftLight(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $1 <= 0.5 { return (2 * $1 - 1) * ($0 - $0 * $0) + $0 }
else { return (2 * $1 - 1)*( sqrt($0) - $0) + $0 }
}
}
該模式類似上層以Gamma值范圍為2.0到0.5的方式來調(diào)制下層的色彩值早龟。結(jié)果將是一個非常柔和的組合。
12.強(qiáng)光(Hard Light)
如果兩層中顏色的灰階是偏向低灰階猜丹,作用與正片疊底模式類似芝加,而當(dāng)偏向高灰階時(shí),則與濾色模式類似射窒。中間階調(diào)作用不明顯藏杖。正片疊底或者是濾色混合基層顏色将塑,取決于混合層顏色。產(chǎn)生的效果就好像為圖像應(yīng)用強(qiáng)烈的聚光燈一樣蝌麸。如果混合層層顏色(光源)亮度高于0.5点寥,圖像就會被照亮,這時(shí)混合方式類似于濾色(Screen)模式来吩。反之敢辩,如果亮度低于0.5,圖像就會變暗弟疆,這時(shí)混合方式就類似于正片疊底(Multiply)模式戚长。該模式能為圖像添加陰影。如果用純黑或者純白來進(jìn)行混合兽间,得到的也將是純黑或者純白历葛。
計(jì)算公式:
A<=0.5: C=2*A*B
A>0.5: C=1-2*(1-A)*(1-B)
代碼實(shí)現(xiàn):
/// Hard Light 強(qiáng)光 A<=0.5: C=2*A*B; A>0.5: C=1-2*(1-A)*(1-B)
func blendHardLight(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $1 <= 0.5 { return 2 * $1 * $0 }
else { return 1 - 2 * (1 - $1) * (1 - $0) }
}
}
該模式完全相對應(yīng)于Overlay模式下,兩個圖層進(jìn)行次序交換的情況嘀略。如過上層的顏色高于0.5恤溶,則下層越亮,反之越暗帜羊。
13.亮光(Vivid Light)
調(diào)整對比度以加深或減淡顏色咒程,取決于混合層圖像的顏色分布。如果混合層顏色(光源)亮度高于0.5讼育,圖像將被降低對比度并且變亮;如果混合層顏色(光源)亮度低于0.5帐姻,圖像會被提高對比度并且變暗。
計(jì)算公式:
A<=0.5: C=1-(1-B)/2*A
A>0.5: C=B/(2*(1-A))
代碼實(shí)現(xiàn):
/// Vivid Light 亮光 A<=0.5: C=1-(1-B)/(2*A); A>0.5: C=B/(2*(1-A))
func blendVividLight(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $1 <= 0.5 { return self.fitIn((1 - (1 - $0) / (2 * $1)), ceil: 1.0) }
else { return self.fitIn($0 / (2 * (1 - $1)), ceil: 1.0) }
}
}
該模式非常強(qiáng)烈的增加了對比度奶段,特別是在高亮和陰暗處饥瓷。可以認(rèn)為是陰暗處應(yīng)用Color Burn和高亮處應(yīng)用Color Dodge痹籍。
14.線性光(Linear Light)
線性光通過減少或增加亮度呢铆,來使顏色加深或減淡。具體取決于混合色的數(shù)值蹲缠。如果混合層顏色(光源)亮度高于0.5棺克,則用增加亮度的方法來使得畫面變亮,反之用降低亮度的方法來使畫面變暗线定。
計(jì)算公式:
C=B+2*A-1
代碼實(shí)現(xiàn):
/// Linear Light 線性光 C=B+2*A-1
func blendLinearLight(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return self.fitIn($0 + 2 * $1 - 1, ceil: 1.0) }
}
相對于前一種模式而言娜谊,該模式增加的對比度要弱些。其類似于Linear Burn,只不過是加深了上層的影響力斤讥。
15.點(diǎn)光(Pin Light)
點(diǎn)光模式她會根據(jù)混合色的顏色數(shù)值替換相應(yīng)的顏色纱皆。如果混合層顏色(光源)亮度高于0.5,比混合層顏色暗的像素將會被取代,而較之亮的像素則不發(fā)生變化抹剩。如果混合層顏色(光源)亮度低于0.5撑帖,比混合層顏色亮的像素會被取代,而較之暗的像素則不發(fā)生變化澳眷。
計(jì)算公式:
B<2*A-1: C=2*A-12*A-1<B<2*A: C=B
B>2*A: C=2*A
代碼實(shí)現(xiàn):
/// Pin Light 點(diǎn)光
/// B<2*A-1: C=2*A-1
/// 2*A-1<B<2*A: C=B
/// B>2*A: C=2*A
func blendPinLight(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $0 <= 2 * $1 - 1 { return 2 * $1 - 1 }
else if (2 * $1 - 1 < $0) && ($0 < 2 * $1) { return $0}
else { return 2 * $1 }
}
}
該模式結(jié)果就是導(dǎo)致中間調(diào)幾乎是不變的下層,但是兩邊是Darken和Lighte年模式的組合蛉艾。
16.實(shí)色混合(Hard Mix)
實(shí)色混合是把混合色顏色中的紅钳踊、綠、藍(lán)通道數(shù)值勿侯,添加到基色的RGB值中拓瞪。結(jié)果色的R、G助琐、B通道的數(shù)值只能是255或0祭埂。因此結(jié)構(gòu)色只有一下八種可能:紅、綠兵钮、藍(lán)蛆橡、黃、青掘譬、洋紅泰演、白、黑葱轩。由此看以看出結(jié)果色是非常純的顏色睦焕。
計(jì)算公式:
A<1-B: C=0
A>1-B: C=1
代碼實(shí)現(xiàn):
/// Hard Mix 實(shí)色混合A<1-B: C=0; A>1-B: C=1
func blendHardMix(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $1 <= 1 - $0 { return 0 }
else { return 1 }
}
}
該模式導(dǎo)致了最終結(jié)果僅包含6種基本顏色,每個通道要么就是0靴拱,要么就是255垃喊。
差值型
17.差值(Difference)
將要混合圖層雙方的RGB值中每個值分別進(jìn)行比較,用高值減去低值作為合成后的顏色袜炕。所以這種模式也常使用本谜,白色與任何顏色混合得到反相色,黑色與任何顏色混合顏色不變妇蛀。
計(jì)算公式:
C=|A-B|
代碼實(shí)現(xiàn):
/// Difference 差值 C=|A-B|
func blendDifference(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { fabs($1 - $0) }
}
上下層色調(diào)的絕對值耕突。該模式主要用于比較兩個不同版本的圖片。如果兩者完全一樣评架,則結(jié)果為全黑
18.排除(Exclusion)
排除于差值的作用類似眷茁,只是排除模式的結(jié)果色對比度沒有差值模式強(qiáng)。白色與基色混合得到基色補(bǔ)色纵诞,黑色與基色混合得到基色上祈。
計(jì)算公式:
C=A+B-2*A*B
代碼實(shí)現(xiàn):
/// Exclusion 排除 C = A+B-2*A*B
func blendExclusion(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { $1 + $0 - 2 * $1 * $0 }
}
亮的圖片區(qū)域?qū)?dǎo)致另一層的反相,很暗的區(qū)域則將導(dǎo)致另一層完全沒有改變。
19.減去(Subtract)
減去模式的作用是查看各通道的顏色信息登刺,并從基色中減去混合色籽腕。如果出現(xiàn)負(fù)數(shù)就歸為零。與基色相同的顏色混合得到黑色;白色與基色混合得到黑色;黑色與基色混合得到基色纸俭。
計(jì)算公式:
C=A-B
代碼實(shí)現(xiàn):
/// 減去 C=A-B
func blendMinus(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { $1 - $0 }
}
20.劃分(Divide)
劃分模式的作用是查看每個通道的顏色信息皇耗,并用基色分割混合色∽岷埽基色數(shù)值大于或等于混合色數(shù)值郎楼,混合出的顏色為白色≈匣冢基色數(shù)值小于混合色呜袁,結(jié)果色比基色更暗。因此結(jié)果色對比非常強(qiáng)简珠。白色與基色混合得到基色阶界,黑色與基色混合得到白色。
計(jì)算公式:
C=A/B
代碼實(shí)現(xiàn):
/// 劃分 C=A/B
func blendDivision(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $0 == 0{
return 1.0
}else {
return self.fitIn($1 / $0, ceil: 1.0)
}
}
}
結(jié)尾
附上 github https://github.com/Orange-W/PhotoshopBending
源碼聋庵,和總文件:
//
// UIColor+Combine.swift
// iScales
//
// Created by OrangeEvan on 2017/10/26.
// Copyright ? 2017年 NetEase. All rights reserved.
//
import Foundation
import UIKit
extension UIColor {
// MARK: - 常用疊圖
// Alpha Blending 前景色疊圖
func blendAlpha(coverColor: UIColor) -> UIColor {
let c1 = coverColor.rgbaTuple()
let c2 = self.rgbaTuple()
let c1r = CGFloat(c1.r)
let c1g = CGFloat(c1.g)
let c1b = CGFloat(c1.b)
let c2r = CGFloat(c2.r)
let c2g = CGFloat(c2.g)
let c2b = CGFloat(c2.b)
// 前景色疊圖公式
let r = c1r * c1.a + c2r * (1 - c1.a)
let g = c1g * c1.a + c2g * (1 - c1.a)
let b = c1b * c1.a + c2b * (1 - c1.a)
return UIColor.init(red: r/255.0, green: g/255.0, blue: b/255.0, alpha: 1.0)
}
// MARK: - 去亮度型
/// Darken 變暗 B<=A: C=B; B>=A: C=A
func blendDarken(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return ($0 <= $1) ? $0 : $1 }
}
/// Multiply 正片疊底 C = A*B
func blendMultiply(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return $0 * $1 }
}
/// Color Burn 顏色加深 C=1-(1-B)/A
func blendColorBurn(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return 1 - (1 - $0) / $1 }
}
/// Linear Burn 線性加深 C=A+B-1
func blendLinearBurn(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return ($1 + $0) - 1.0 }
}
// MARK: - 去暗型
/// Lighten 變亮 B>=A: C=B; B<=A: C=A
func blendLighten(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return ($0 >= $1) ? $0 : $1 }
}
/// Screen 濾色 C=1-(1-A)*(1-B), 也可以寫成 1-C=(1-A)*(1-B)
func blendScreen(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return 1 - (1 - $1) * (1 - $0) }
}
/// Color Dodge 顏色減淡 C=B/(1-A)
func blendColorDodge(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $1 >= 1.0 { return $1 }
else { return min(1.0, $0 / (1 - $1)) }
}
}
/// Linear Dodge 線性減淡 C=A+B
func blendLinearDodge(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return min(1, $1 + $0) }
}
// MARK: - 溶合型
/// Overlay 疊加 B<=0.5: C=2*A*B; B>0.5: C=1-2*(1-A)*(1-B)
func blendOverlay(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $0 <= 0.5 { return 2 * $1 * $0 }
else { return 1 - 2 * (1 - $1) * (1 - $0) }
}
}
/// Soft Light 柔光 A<=0.5: C=(2*A-1)*(B-B*B)+B; A>0.5: C=(2*A-1)*(sqrt(B)-B)+B
func blendSoftLight(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $1 <= 0.5 { return (2 * $1 - 1) * ($0 - $0 * $0) + $0 }
else { return (2 * $1 - 1)*( sqrt($0) - $0) + $0 }
}
}
/// Hard Light 強(qiáng)光 A<=0.5: C=2*A*B; A>0.5: C=1-2*(1-A)*(1-B)
func blendHardLight(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $1 <= 0.5 { return 2 * $1 * $0 }
else { return 1 - 2 * (1 - $1) * (1 - $0) }
}
}
/// Vivid Light 亮光 A<=0.5: C=1-(1-B)/(2*A); A>0.5: C=B/(2*(1-A))
func blendVividLight(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $1 <= 0.5 { return self.fitIn((1 - (1 - $0) / (2 * $1)), ceil: 1.0) }
else { return self.fitIn($0 / (2 * (1 - $1)), ceil: 1.0) }
}
}
/// Linear Light 線性光 C=B+2*A-1
func blendLinearLight(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { return self.fitIn($0 + 2 * $1 - 1, ceil: 1.0) }
}
/// Pin Light 點(diǎn)光
/// B<2*A-1: C=2*A-1
/// 2*A-1<B<2*A: C=B
/// B>2*A: C=2*A
func blendPinLight(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $0 <= 2 * $1 - 1 { return 2 * $1 - 1 }
else if (2 * $1 - 1 < $0) && ($0 < 2 * $1) { return $0}
else { return 2 * $1 }
}
}
/// Hard Mix 實(shí)色混合A<1-B: C=0; A>1-B: C=1
func blendHardMix(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $1 <= 1 - $0 { return 0 }
else { return 1 }
}
}
// MARK: - 色差型
/// Difference 差值 C=|A-B|
func blendDifference(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { fabs($1 - $0) }
}
/// Exclusion 排除 C = A+B-2*A*B
func blendExclusion(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { $1 + $0 - 2 * $1 * $0 }
}
/// 減去 C=A-B
func blendMinus(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) { $1 - $0 }
}
/// 劃分 C=A/B
func blendDivision(coverColor: UIColor,alpha: CGFloat = 1.0) -> UIColor {
return blendProcedure(coverColor: coverColor, alpha: alpha) {
if $0 == 0{
return 1.0
}else {
return self.fitIn($1 / $0, ceil: 1.0)
}
}
}
// MARK: 處理函數(shù)
func blendProcedure(
coverColor: UIColor,
alpha: CGFloat,
procedureBlock: ((_ baseValue: CGFloat,_ topValue: CGFloat) -> CGFloat)?
) -> UIColor {
let baseCompoment = self.rgbaTuple()
let topCompoment = coverColor.rgbaTuple()
// 該層透明度
let mixAlpha = alpha * topCompoment.a + (1.0 - alpha) * baseCompoment.a
// RGB 值
let mixR = procedureBlock?(
baseCompoment.r / 255.0,
topCompoment.r / 255.0)
?? (baseCompoment.r) / 255.0
let mixG = procedureBlock?(
baseCompoment.g / 255.0,
topCompoment.g / 255.0)
?? (baseCompoment.g) / 255.0
let mixB = procedureBlock?(
baseCompoment.b / 255.0,
topCompoment.b / 255.0)
?? baseCompoment.b / 255.0
return UIColor.init(red: fitIn(mixR),
green: fitIn(mixG),
blue: fitIn(mixB),
alpha: mixAlpha)
}
// 防止越界
func fitIn(_ value: CGFloat, ceil: CGFloat = 255) -> CGFloat { return max(min(value,ceil),0) }
func fitIn(_ value: Double, ceil: CGFloat = 255) -> CGFloat { return fitIn(CGFloat(value), ceil: ceil) }
// 返回 RBGA
func rgbaTuple() -> (r: CGFloat, g: CGFloat, b: CGFloat,a: CGFloat) {
var r: CGFloat = 0
var g: CGFloat = 0
var b: CGFloat = 0
var a: CGFloat = 0
self.getRed(&r, green: &g, blue: &b, alpha: &a)
r = r * 255
g = g * 255
b = b * 255
return ((r),(g),(b),a)
}
}
參考資料
圖層混合開發(fā)案例: http://avnpc.com/pages/photoshop-layer-blending-algorithm
圖層混合樣式及說明: http://www.jb51.net/photoshop/249182.
圖層混合公式: https://www.zhihu.com/question/20293077