SSE用いたアルファブレンドの高速化4 アライメントがずれている画像同士のアルファブレンド
Posted on Sat 03 December 2011 in ペイントツール
↓の続き
今回はアライメントをずらした場合での計測をします。
_mm_loadu_si128などアライメントをあわせる必要がないロード・ストア命令を使用する場合は、
前回と同じ様に何も考えずに読み込んでアルファブレンドを行い、書きこむという順で処理を行うことができます。
しかし、アライメントを合わせる必要があるロード・ストア命令を使用する場合は、
処理方法をアライメントのズレによって変える必要があります。
今回のアルファブレンド処理では、
- 入出力メモリともアライメントが揃っている場合
- 入出力メモリともアライメントから同じだけずれている場合
- 入力、出力メモリのアライメントのずれ量が違う場合
の3通りに分けてコードを実装しました。
1の場合は、前回と同様のコードで処理します。
2の場合は、ずれてる分を処理して入出力ともアライメントを合わせた状態にして、1と同じように処理します。
3の場合は、ずれてる分をシフト演算を使用してずれを合わせる処理をいれて、
ロード・ストア命令がアライメントが合った状態で実行されるようにします。
計測方法
32bit,64bit向けに2通りに実行ファイルを作成、それぞれについて
- SSEを使わない処理
- _mm_loadu_si128でメモリ読み込み、_mm_storeu_si128でメモリ書き込み
- _mm_load_si128でメモリ読み込み、_mm_store_si128でメモリ書き込み
- _mm_load_si128でメモリ読み込み、_mm_stream_si128でメモリ書き込み
の4通りの処理の処理速度を計測する。
前回までは間違えて浮動小数点用の_mm_loadu_ps等を使っていたので、今回は_mm_loadu_si128等を使います。
- チャンネルサイズ8bit 5700x5700BGR画像を2枚読み込み、アルファ値を指定してアルファブレンドを行う。
- 画像の保存、読み込みはopenCV 2.3を用いた。
- アルファブレンド部分の処理のみ処理時間を計測した。
- 与えるアルファチャンネルは、上下ともにアルファ値を均等に分布させる
- アルファブレンドでの入力画像は上下でアライメントのずれ量が合った状態にする。
以下の3通りのように意図的にアライメントをずらして計測を行います。
- 入出力画像ともにずらさない
- 入出力画像とも2ピクセルずつずらす
- 入力画像を2ピクセル、出力画像を1ピクセルずらす
計測1
環境
- CPU: Core i7 2600 3.40GHz
- OS: Windows 7 64bit
- Mem: 8.00GB(7.73GB usable)
- コンパイラ: Visual Studio 2010
- 最適化オプション: 実行速度 (/O2)
32bit向け実行ファイル
- 入出力画像ともにずらさない
非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
---|---|---|---|---|
1回目 | 675.374 msec | 203.678 msec | 166.000 msec | 166.106 msec |
2回目 | 591.512 msec | 181.441 msec | 175.978 msec | 207.662 msec |
3回目 | 563.755 msec | 170.575 msec | 179.130 msec | 165.016 msec |
- 入出力画像とも2ピクセルずつずらす
非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
---|---|---|---|---|
1回目 | 584.079 msec | 184.901 msec | 163.356 msec | 197.957 msec |
2回目 | 601.959 msec | 229.62 msec | 169.618 msec | 179.718 msec |
3回目 | 576.596 msec | 172.858 msec | 172.926 msec | 238.996 msec |
- 入力画像を2ピクセル、出力画像を1ピクセルずらす
非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
---|---|---|---|---|
1回目 | 581.447 msec | 215.271 msec | 194.737 msec | 183.298 msec |
2回目 | 583.466 msec | 168.634 msec | 175.305 msec | 204.374 msec |
3回目 | 585.919 msec | 170.421 msec | 179.532 msec | 177.077 msec |
64bit向け実行ファイル
- 入出力画像ともにずらさない
非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
---|---|---|---|---|
1回目 | 321.546 msec | 178.499 msec | 181.309 msec | 192.388 msec |
2回目 | 370.826 msec | 170.399 msec | 159.175 msec | 185.213 msec |
3回目 | 311.765 msec | 162.117 msec | 166.067 msec | 167.506 msec |
- 入出力画像とも2ピクセルずつずらす
非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
---|---|---|---|---|
1回目 | 316.697 msec | 172.909 msec | 162.310 msec | 167.428 msec |
2回目 | 308.390 msec | 169.355 msec | 163.629 msec | 159.495 msec |
3回目 | 312.266 msec | 165.965 msec | 158.519 msec | 173.990 msec |
- 入力画像を2ピクセル、出力画像を1ピクセルずらす
非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
---|---|---|---|---|
1回目 | 303.656 msec | 175.868 msec | 172.008 msec | 163.839 msec |
2回目 | 329.698 msec | 157.363 msec | 171.484 msec | 166.765 msec |
3回目 | 297.241 msec | 159.257 msec | 164.473 msec | 163.332 msec |
計測2
環境
- CPU: Core2 Duo E8300 3.00GHZ
- OS: Windows 7 32bit
- Mem: 4.00GM(3.50GB usable)
- コンパイラ: Visual Studio 2010
- 最適化オプション: 実行速度 (/O2)
- 入出力画像ともにずらさない
非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
---|---|---|---|---|
1回目 | 1098.980 msec | 217.239 msec | 191.035 msec | 197.020 msec |
2回目 | 1103.000 msec | 218.563 msec | 190.894 msec | 193.745 msec |
3回目 | 1104.150 msec | 218.304 msec | 190.167 msec | 196.236 msec |
- 入出力画像とも2ピクセルずつずらす
非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
---|---|---|---|---|
1回目 | 1106.910 msec | 218.914 msec | 194.721 msec | 199.865 msec |
2回目 | 1109.580 msec | 223.114 msec | 196.515 msec | 199.412 msec |
3回目 | 1127.070 msec | 216.637 msec | 190.159 msec | 196.093 msec |
- 入力画像を2ピクセル、出力画像を1ピクセルずらす
非SSE | _mm_loadu_si128 + _mm_storeu_si128 | _mm_load_si128 + _mm_store_si128 | _mm_load_si128 + _mm_stream_si128 | |
---|---|---|---|---|
1回目 | 1119.890 msec | 255.483 msec | 245.189 msec | 243.910 msec |
2回目 | 1086.530 msec | 221.121 msec | 210.191 msec | 211.133 msec |
3回目 | 1086.650 msec | 221.380 msec | 209.693 msec | 221.721 msec |
まとめ
Core i7 2600では、_mm_loadu_si128を使っても_mm_load_si128とバラツキがあるが同程度という結果だった。
32bit Core2 Duo
E8400では、アライメントを合わせた効果がCorei7に比べてよく出ていて
入出力画像とも2ピクセルずつずらした場合には約10%、
入力画像を1ピクセル、出力画像を2ピクセルずらした場合には約5%速くなっていることがわかった。
使用したコード
SSEのアルファブレンド関数はinlineを付けてもインライン展開されなかったのでマクロ関数で実装しています。
#include <iostream>
#include <opencv/cv.h>
#include <opencv/highgui.h>
#include "IETimer.h"
union UCvPixel32
{
uint32_t value;
struct
{
uint8_t b,g,r,a;
};
};
typedef UCvPixel32 UCvPixel;
inline uint8_t* GetNextLineAddress(const IplImage* image, uint8_t* addr)
{
return addr + image->widthStep;
}
inline uint8_t* GetPixelAddress(const IplImage* image, int x, int y)
{
assert(image);
assert(image->depth == IPL_DEPTH_8U);
assert((0 <= x && x <= image->width-1) && (0 <= y && y <= image->height-1));
return (uint8_t*)(image->imageData + y*image->widthStep + x*image->nChannels);
}
inline UCvPixel32* GetNextLineAddress32(const IplImage* image, UCvPixel32* addr)
{
return (UCvPixel32*)((uint8_t*)addr + image->widthStep);
}
inline UCvPixel32* GetPixelAddress32(const IplImage* image, int x, int y)
{
assert(image);
assert(image->depth == IPL_DEPTH_8U);
assert((0 <= x && x <= image->width-1) && (0 <= y && y <= image->height-1));
return (UCvPixel32*)(image->imageData + y*image->widthStep + x*image->nChannels);
}
#define ALPHABLEND_SSE(dst, under, over)
{
__m128i __xmzero = _mm_setzero_si128();
__m128i __xmff16 = _mm_set1_epi16(0xff);
__m128i __xmo16, __xmu16;
__m128i __xmlo, __xmhi;
__m128i __xmover_alpha, __xmover_ralpha, __xmunder_alpha, __xmunder_ralpha, __xmalpha;
__m128 __xmf_lo, __xmf_hi, __xmf_alpha_lo, __xmf_alpha_hi;
/*load lo double ward */
__xmo16 = _mm_unpacklo_epi8(over, __xmzero);
__xmu16 = _mm_unpacklo_epi8(under, __xmzero);
int over_a1 = _mm_extract_epi16(__xmo16, 3);
int over_a2 = _mm_extract_epi16(__xmo16, 7);
if ((over_a1 == 255 && over_a2 == 255)) {
__xmlo = __xmo16;
} else {
int under_a1 = _mm_extract_epi16(__xmu16, 3);
int under_a2 = _mm_extract_epi16(__xmu16, 7);
if (under_a1 == 0 && under_a2 == 0) {
__xmlo = __xmo16;
} else if (under_a1 == 0 && under_a2 == 0) {
__xmlo = __xmu16;
} else if (under_a1 == 255 && under_a2 == 255) {
__xmover_alpha = _mm_shufflelo_epi16(__xmo16, _MM_SHUFFLE(3,3,3,3));
__xmover_alpha = _mm_shufflehi_epi16(__xmover_alpha, _MM_SHUFFLE(3,3,3,3));
__xmover_ralpha = _mm_sub_epi16(__xmff16, __xmover_alpha);
__xmu16 = _mm_mullo_epi16(__xmu16, __xmover_ralpha); /* under_pix * over_ralpha */
__xmo16 = _mm_mullo_epi16(__xmo16, __xmover_alpha); /* over_pix * over_alpha */
__xmo16 = _mm_add_epi16(__xmu16, __xmo16); /* (under_pix * over_ralpha + over_pix * over_alpha) */
__xmlo = _mm_srli_epi16(__xmo16, 8); /* (under_pix * over_ralpha + over_pix * over_alpha) >> 8 */
__xmlo = _mm_insert_epi16(__xmlo, 255, 3); /* write alpha */
__xmlo = _mm_insert_epi16(__xmlo, 255, 7); /* write alpha */
} else {
__xmover_alpha = _mm_shufflelo_epi16(__xmo16, _MM_SHUFFLE(3,3,3,3));
__xmover_alpha = _mm_shufflehi_epi16(__xmover_alpha, _MM_SHUFFLE(3,3,3,3));
__xmover_ralpha = _mm_sub_epi16(__xmff16, __xmover_alpha);
__xmunder_alpha = _mm_shufflelo_epi16(__xmu16, _MM_SHUFFLE(3,3,3,3));
__xmunder_alpha = _mm_shufflehi_epi16(__xmunder_alpha, _MM_SHUFFLE(3,3,3,3));
__xmunder_ralpha = _mm_sub_epi16(__xmff16, __xmunder_alpha);
__xmalpha = _mm_sub_epi16(__xmff16, _mm_srli_epi16( _mm_mullo_epi16(__xmover_ralpha, __xmunder_ralpha), 8)); /* 255 - ((over_ralpha * under_ralpha) >> 8) */
__xmu16 = _mm_mullo_epi16( _mm_srli_epi16( _mm_mullo_epi16(__xmunder_alpha, __xmover_ralpha), 8), __xmu16); /* under_pix * ((under_alpha * over_ralpha)>>8) */
__xmo16 = _mm_mullo_epi16(__xmo16, __xmover_alpha); /* over_pix * over_alpha */
__xmo16 = _mm_add_epi16(__xmu16, __xmo16); /* over_pix * over_alpha + (under_pix * under_alpha * over_ralpha)>>8 */
/* calc pixel 1 */
__xmf_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(__xmo16, __xmzero) );
__xmf_alpha_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(__xmalpha, __xmzero) );
__xmf_lo = _mm_mul_ps(__xmf_lo, _mm_rcp_ps(__xmf_alpha_lo));
/* calc pixel 2 */
__xmf_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(__xmo16, __xmzero) );
__xmf_alpha_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(__xmalpha, __xmzero) );
__xmf_hi = _mm_mul_ps(__xmf_hi, _mm_rcp_ps(__xmf_alpha_hi));
/* pack to 16bit data */
__xmlo = _mm_packs_epi32(_mm_cvtps_epi32(__xmf_lo), _mm_cvtps_epi32(__xmf_hi));
int a1 = _mm_extract_epi16(__xmalpha, 3);
__xmlo = _mm_insert_epi16(__xmlo, a1, 3); /* write alpha */
int a2 = _mm_extract_epi16(__xmalpha, 7);
__xmlo = _mm_insert_epi16(__xmlo, a2, 7); /* write alpha */
}
}
/* load hi double ward */
__xmo16 = _mm_unpackhi_epi8(over, __xmzero);
__xmu16 = _mm_unpackhi_epi8(under, __xmzero);
over_a1 = _mm_extract_epi16(__xmo16, 3);
over_a2 = _mm_extract_epi16(__xmo16, 7);
if ((over_a1 == 255 && over_a2 == 255)) {
__xmhi = __xmo16;
} else {
int under_a1 = _mm_extract_epi16(__xmu16, 3);
int under_a2 = _mm_extract_epi16(__xmu16, 7);
if (under_a1 == 0 && under_a2 == 0) {
__xmhi = __xmo16;
} else if (under_a1 == 0 && under_a2 == 0) {
__xmhi = __xmu16;
} else if (under_a1 == 255 && under_a2 == 255) {
__xmover_alpha = _mm_shufflelo_epi16(__xmo16, _MM_SHUFFLE(3,3,3,3));
__xmover_alpha = _mm_shufflehi_epi16(__xmover_alpha, _MM_SHUFFLE(3,3,3,3));
__xmover_ralpha = _mm_sub_epi16(__xmff16, __xmover_alpha);
__xmu16 = _mm_mullo_epi16(__xmu16, __xmover_ralpha); /* under_pix * over_ralpha */
__xmo16 = _mm_mullo_epi16(__xmo16, __xmover_alpha); /* over_pix * over_alpha */
__xmo16 = _mm_add_epi16(__xmu16, __xmo16); /* (under_pix * over_ralpha + over_pix * over_alpha) */
__xmhi = _mm_srli_epi16(__xmo16, 8); /* (under_pix * over_ralpha + over_pix * over_alpha) >> 8 */
__xmhi = _mm_insert_epi16(__xmhi, 255, 3); /* write alpha */
__xmhi = _mm_insert_epi16(__xmhi, 255, 7); /* write alpha */
} else {
__xmover_alpha = _mm_shufflelo_epi16(__xmo16, _MM_SHUFFLE(3,3,3,3));
__xmover_alpha = _mm_shufflehi_epi16(__xmover_alpha, _MM_SHUFFLE(3,3,3,3));
__xmover_ralpha = _mm_sub_epi16(__xmff16, __xmover_alpha);
__xmunder_alpha = _mm_shufflelo_epi16(__xmu16, _MM_SHUFFLE(3,3,3,3));
__xmunder_alpha = _mm_shufflehi_epi16(__xmunder_alpha, _MM_SHUFFLE(3,3,3,3));
__xmunder_ralpha = _mm_sub_epi16(__xmff16, __xmunder_alpha);
__xmalpha = _mm_sub_epi16(__xmff16, _mm_srli_epi16( _mm_mullo_epi16(__xmover_ralpha, __xmunder_ralpha), 8)); /* 255 - ((over_ralpha * under_ralpha) >> 8) */
__xmu16 = _mm_mullo_epi16( _mm_srli_epi16( _mm_mullo_epi16(__xmunder_alpha, __xmover_ralpha), 8), __xmu16); /* under_pix * ((under_alpha * over_ralpha)>>8) */
__xmo16 = _mm_mullo_epi16(__xmo16, __xmover_alpha); /* over_pix * over_alpha */
__xmo16 = _mm_add_epi16(__xmu16, __xmo16); /* over_pix * over_alpha + (under_pix * ((under_alpha * over_ralpha)>>8) */
/* calc pixel1 */
__xmf_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(__xmo16, __xmzero) );
__xmf_alpha_lo = _mm_cvtepi32_ps( _mm_unpacklo_epi16(__xmalpha, __xmzero) );
__xmf_lo = _mm_mul_ps(__xmf_lo, _mm_rcp_ps(__xmf_alpha_lo));
/* calc pixel 2 */
__xmf_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(__xmo16, __xmzero) );
__xmf_alpha_hi = _mm_cvtepi32_ps( _mm_unpackhi_epi16(__xmalpha, __xmzero) );
__xmf_hi = _mm_mul_ps(__xmf_hi, _mm_rcp_ps(__xmf_alpha_hi));
/* pack to 16bit data */
__xmhi = _mm_packs_epi32(_mm_cvtps_epi32(__xmf_lo), _mm_cvtps_epi32(__xmf_hi));
int a3 = _mm_extract_epi16(__xmalpha, 3);
__xmhi = _mm_insert_epi16(__xmhi, a3, 3); /* write alpha */
int a4 = _mm_extract_epi16(__xmalpha, 7);
__xmhi = _mm_insert_epi16(__xmhi, a4, 7); /* write alpha */
}
}
dst = _mm_packus_epi16(__xmlo, __xmhi);
}
inline void BltLineAlphaBlend(
UCvPixel* dst,
const UCvPixel* under,
const UCvPixel* over,
int blt_width)
{
int x;
for(x = 0; x < blt_width; x++){
//alpha blend
if ((over->a == 255) || (under->a == 0)) {
dst->value = over->value;
} else if (over->a == 0) {
dst->value = under->value;
} else if (under->a == 255) {
uint8_t ra = ~over->a;
dst->b = (over->b*over->a + under->b*ra)>>8;
dst->g = (over->g*over->a + under->g*ra)>>8;
dst->r = (over->r*over->a + under->r*ra)>>8;
dst->a = 255;
} else {
uint8_t ra = ~over->a;
uint8_t alpha = 255 - (ra*(255 - under->a)>>8);
double inv_alpha = 1.0/alpha;
dst->b = (over->b*over->a + ((under->b*under->a*ra)>>8))*inv_alpha;
dst->g = (over->g*over->a + ((under->g*under->a*ra)>>8))*inv_alpha;
dst->r = (over->r*over->a + ((under->r*under->a*ra)>>8))*inv_alpha;
dst->a = alpha;
}
over++;
under++;
dst++;
}
}
inline void BltLineAlphaBlend_SSE_unaligned(
UCvPixel* dst,
const UCvPixel* under,
const UCvPixel* over,
int blt_width)
{
//4ピクセルずつ処理するときのループ回数を求める
int nloop = blt_width >> 2; //blt_width / 4
int x;
for(x = 0; x < nloop; x++){
__m128i xmunder = _mm_loadu_si128((const __m128i*)under);
__m128i xmover = _mm_loadu_si128((const __m128i*)over);
__m128i xmdst;
ALPHABLEND_SSE(xmdst, xmunder, xmover);
_mm_storeu_si128((__m128i*)dst, xmdst);
over += 4;
under += 4;
dst += 4;
}
//波数を1ピクセルずつ処理
int bn = blt_width & 0x3; //blt_width % 4
BltLineAlphaBlend(
dst,
under,
over,
bn);
}
inline void SubBltLineAlphaBlend_SSE(
UCvPixel* dst,
const UCvPixel* under,
const UCvPixel* over,
int blt_width)
{
assert (DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over));
assert (IS_16BYTE_ALIGNMENT(dst) == IS_16BYTE_ALIGNMENT(under));
//4ピクセルずつ処理するときのループ回数を求める
int nloop = blt_width >> 2; //blt_width / 4
int x;
for(x = 0; x < nloop; x++){
__m128i xmunder = _mm_load_si128((const __m128i*)under);
__m128i xmover = _mm_load_si128((const __m128i*)over);
__m128i xmdst;
ALPHABLEND_SSE(xmdst, xmunder, xmover);
_mm_store_si128((__m128i*)dst, xmdst);
over += 4;
under += 4;
dst += 4;
}
//1ピクセルずつ処理
int bn = blt_width & 0x3; //blt_width % 4
BltLineAlphaBlend(
dst,
under,
over,
bn);
return;
}
template <int _SHIFT>
void ShiftSubBltLineAlphaBlend_SSE(
UCvPixel* dst,
const UCvPixel* under,
const UCvPixel* over,
int blt_width)
{
UCvPixel* _dst = dst;
const UCvPixel* _over = over;
assert(DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over));
assert(IS_16BYTE_ALIGNMENT(under) && IS_16BYTE_ALIGNMENT(over));
int nshift_pix = _SHIFT >> 2;
int nloop = (blt_width >> 2) - 1; //(blt_width / 4) - 1
__m128i xmunder, xmunder0, xmunder1, xmover, xmover0, xmover1;
xmunder0 = _mm_load_si128((const __m128i*)under);
xmover0 = _mm_load_si128((const __m128i*)over);
//_SHIFT byte分処理
//出力メモリを次回からアライメントを合わせて書き込めるようにする。
BltLineAlphaBlend(dst, under, over, nshift_pix);
dst = dst + nshift_pix;
assert(IS_16BYTE_ALIGNMENT(dst));
//
xmunder0 = _mm_srli_si128(xmunder0, _SHIFT); //xmunder0 >> _SHIFT
xmover0 = _mm_srli_si128(xmover0, _SHIFT); //xmover0 >> _SHIFT
int x;
for(x = 0; x < nloop; x++){
//
xmunder = _mm_load_si128((const __m128i*)(under + 4));
xmover = _mm_load_si128((const __m128i*)(over + 4));
//読み込んだ分をアライメントを合わせて書き込む時に
//合うようにシフト演算でずらしてOR演算で前回の残りと組み合わせる。
xmunder1 = _mm_slli_si128(xmunder, 16 - _SHIFT); //xmunder << (16 - _SHIFT)
xmover1 = _mm_slli_si128(xmover, 16 - _SHIFT); //xmmover << (16 - _SHIFT)
xmunder1 = _mm_or_si128(xmunder1, xmunder0);
xmover1 = _mm_or_si128(xmover1, xmover0);
//次回のループで使う分をずらして残しておく
xmunder0 = _mm_srli_si128(xmunder, _SHIFT); //xmunder >> _SHIFT
xmover0 = _mm_srli_si128(xmover, _SHIFT); //xmover >> _SHIFT
__m128i xmdst;
ALPHABLEND_SSE(xmdst, xmunder1, xmover1);
_mm_store_si128((__m128i*)dst, xmdst);
over += 4;
under += 4;
dst += 4;
}
//波数を1ピクセルずつ処理
int bn = blt_width - (nloop << 2) - nshift_pix;
BltLineAlphaBlend(
dst,
under + nshift_pix,
over + nshift_pix,
bn);
return;
}
inline void BltLineAlphaBlend_SSE(
UCvPixel* dst,
const UCvPixel* under,
const UCvPixel* over,
int blt_width)
{
assert (DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over));
if (IS_16BYTE_ALIGNMENT(dst) && IS_16BYTE_ALIGNMENT(under)) {
//入出力ともアライメントがあってるならSSEで処理
SubBltLineAlphaBlend_SSE(dst, under, over, blt_width);
return;
}
int dif_d = DIFF_16BYTE_ALIGNMENT(dst); //アライメントのずれ量を求める
int dif_s = DIFF_16BYTE_ALIGNMENT(under);
assert((dif_d & 0x3) == 0); //1pixel、4byteなので4の倍数になるはず
assert((dif_s & 0x3) == 0); //
if (dif_d == dif_s) {
int dif_npix = dif_s >> 2; //アライメントがあっていないピクセル数を得る
//アライメントがあっていない分を1ピクセルずつしょり
BltLineAlphaBlend(
dst,
under,
over,
dif_npix);
//残りはSSEで
SubBltLineAlphaBlend_SSE(
dst + dif_npix,
under + dif_npix,
over + dif_npix,
blt_width - dif_npix);
} else {
//読み込み元のアライメントを合わせる。
int dif_npix = dif_s >> 2;
BltLineAlphaBlend(
dst,
under,
over,
dif_npix);
//書き込み先のアライメントのズレを再度計算
dst += dif_npix;
under += dif_npix;
over += dif_npix;
int nshift_byte = DIFF_16BYTE_ALIGNMENT(dst);
assert((nshift_byte & 0x3) == 0);
switch (nshift_byte) {
case 4: //1pixelずれ
ShiftSubBltLineAlphaBlend_SSE<4>(dst, under, over, blt_width);
break;
case 8: //2pixelずれ
ShiftSubBltLineAlphaBlend_SSE<8>(dst, under, over, blt_width);
break;
case 12: //3pixelずれ
ShiftSubBltLineAlphaBlend_SSE<12>(dst, under, over, blt_width);
break;
default:
assert(0);
}
}
}
inline void SubBltLineAlphaBlend_SSE_nocache(
UCvPixel* dst,
const UCvPixel* under,
const UCvPixel* over,
int blt_width)
{
assert (DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over));
assert (IS_16BYTE_ALIGNMENT(dst) == IS_16BYTE_ALIGNMENT(under));
int nloop = blt_width >> 2; //blt_width / 4
int x;
for(x = 0; x < nloop; x++){
__m128i xmunder = _mm_load_si128((const __m128i*)under);
__m128i xmover = _mm_load_si128((const __m128i*)over);
__m128i xmdst;
ALPHABLEND_SSE(xmdst, xmunder, xmover);
_mm_stream_si128((__m128i*)dst, xmdst);
over += 4;
under += 4;
dst += 4;
}
int bn = blt_width & 0x3; //blt_width % 4
BltLineAlphaBlend(
dst,
under,
over,
bn);
return;
}
template <int _SHIFT>
void ShiftSubBltLineAlphaBlend_SSE_nocache(
UCvPixel* dst,
const UCvPixel* under,
const UCvPixel* over,
int blt_width)
{
assert(DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over));
assert(IS_16BYTE_ALIGNMENT(under) && IS_16BYTE_ALIGNMENT(over));
int nshift_pix = _SHIFT >> 2;
int nloop = (blt_width >> 2) - 1; //(blt_width / 4) - 1
__m128i xmunder, xmunder0, xmunder1, xmover, xmover0, xmover1;
xmunder0 = _mm_load_si128((const __m128i*)under);
xmover0 = _mm_load_si128((const __m128i*)over);
//_SHIFT byte分処理
BltLineAlphaBlend(dst, under, over, nshift_pix);
dst = dst + nshift_pix;
assert(IS_16BYTE_ALIGNMENT(dst));
xmunder0 = _mm_srli_si128(xmunder0, _SHIFT); //xmunder0 >> _SHIFT
xmover0 = _mm_srli_si128(xmover0, _SHIFT); //xmover0 >> _SHIFT
int x;
for(x = 0; x < nloop; x++){
xmunder = _mm_load_si128((const __m128i*)(under + 4));
xmover = _mm_load_si128((const __m128i*)(over + 4));
xmunder1 = _mm_slli_si128(xmunder, 16 - _SHIFT); //xmunder << (16 - _SHIFT)
xmover1 = _mm_slli_si128(xmover, 16 - _SHIFT); //xmmover << (16 - _SHIFT)
xmunder1 = _mm_or_si128(xmunder1, xmunder0);
xmover1 = _mm_or_si128(xmover1, xmover0);
xmunder0 = _mm_srli_si128(xmunder, _SHIFT); //xmunder >> _SHIFT
xmover0 = _mm_srli_si128(xmover, _SHIFT); //xmover >> _SHIFT
__m128i xmdst;
ALPHABLEND_SSE(xmdst, xmunder1, xmover1);
_mm_store_si128((__m128i*)dst, xmdst);
over += 4;
under += 4;
dst += 4;
}
int bn = blt_width - (nloop << 2) - nshift_pix;
BltLineAlphaBlend(
dst,
under + nshift_pix,
over + nshift_pix,
bn);
return;
}
inline void BltLineAlphaBlend_SSE_nocache(
UCvPixel* dst,
const UCvPixel* under,
const UCvPixel* over,
int blt_width)
{
assert (DIFF_16BYTE_ALIGNMENT(under) == DIFF_16BYTE_ALIGNMENT(over));
if (IS_16BYTE_ALIGNMENT(dst) && IS_16BYTE_ALIGNMENT(under)) {
SubBltLineAlphaBlend_SSE(dst, under, over, blt_width);
return;
}
int dif_d = DIFF_16BYTE_ALIGNMENT(dst);
int dif_s = DIFF_16BYTE_ALIGNMENT(under);
assert((dif_d & 0x3) == 0); //1pixel、4byteなので4の倍数になるはず
assert((dif_s & 0x3) == 0); //
if (dif_d == dif_s) {
int dif_npix = dif_s >> 2; //アライメントがあっていないピクセル数を得る
BltLineAlphaBlend(
dst,
under,
over,
dif_npix);
SubBltLineAlphaBlend_SSE_nocache(
dst + dif_npix,
under + dif_npix,
over + dif_npix,
blt_width - dif_npix);
} else {
//読み込み元のアライメントを合わせる。
int dif_npix = dif_s >> 2;
BltLineAlphaBlend(
dst,
under,
over,
dif_npix);
//
dst += dif_npix;
under += dif_npix;
over += dif_npix;
int nshift_byte = DIFF_16BYTE_ALIGNMENT(dst);
assert((nshift_byte & 0x3) == 0);
switch (nshift_byte) {
case 4: //1pixelずれ
ShiftSubBltLineAlphaBlend_SSE_nocache<4>(dst, under, over, blt_width);
break;
case 8: //2pixelずれ
ShiftSubBltLineAlphaBlend_SSE_nocache<8>(dst, under, over, blt_width);
break;
case 12: //3pixelずれ
ShiftSubBltLineAlphaBlend_SSE_nocache<12>(dst, under, over, blt_width);
break;
default:
assert(0);
}
}
}
void BltAlphaBlend1(
IplImage* dst_img,
int dst_startX,
int dst_startY,
int blt_width,
int blt_height,
const IplImage* under_img,
int under_startX,
int under_startY,
const IplImage* over_img,
int over_startX,
int over_startY)
{
int y;
UCvPixel *over_line = GetPixelAddress32(over_img, over_startX, over_startY);
UCvPixel *under_line = GetPixelAddress32(under_img, under_startX, under_startY);
UCvPixel *dst_line = GetPixelAddress32(dst_img, dst_startX, dst_startY);
for(y = 0; y < blt_height; y++){
BltLineAlphaBlend(dst_line, under_line, over_line, blt_width);
over_line = GetNextLineAddress32(over_img, over_line);
under_line = GetNextLineAddress32(under_img, under_line);
dst_line = GetNextLineAddress32(dst_img, dst_line);
}
}
void BltAlphaBlend2(
IplImage* dst_img,
int dst_startX,
int dst_startY,
int blt_width,
int blt_height,
const IplImage* under_img,
int under_startX,
int under_startY,
const IplImage* over_img,
int over_startX,
int over_startY)
{
int y;
UCvPixel *over_line = GetPixelAddress32(over_img, over_startX, over_startY);
UCvPixel *under_line = GetPixelAddress32(under_img, under_startX, under_startY);
UCvPixel *dst_line = GetPixelAddress32(dst_img, dst_startX, dst_startY);
for(y = 0; y < blt_height; y++){
BltLineAlphaBlend_SSE_unaligned(
dst_line,
under_line,
over_line,
blt_width);
over_line = GetNextLineAddress32(over_img, over_line);
under_line = GetNextLineAddress32(under_img, under_line);
dst_line = GetNextLineAddress32(dst_img, dst_line);
}
}
void BltAlphaBlend3(
IplImage* dst_img,
int dst_startX,
int dst_startY,
int blt_width,
int blt_height,
const IplImage* under_img,
int under_startX,
int under_startY,
const IplImage* over_img,
int over_startX,
int over_startY)
{
int y;
UCvPixel *over_line = GetPixelAddress32(over_img, over_startX, over_startY);
UCvPixel *under_line = GetPixelAddress32(under_img, under_startX, under_startY);
UCvPixel *dst_line = GetPixelAddress32(dst_img, dst_startX, dst_startY);
for(y = 0; y < blt_height; y++){
BltLineAlphaBlend_SSE(
dst_line,
under_line,
over_line,
blt_width);
over_line = GetNextLineAddress32(over_img, over_line);
under_line = GetNextLineAddress32(under_img, under_line);
dst_line = GetNextLineAddress32(dst_img, dst_line);
}
}
void BltAlphaBlend4(
IplImage* dst_img,
int dst_startX,
int dst_startY,
int blt_width,
int blt_height,
const IplImage* under_img,
int under_startX,
int under_startY,
const IplImage* over_img,
int over_startX,
int over_startY)
{
int y;
UCvPixel *over_line = GetPixelAddress32(over_img, over_startX, over_startY);
UCvPixel *under_line = GetPixelAddress32(under_img, under_startX, under_startY);
UCvPixel *dst_line = GetPixelAddress32(dst_img, dst_startX, dst_startY);
for(y = 0; y < blt_height; y++){
BltLineAlphaBlend_SSE_nocache(
dst_line,
under_line,
over_line,
blt_width);
over_line = GetNextLineAddress32(over_img, over_line);
under_line = GetNextLineAddress32(under_img, under_line);
dst_line = GetNextLineAddress32(dst_img, dst_line);
}
}
//sseを使った4ch同士のalphablend
int main(void)
{
//cvCreateImageで帰ってくる画像データのアドレスは先頭が16byteアライメントに合わせてある
//これをわざとずらす。
int dst_dif = 1; //この数分、正方向にずらす
int src_dif = 2; //この数分、正方向にずらす
int x,y;
int r,g,b;
int r1, g1, b1;
int r2, g2, b2;
UCvPixel *over_pix;
UCvPixel *under_pix;
UCvPixel *dst_pix;
UCvPixel *over_line;
UCvPixel *under_line;
UCvPixel *dst_line;
IplImage* test1 = cvLoadImage("test1.bmp");
IplImage* test2 = cvLoadImage("test2.bmp");
IplImage* b_ch = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1);
IplImage* g_ch = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1);
IplImage* r_ch = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1);
IplImage* a_ch1 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1);
IplImage* a_ch2 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 1);
IplImage* img1 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4);
IplImage* img2 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4);
IplImage* img3 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4);
IplImage* img4 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4);
IplImage* img5 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4);
IplImage* img6 = cvCreateImage(cvGetSize(test1), IPL_DEPTH_8U, 4);
//上になる画像のアルファ値を均等に分布させる
uint8_t* line1 = GetPixelAddress(a_ch1, 0, 0);
for (y = 0; y < a_ch1->height; y++) {
uint8_t* pix = line1;
for (x = 0; x < a_ch1->width; x++) {
(*pix++) = ((double)x / a_ch1->width) * 255;
}
line1 = GetNextLineAddress(a_ch1, line1);
}
//下になる画像のアルファ値を均等に分布させる
uint8_t* line2 = GetPixelAddress(a_ch2, 0, 0);
for (y = 0; y < a_ch2->height; y++) {
uint8_t* pix = line2;
for (x = 0; x < a_ch2->width; x++) {
(*pix++) = ((double)y / a_ch2->height) * 255;
}
line2 = GetNextLineAddress(a_ch2, line2);
}
cvSplit(test1, b_ch, g_ch, r_ch, NULL);
cvMerge(b_ch, g_ch, r_ch, a_ch1, img1);
cvSplit(test2, b_ch, g_ch, r_ch, NULL);
cvMerge(b_ch, g_ch, r_ch, a_ch2, img2);
//ずれがある分、書き込み幅を減らす。
int write_width = img1->width - max(dst_dif, src_dif);
//非SSE
IETimer timer;
BltAlphaBlend1(
img3,
dst_dif, 0,
write_width, img3->height,
img2,
src_dif, 0,
img1,
src_dif, 0);
std::cout << timer.elapsed() << " msec" << std::endl;
cvSaveImage("test3.bmp", img3);
//SSE: _mm_loadu_ps + _mm_storeu_ps
timer.restart();
BltAlphaBlend2(
img4,
dst_dif, 0,
write_width, img4->height,
img2,
src_dif, 0,
img1,
src_dif, 0);
std::cout << timer.elapsed() << " msec" << std::endl;
cvSaveImage("test4.bmp", img4);
//SSE: _mm_load_ps + _mm_store_ps
timer.restart();
BltAlphaBlend3(
img5,
dst_dif, 0,
write_width, img5->height,
img2,
src_dif, 0,
img1,
src_dif, 0);
std::cout << timer.elapsed() << " msec" << std::endl;
cvSaveImage("test5.bmp", img5);
//SSE: _mm_load_ps + _mm_stream_si128
timer.restart();
BltAlphaBlend4(
img6,
dst_dif, 0,
write_width, img6->height,
img2,
src_dif, 0,
img1,
src_dif, 0);
std::cout << timer.elapsed() << " msec" << std::endl;
cvSaveImage("test6.bmp", img6);
cvReleaseImage(&b_ch);
cvReleaseImage(&g_ch);
cvReleaseImage(&r_ch);
cvReleaseImage(&a_ch1);
cvReleaseImage(&a_ch2);
cvReleaseImage(&img1);
cvReleaseImage(&img2);
cvReleaseImage(&img3);
cvReleaseImage(&img4);
cvReleaseImage(&img5);
cvReleaseImage(&img6);
}