アルファ付きピクセルからアルファ付きピクセルへの転送

ってどうなるんだろうと思って計算してみました。

オレオレ記法

(S_a,S_r,S_g,S_b)\rightarrow(D_a,D_r,D_g,D_b)
と書いた場合、\rightarrow の左は転送元、右は転送先です。
括弧内の各要素はアルファ値、赤の要素、緑の要素、青の要素という順番で並んでいて、それらは [0...1] の実数です。
また、今回の計算においては、緑や青の要素についての計算は赤と同じはずなので、以下のように緑や青の要素を省略して書きます。
(S_a,S_r)\rightarrow(D_a,D_r)

特殊なケース

まず転送先のアルファ値 1 の場合を考えてみると、これは普通のアルファブレンドになるので、以下のような式になります。
(S_a,S_r)\rightarrow(1,D_r) = (1,S_aS_r+(1-S_a)D_r)
これを武器に (S_a,S_r)\rightarrow(D_a,D_r) がどうなるかというのを考えていこうと思います。

考え方

ピクセルというのは最終的には画面に表示されるので、いつかは転送先のアルファが 1 のピクセルと計算されます。
そこで、次のような転送を考えます。
(S_a,S_r)\rightarrow(D_a,D_r)\rightarrow(1,T_r)
アルファ付きの転送の性質から、(S_a,S_r)\rightarrow(D_a,D_r) の計算が先でも (D_a,D_r)\rightarrow(1,T_r) の計算が先でも同じ結果になるはずです。というかアルファ付きピクセルからアルファ付きピクセルへの転送はそうなるような計算式でないとダメという定義にしてしまいます。


そして、右から計算した場合と左から計算した結果が同じになるように (S_a,S_r)\rightarrow(D_a,D_r) の計算を作っていきます。

右から計算

まずは (D_a,D_r)\rightarrow(1,T_r) を計算してみます。
これは転送先のアルファ値 が 1 なので普通のアルファブレンドです。
(D_a,D_r)\rightarrow(1,T_r)=(1,D_aD_r+(1-D_a)T_r)
で、D_aD_r+(1-D_a)T_rN として、この結果に (S_a,S_r) を転送してみると、
(S_a,S_r)\rightarrow(1,N)=(1,S_aS_r+(1-S_a)N)
となって、N を展開して整理してみると、
\begin{matrix}&(&1&,&S_aS_r+(1-S_a)N&) \\ =&(&1&,&S_aS_r+(1-S_a)(D_aD_r+(1-D_a)T_r)&) \\ =&(&1&,&S_aS_r+D_aD_r+T_r-D_aT_r-S_aD_aD_r-S_aT_r+S_aD_aT_r&)\end{matrix}
となります。
これが右から計算した場合の最終結果です。

左から計算

右から計算した結果と左から計算した結果は同じになるはずなので、最終的に求めたい計算式である
(S_a,S_r)\rightarrow(D_a,D_r)
の結果をとりあえず (X_a,X_r) ということにしておくと、
(X_a,X_r)\rightarrow(1,T_r)

\begin{matrix}&(&1&,&X_aX_r+(1-X_a)T_r&) \\ =&(&1&,&S_aS_r+D_aD_r+T_r-D_aT_r-S_aD_aD_r-S_aT_r+S_aD_aT_r&)\end{matrix}
になるはずです。

(X_a,X_r) を逆算

上記の結果から、(X_a,X_r) を求めてみます。
X_aX_r=A1-X_a=B として考えて、右辺を T_r のある項と無い項に分けて、T_r のある項はそれを外に出してやります。
\begin{matrix}A+BT_r&=&S_aS_r+D_aD_r-S_aD_aD_r&+&T_r-S_aT_r-D_aT_r+S_aD_aT_r \\ A+BT_r&=&S_aS_r+D_aD_r-S_aD_aD_r&+&(1-S_a-D_a+S_aD_a)T_r\end{matrix}
となるので、
A=X_aX_r=S_aS_r+D_aD_r-S_aD_aD_r
B=1-X_a=1-S_a-D_a+S_aD_a
であることが分かります。
そうすると B から X_a が求まります。
\begin{matrix}1&-&X_a&=&1-S_a-D_a+S_aD_a\\&-&X_a&=&-S_a-D_a+S_aD_a\\&&X_a&=&S_a+D_a-S_aD_a\\&&X_a&=&S_a+(1-S_a)D_a\end{matrix}
X_aA から X_r が求まります。
\begin{matrix}X_a&X_r&=&S_aS_r+D_aD_r-S_aD_aD_r\\&X_r&=&\frac{S_aS_r+(1-S_a)D_aD_r}{X_a}\\&X_r&=&\frac{S_aS_r+(1-S_a)D_aD_r}{S_a+(1-S_a)D_a}\end{matrix}
となります。これが X_r です。


つまり、
(S_a,S_r)\rightarrow(D_a,D_r)=(S_a+(1-S_a)D_a,\frac{S_aS_r+(1-S_a)D_aD_r}{S_a+(1-S_a)D_a})
となります。
分数があるのがなんだかすっきりしない結果です。

Premultiplied Alpha

アルファ付きピクセルには、Premultiplied Alpha という高速化方法があるらしくて、これは各RGBピクセルを事前に乗算しておくという方法のようです。
つまり、
(S_a,S_r,S_g,S_b)

(S_a,S_aS_r,S_aS_g,S_aS_b)
として考えるようです。


(S_a,S_aS_r,S_aS_g,S_aS_b)\{S_a,S_{ar},S_{ag},S_{ab}\} と書くとして、これで例えば
\{S_a,S_{ar}\}\rightarrow(1,D_r)
なんかをを計算すると
(1,S_{ar}+(1-S_a)D_r)
になるので、乗算が減って高速化が図れるようです。


この Premultiplied Alpha を使うと、
\{S_a,S_{ar}\}\rightarrow\{D_a,D_{ar}\}=(S_a+(1-S_a)D_a,\frac{S_{ar}+(1-S_a)D_{ar}}{S_a+(1-S_a)D_a})
となりいくつか乗算が減ります。
これ自体はあまり大した意味は無いのですが、このピクセルを Premultiplied Alpha なピクセルにしてみると分母が丸ごと消えることが分かります。
\begin{matrix}&\{S_a+(1-S_a)D_a,(S_a+(1-S_a)D_a)\cdot\frac{S_{ar}+(1-S_a)D_{ar}}{S_a+(1-S_a)D_a}\}\\=&\{S_a+(1-S_a)D_a,S_{ar}+(1-S_a)D_{ar}\}\end{matrix}
Premultiplied Alpha なピクセルを求める場合ならかなり綺麗な結果になりそうです。

まとめ

Premultiplied Alpha でないピクセルから Premultiplied Alpha でないピクセルを求める場合は
\Large(S_a,S_r)\rightarrow(D_a,D_r)=(S_a+(1-S_a)D_a,\frac{S_aS_r+(1-S_a)D_aD_r}{S_a+(1-S_a)D_a})
となって、
Premultiplied Alpha なピクセルから Premultiplied Alpha なピクセルを求める場合は
\Large\{S_a,S_{ar}\}\rightarrow\{D_a,D_{ar}\}=\{S_a+(1-S_a)D_a,S_{ar}+(1-S_a)D_{ar}\}
になるようです。

追記

そういえば分数になったときに分母が 0 の場合を考慮してなかったんだけど、どうせ Premultiplied Alpha しか使わないだろうから考えない方向で。