SSE用いたアルファブレンドの高速化4 アライメントがずれている画像同士のアルファブレンド

Posted on Sat 03 December 2011 in ペイントツール

↓の続き

今回はアライメントをずらした場合での計測をします。

_mm_loadu_si128などアライメントをあわせる必要がないロード・ストア命令を使用する場合は、
前回と同じ様に何も考えずに読み込んでアルファブレンドを行い、書きこむという順で処理を行うことができます。

しかし、アライメントを合わせる必要があるロード・ストア命令を使用する場合は、
処理方法をアライメントのズレによって変える必要があります。

今回のアルファブレンド処理では、

  1. 入出力メモリともアライメントが揃っている場合
  2. 入出力メモリともアライメントから同じだけずれている場合
  3. 入力、出力メモリのアライメントのずれ量が違う場合

の3通りに分けてコードを実装しました。

1の場合は、前回と同様のコードで処理します。
2の場合は、ずれてる分を処理して入出力ともアライメントを合わせた状態にして、1と同じように処理します。
3の場合は、ずれてる分をシフト演算を使用してずれを合わせる処理をいれて、
ロード・ストア命令がアライメントが合った状態で実行されるようにします。

計測方法

32bit,64bit向けに2通りに実行ファイルを作成、それぞれについて

  1. SSEを使わない処理
  2. _mm_loadu_si128でメモリ読み込み、_mm_storeu_si128でメモリ書き込み
  3. _mm_load_si128でメモリ読み込み、_mm_store_si128でメモリ書き込み
  4. _mm_load_si128でメモリ読み込み、_mm_stream_si128でメモリ書き込み

の4通りの処理の処理速度を計測する。
前回までは間違えて浮動小数点用の_mm_loadu_ps等を使っていたので、今回は_mm_loadu_si128等を使います。

  • チャンネルサイズ8bit 5700x5700BGR画像を2枚読み込み、アルファ値を指定してアルファブレンドを行う。
  • 画像の保存、読み込みはopenCV 2.3を用いた。
  • アルファブレンド部分の処理のみ処理時間を計測した。
  • 与えるアルファチャンネルは、上下ともにアルファ値を均等に分布させる
  • アルファブレンドでの入力画像は上下でアライメントのずれ量が合った状態にする。

以下の3通りのように意図的にアライメントをずらして計測を行います。

  1. 入出力画像ともにずらさない
  2. 入出力画像とも2ピクセルずつずらす
  3. 入力画像を2ピクセル、出力画像を1ピクセルずらす

計測1

環境
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);
}