Porter & Duff のテストプログラム
DirectFB の gfxdriver を開発するにあたって、Porter & Duff について軽く調べたのでメモ。
Porter & Duff の説明は wikipedia:アルファチャンネル に詳しいので、ここでは C で書いた簡単なテストプログラムのみを載せる。
Wikipedia を見ると、TeX じゃないと書けないような計算式が一見大義そうな印象を受けるが、実は、ただの掛け算と足し算であった。
サンプルプログラムは、引数で src color, src alpha, dst color, dst alpha の値を入れてやる。
(それぞれ 0 から 255 の範囲)
Porter & Duff のオペレーションをして、ブレンドした結果の色と alpha 値が表示される、という単純なもの。
#include#include enum opsv { ZERO, // 0 ONE, // 1 AS, // src alpha AD, // dst alpha INV_AS, // 1 - src alpha INV_AD, // 1 - dst alpha }; typedef struct _ops { char v1; char v2; } ops_t; char *op_name = { "CLEAR ", "SOURCE ", "DEST ", "SRC_OVER", "DST_OVER", "SRC_IN ", "DST_IN ", "SRC_OUT ", "DST_OUT ", "SRC_ATOP", "DST_ATOP", "XOR ", "PLUS ", }; ops_t ops_table = { { ZERO, ZERO }, // CLEAR { ONE, ZERO }, // SRC { ZERO, ONE }, // DST { ONE, INV_AS }, // SRC_OVER { INV_AD, ONE }, // DST_OVER { AD, ZERO }, // SRC_IN { ZERO, AS }, // DST_IN { INV_AD, ZERO }, // SRC_OUT { ZERO, INV_AS }, // DST_OUT { AD, INV_AS }, // SRC_ATOP { INV_AD, AS }, // DST_ATOP { INV_AD, INV_AS }, // XOR { ONE, ONE }, // PLUS }; double calc_each_op( double as, double ad, enum opsv op ) { switch ( op ) { case ZERO: return 0; case ONE: return 1; case AS: return as; case AD: return ad; case INV_AS: return (1 - as); case INV_AD: return (1 - ad); } assert( 0 ); return 0; } unsigned char calc_cd( unsigned char cs, double as, unsigned char cd, double ad, int op ) { ops_t ops = ops_table[op]; return cs * as * calc_each_op(as, ad, ops.v1) + cd * ad * calc_each_op(as, ad, ops.v2); } double calc_ad( double as, double ad, int op ) { ops_t ops = ops_table[op]; return as * calc_each_op(as, ad, ops.v1) + ad * calc_each_op(as, ad, ops.v2); } int main( int argc, char *argv[] ) { int i; unsigned char sc, dc; double sa, da; if ( argc <= 4 ) { fprintf( stderr, "usage : porter_duff_calc src_color src_alpha dst_color dst_alpha\n" ); fprintf( stderr, "\t color : 0 .. 255\n" ); fprintf( stderr, "\t alpha : 0 .. 255\n" ); return 0; } sc = atoi( argv[1] ); sa = atoi( argv[2] ) / 255.0; dc = atoi( argv[3] ); da = atoi( argv[4] ) / 255.0; printf( "operation \t color alpha\n" ); for ( i = 0; i < (sizeof(op_name) / sizeof( char * ) ) ; i++ ) { printf( "%s \t %d \t %f\n", op_name[i], calc_cd( sc, sa, dc, da, i ), calc_ad( sa, da, i ) ); } }
動かしたところ:
$ ./porter_duff_calc 128 255 192 128 operation color alpha CLEAR 0 0.000000 SOURCE 128 1.000000 DEST 96 0.501961 SRC_OVER 128 1.000000 DST_OVER 160 1.000000 SRC_IN 64 0.501961 DST_IN 96 0.501961 SRC_OUT 63 0.498039 DST_OUT 0 0.000000 SRC_ATOP 64 0.501961 DST_ATOP 160 1.000000 XOR 63 0.498039 PLUS 224 1.501961