b = bottom
t = top
def normal(b,t):
return t
\(\text{normal}(b,t) = t\)
def darken(b,t):
return np.minimum(b,t)
\(\text{darken}(b,t) = \min(b,t)\)
def multiply(b,t):
return b*t
\(\text{multiply}(b,t) = bt\)
def color_burn(b,t):
invalid = np.geterr()['invalid']
np.seterr(invalid='ignore')
r = np.where(
b == 1,1,np.where(
t == 0,0,1-np.minimum((1-b)/t,1)))
np.seterr(invalid=invalid)
return r
\(\text{color_burn}(b,t) = \begin{cases} 1&\text{if }b = 1\\ 0&\text{elif }t = 0\\ 1-\min((1-b)/t,1)&\text{else} \end{cases}\)
def linear_burn(b,t):
return np.maximum(b+t-1,0)
\(\text{linear_burn}(b,t) = \text{max}(b+t-1,0)\)
def darker_color(b,t):
bY = rgb_to_gray(b).atleast_3d()
tY = rgb_to_gray(t).atleast_3d()
return np.where(bY < tY,b,t)
\(\text{darker_color}(b,t) = \begin{cases} b&\text{if }b_Y \lt t_Y\\ t&\text{else} \end{cases}\)
def lighten(b,t):
return np.maximum(b,t)
\(\text{lighten}(b,t) = \text{max}(b,t)\)
def screen(b,t):
return b+t-b*t
\(\begin{align} \text{screen}(b,t) &= 1-(1-b)(1-t)\\ &= b+t-bt \end{align}\)
def color_dodge(b,t):
invalid = np.geterr()['invalid']
np.seterr(invalid='ignore')
r = np.where(
b == 0,0,np.where(
t == 1,1,1-np.minimum(b/(1-t),1)))
np.seterr(invalid=invalid)
return r
\(\text{color_dodge}(b,t) = \begin{cases} 0&\text{if }b = 0\\ 1&\text{elif }t = 1\\ \min(b/(1-t),1)&\text{else} \end{cases}\)
def linear_dodge(b,t):
return np.minimum(b+t,1)
\(\text{linear_dodge}(b,t) = \text{min}(b+t,1)\)
def lighter_color(b,t):
bY = rgb_to_gray(b).atleast_3d()
tY = rgb_to_gray(t).atleast_3d()
return np.where(bY < tY,t,b)
\(\text{lighter_color}(b,t) = \begin{cases} t&\text{if }b_Y \lt t_Y\\ b&\text{else} \end{cases}\)
def overlay(b,t):
return hard_light(t,b)
\(\text{overlay}(b,t) = \text{hard_light}(b,t)\)
def soft_light(b,t):
return np.where(t <= 0.5,
2*b*t+(1-2*t)*b**2,
2*b*(1-t)+(2*t-1)*b**0.5)
\(\text{soft_light}(b,t) = \begin{cases} 2bt+(1-2t)b^2&\text{if }t \le 0.5\\ 2b(1-t)+(2t-1)b^{0.5}&\text{else} \end{cases}\)
def hard_light(b,t):
return np.where(t <= 0.5,
multiply(b,2*t),
screen(b,2*t-1))
\(\text{hard_light}(b,t) = \begin{cases} \text{multiply}(b,2t)&\text{if }t \le 0.5\\ \text{screen}(b,2t-1)&\text{else} \end{cases}\)
def vivid_light(b,t):
return np.where(t <= 0.5,
color_burn(b,2*t),
color_dodge(b,2*t-1))
\(\text{vivid_light}(b,t) = \begin{cases} \text{color_burn}(b,2t)&\text{if }t \le 0.5\\ \text{color_dodge}(b,2t-1)&\text{else} \end{cases}\)
def linear_light(b,t):
return np.where(t <= 0.5,
linear_burn(b,2*t),
linear_dodge(b,2*t-1))
\(\text{linear_light}(b,t) = \begin{cases} \text{linear_burn}(b,2t)&\text{if }t \le 0.5\\ \text{linear_dodge}(b,2t-1)&\text{else} \end{cases}\)
def pin_light(b,t):
return np.where(t <= 0.5,
darken(b,2*t),
lighten(b,2*t-1))
\(\text{pin_light}(b,t) = \begin{cases} \text{darken}(b,2t)&\text{if }t \le 0.5\\ \text{lighten}(b,2t-1)&\text{else} \end{cases}\)
def hard_mix(b,t):
return np.floor(b+t)
\(\text{hard_mix}(b,t) = \lfloor b+t \rfloor\)
def difference(b,t):
return np.maximum(b-t,t-b)
\(\text{difference}(b,t) = \max(b-t,t-b)\)
\(\text{exclusion}(b,t) = b+t-2bt\)
Cancellation Group
def subtract(b,t):
return np.maximum(b-t,0)
\(\text{subtract}(b,t) = \text{max}(b-t,0)\)
\(\text{divide}(b,t) = \begin{cases}
0&\text{if }b = 0\\
1&\text{elif }b \ge t\\
b/t&\text{else}
\end{cases}\)
Component Group
def hue(b,t):
tH = get_hsl_h(t)
return set_hsl_h(b,tH)
def saturation(b,t):
tS = get_hsl_s(t)
return set_hsl_s(b,tS)
def color(b,t):
bL = get_hsl_l(b)
return set_hsl_l(t,bL)
def luminosity(b,t):
return color(t,b)