圖形圖像處理-之-任意角度的高質量的快速的圖像旋轉 中篇 高質量的旋轉

          圖形圖像處理-之-任意角度的高質量的快速的圖像旋轉 中篇 高質量的旋轉
                        
[email protected]   2007.06.26

 

(2009.03.09  可以到這裏下載旋轉算法的完整的可以編譯的項目源代碼:  http://blog.csdn.net/housisong/archive/2009/03/09/3970925.aspx  )

(2007.09.14 修正三次卷積的MMX版本中表的精度太低(7bit),造成卷積結果誤差較大的問題,該版本提高了插值質量,並且速度加快約15-20%)


tag:圖像旋轉,任意角度,圖像縮放,速度優化,定點數優化,近鄰取樣插值,二次線性插值,
   三次卷積插值,MipMap鏈,三次線性插值,MMX/SSE優化,CPU緩存優化,AlphaBlend,顏色混合,並行

摘要:首先給出一個基本的圖像旋轉算法,然後一步一步的優化其速度和旋轉質量,打破不能軟件旋轉的神話!

任意角度的高質量的快速的圖像旋轉 全文 分爲:
     上篇 純軟件的任意角度的快速旋轉
     中篇 高質量的旋轉
     下篇 補充話題(完整AlphaBlend旋轉、旋轉函數並行化、針對大圖片的預讀緩衝區優化)


正文:
  爲了便於討論,這裏只處理32bit的ARGB顏色;
  代碼使用C++;涉及到彙編優化的時候假定爲x86平臺;使用的編譯器爲vc2005;
  爲了代碼的可讀性,沒有加入異常處理代碼;
   測試使用的CPU爲AMD64x2 4200+(2.37G),測試時使用的單線程執行;
  (一些基礎代碼和插值原理的詳細說明參見作者的《圖形圖像處理-之-高質量的快速的圖像縮放》系列文章
   旋轉原理和基礎參考《圖形圖像處理-之-任意角度的高質量的快速的圖像旋轉 上篇 純軟件的任意角度的快速旋轉》)


速度測試說明:
  只測試內存數據到內存數據的縮放
  測試圖片都是800*600旋轉到1004*1004,測試成績取各個旋轉角度的平均速度值; fps表示每秒鐘的幀數,值越大表示函數越快


A:一些顏色和圖片的數據定義:

#define asm __asm

typedef unsigned 
char TUInt8; // [0..255]
struct TARGB32      //32 bit color
{
    TUInt8  b,g,r,a;          
//a is alpha

};

struct TPicRegion  //一塊顏色數據區的描述,便於參數傳遞

{
    TARGB32
*    pdata;         //顏色數據首地址

    long        byte_width;    //一行數據的物理寬度(字節寬度);
                
//abs(byte_width)有可能大於等於width*sizeof(TARGB32);

    long        width;         //像素寬度
    long        height;        //像素高度
};

  inline TARGB32
& Pixels(TARGB32* pcolor,const long byte_width,const long x,const long
 y)
  {
    
return ( (TARGB32*)((TUInt8*)pcolor+byte_width*
y) )[x];
  }

//那麼訪問一個點的函數可以寫爲:

inline TARGB32& Pixels(const TPicRegion& pic,const long x,const long y)
{
    
return
 Pixels(pic.pdata,pic.byte_width,x,y);
}
//判斷一個點是否在圖片中

inline bool PixelsIsInPic(const TPicRegion& pic,const long x,const long y)
{
    
return ( (x>=0)&&(x<pic.width) && (y>=0)&&(y<
pic.height) );
}

//訪問一個點的函數,(x,y)座標可能超出圖片邊界; //邊界處理模式:邊界飽和

inline TARGB32& Pixels_Bound(const TPicRegion& pic,long x,long y)
{
    
//assert((pic.width>0)&&(pic.height>0));

    if (x<0) x=0else if (x>=pic.width ) x=pic.width -1;
    
if (y<0) y=0else if (y>=pic.height) y=pic.height-1
;
    
return
 Pixels(pic,x,y);
}

inline TARGB32
& Pixels_Bound(const TPicRegion& pic,long x,long y,bool&
 IsInPic)
{
    
//assert((pic.width>0)&&(pic.height>0));

    IsInPic=true;
    
if (x<0) {x=0; IsInPic=false; } else if (x>=pic.width ) {x=pic.width -1; IsInPic=false
; }
    
if (y<0) {y=0; IsInPic=false; } else if (y>=pic.height) {y=pic.height-1; IsInPic=false
; }
    
return
 Pixels(pic,x,y);
}

B:實現二次線性插值的旋轉

(插值原理參見我的blog文章《圖形圖像處理-之-高質量的快速的圖像縮放 中篇 二次線性插值和三次卷積插值》)

   a.首先改寫用於邊界掃描的類TRotaryClipData;在圖片邊緣插值的時候,插值的顏色數據可能
部分在圖片外,部分顏色數據在圖片內,所以TRotaryClipData需要同時找出“插值邊界以外”、
“插值邊界”、“插值邊界以內”
    掃描線圖示:       外    | 邊界 |      內     | 邊界 |    外  

    

struct
 TRotaryClipData{
public
:
    
long
 src_width;
    
long
 src_height;
    
long
 dst_width;
    
long
 dst_height;
    
long
 Ax_16; 
    
long
 Ay_16; 
    
long
 Bx_16; 
    
long
 By_16; 
    
long
 Cx_16;
    
long
 Cy_16; 
    
long border_width;//插值邊界寬度

private:
    
long
 cur_dst_up_x0;
    
long
 cur_dst_up_x1;
    
long
 cur_dst_down_x0;
    
long
 cur_dst_down_x1;
    inline 
bool is_border_src(long src_x_16,long
 src_y_16)
    {
         
return ( ( (src_x_16>=(-(border_width<<16)))&&((src_x_16>>16)<(src_width +
border_width)) )
               
&& ( (src_y_16>=(-(border_width<<16)))&&((src_y_16>>16)<(src_height+
border_width)) ) );
    }
    inline 
bool is_in_src(long src_x_16,long
 src_y_16)
    {
         
return ( ( (src_x_16>=(border_width<<16))&&((src_x_16>>16)<(src_width-
border_width) ) )
               
&& ( (src_y_16>=(border_width<<16))&&((src_y_16>>16)<(src_height-
border_width)) ) );
    }
    
void find_begin_in(long dst_y,long& out_dst_x,long& src_x_16,long&
 src_y_16)
    {
        src_x_16
-=
Ax_16;
        src_y_16
-=
Ay_16;
        
while
 (is_border_src(src_x_16,src_y_16))
        {
            
--
out_dst_x;
            src_x_16
-=
Ax_16;
            src_y_16
-=
Ay_16;
        }
        src_x_16
+=
Ax_16;
        src_y_16
+=
Ay_16;
    }
    
bool find_begin(long dst_y,long& out_dst_x0,long
 dst_x1)
    {
        
long test_dst_x0=out_dst_x0-1
;
        
long src_x_16=Ax_16*test_dst_x0 + Bx_16*dst_y +
 Cx_16;
        
long src_y_16=Ay_16*test_dst_x0 + By_16*dst_y +
 Cy_16;
        
for (long i=test_dst_x0;i<=dst_x1;++
i)
        {
            
if
 (is_border_src(src_x_16,src_y_16))
            {
                out_dst_x0
=
i;
                
if (i==
test_dst_x0)
                    find_begin_in(dst_y,out_dst_x0,src_x_16,src_y_16);
                
if (out_dst_x0<0
)
                {
                    src_x_16
-=(Ax_16*
out_dst_x0);
                    src_y_16
-=(Ay_16*
out_dst_x0);
                }
                out_src_x0_16
=
src_x_16;
                out_src_y0_16
=
src_y_16;
                
return true
;
            }
            
else

            {
                src_x_16
+=Ax_16;
                src_y_16
+=
Ay_16;
            }
        }
        
return false
;
    }
    
void find_end(long dst_y,long dst_x0,long&
 out_dst_x1)
    {
        
long test_dst_x1=
out_dst_x1;
        
if (test_dst_x1<dst_x0) test_dst_x1=
dst_x0;

        
long src_x_16=Ax_16*test_dst_x1 + Bx_16*dst_y +
 Cx_16;
        
long src_y_16=Ay_16*test_dst_x1 + By_16*dst_y +
 Cy_16;
        
if
 (is_border_src(src_x_16,src_y_16))
        {
            
++
test_dst_x1;
            src_x_16
+=
Ax_16;
            src_y_16
+=
Ay_16;
            
while
 (is_border_src(src_x_16,src_y_16))
            {
                
++
test_dst_x1;
                src_x_16
+=
Ax_16;
                src_y_16
+=
Ay_16;
            }
            out_dst_x1
=
test_dst_x1;
        }
        
else

        {
            src_x_16
-=Ax_16;
            src_y_16
-=
Ay_16;
            
while (!
is_border_src(src_x_16,src_y_16))
            {
                
--
test_dst_x1;
                src_x_16
-=
Ax_16;
                src_y_16
-=
Ay_16;
            }
            out_dst_x1
=
test_dst_x1;
        }
    }

    inline 
void
 update_out_dst_x_in()
    {
        
if ((0==border_width)||(out_dst_x0_boder>=
out_dst_x1_boder) )
        {
            out_dst_x0_in
=
out_dst_x0_boder;
            out_dst_x1_in
=
out_dst_x1_boder;
        }
        
else

        {
            
long src_x_16=out_src_x0_16;
            
long src_y_16=
out_src_y0_16;
            
long i=
out_dst_x0_boder;
            
while (i<
out_dst_x1_boder)
            {
                
if (is_in_src(src_x_16,src_y_16)) break
;
                src_x_16
+=
Ax_16;
                src_y_16
+=
Ay_16;
                
++
i;
            }
            out_dst_x0_in
=
i;

            src_x_16
=out_src_x0_16+(out_dst_x1_boder-out_dst_x0_boder)*
Ax_16;
            src_y_16
=out_src_y0_16+(out_dst_x1_boder-out_dst_x0_boder)*
Ay_16;
            i
=
out_dst_x1_boder;
            
while (i>
out_dst_x0_in)
            {
                src_x_16
-=
Ax_16;
                src_y_16
-=
Ay_16;
                
if (is_in_src(src_x_16,src_y_16)) break
;
                
--
i;
            }
            out_dst_x1_in
=
i;
        }
    }
    inline 
void
 update_out_dst_up_x()
    {
        
if (cur_dst_up_x0<0
)
            out_dst_x0_boder
=0
;
        
else

            out_dst_x0_boder
=cur_dst_up_x0;
        
if (cur_dst_up_x1>=
dst_width)
            out_dst_x1_boder
=
dst_width;
        
else

            out_dst_x1_boder
=cur_dst_up_x1;
        update_out_dst_x_in();
    }
    inline 
void
 update_out_dst_down_x()
    {
        
if (cur_dst_down_x0<0
)
            out_dst_x0_boder
=0
;
        
else

            out_dst_x0_boder
=cur_dst_down_x0;
        
if (cur_dst_down_x1>=
dst_width)
            out_dst_x1_boder
=
dst_width;
        
else

            out_dst_x1_boder
=cur_dst_down_x1;
        update_out_dst_x_in();
    }

public
:
    
long
 out_src_x0_16;
    
long
 out_src_y0_16;

    
long
 out_dst_up_y;
    
long
 out_dst_down_y;

    
long
 out_dst_x0_boder;
    
long
 out_dst_x0_in;
    
long
 out_dst_x1_in;
    
long
 out_dst_x1_boder;

public
:
    
bool inti_clip(double move_x,double move_y,unsigned long
 aborder_width)
    {
        border_width
=
aborder_width;

        
//計算src中心點映射到dst後的座標

        out_dst_down_y=(long)(src_height*0.5+move_y);
        cur_dst_down_x0
=(long)(src_width*0.5+
move_x);
        cur_dst_down_x1
=
cur_dst_down_x0;
        
//得到初始掃描線

        if (find_begin(out_dst_down_y,cur_dst_down_x0,cur_dst_down_x1))
            find_end(out_dst_down_y,cur_dst_down_x0,cur_dst_down_x1);
        out_dst_up_y
=
out_dst_down_y;
        cur_dst_up_x0
=
cur_dst_down_x0;
        cur_dst_up_x1
=
cur_dst_down_x1;
        update_out_dst_up_x();
        
return (cur_dst_down_x0<
cur_dst_down_x1);
    }
    
bool
 next_clip_line_down()
    {
        
++
out_dst_down_y;
        
if (!find_begin(out_dst_down_y,cur_dst_down_x0,cur_dst_down_x1)) return false
;
        find_end(out_dst_down_y,cur_dst_down_x0,cur_dst_down_x1);
        update_out_dst_down_x();
        
return (cur_dst_down_x0<
cur_dst_down_x1);
    }
    
bool
 next_clip_line_up()
    {
        
--
out_dst_up_y;
        
if (!find_begin(out_dst_up_y,cur_dst_up_x0,cur_dst_up_x1)) return false
;
        find_end(out_dst_up_y,cur_dst_up_x0,cur_dst_up_x1);
        update_out_dst_up_x();
        
return (cur_dst_up_x0<
cur_dst_up_x1);
    }
};


  b. 邊界插值的特殊處理
      對於“插值邊界以外”很簡單,不用處理直接跳過插值;
      對於“插值邊界以內”,也比較容易處理,直接調用快速的差值算法就可以了,不用擔心內存訪問問題;
        插值實現:
 (從《圖形圖像處理-之-高質量的快速的圖像縮放 中篇 二次線性插值和三次卷積插值》文章來的,後面不再說明)

    inline void BilInear_Fast(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        TARGB32
* PColor0=&Pixels(pic,x_16>>16,y_16>>16
);
        TARGB32
* PColor1=(TARGB32*)((TUInt8*)PColor0+
pic.byte_width); 
        unsigned 
long u_8=(unsigned char)(x_16>>8
);
        unsigned 
long v_8=(unsigned char)(y_16>>8
);
        unsigned 
long pm3_8=(u_8*v_8)>>8
;
        unsigned 
long pm2_8=u_8-
pm3_8;
        unsigned 
long pm1_8=v_8-
pm3_8;
        unsigned 
long pm0_8=256-pm1_8-pm2_8-
pm3_8;

        unsigned 
long Color=*(unsigned long*
)(PColor0);
        unsigned 
long BR=(Color & 0x00FF00FF)*
pm0_8;
        unsigned 
long GA=((Color & 0xFF00FF00)>>8)*
pm0_8;
                      Color
=((unsigned long*)(PColor0))[1
];
                      GA
+=((Color & 0xFF00FF00)>>8)*
pm2_8;
                      BR
+=(Color & 0x00FF00FF)*
pm2_8;
                      Color
=*(unsigned long*
)(PColor1);
                      GA
+=((Color & 0xFF00FF00)>>8)*
pm1_8;
                      BR
+=(Color & 0x00FF00FF)*
pm1_8;
                      Color
=((unsigned long*)(PColor1))[1
];
                      GA
+=((Color & 0xFF00FF00)>>8)*
pm3_8;
                      BR
+=(Color & 0x00FF00FF)*
pm3_8;

        
*(unsigned long*)result=(GA & 0xFF00FF00)|((BR & 0xFF00FF00)>>8
);
    }

      對於“插值邊界”,就需要特殊處理了,很多插值旋轉的實現可能都在這裏打了折扣;要想完美的解決
   這塊區域,可以引入AlphaBlend(帶Alpha通道的顏色混合) ;
    
      其實AlphaBlend的原理也很簡單,就是按不同的比例混合兩種顏色:
         new_color=dst_color*(1-alpha)+src_color*alpha;
      對於ARGB32bit顏色,需要用該公式分別處理4個顏色通道,並假設Alpha爲[0..255]的整數,那麼完整的實現函數爲:

    inline TARGB32 AlphaBlend(TARGB32 dst,TARGB32 src)
    {
        
//
AlphaBlend顏色混合公式(對其中的每個顏色分量分別處理):
        
//
 new_color=(dst_color*(255-src_color.alpha)+src_color*src_color.alpha)/255

        
//提示:除法指令是很慢的操作,但vc2005可以把x/255編譯爲完全等價的"(x*M)>>N"類似的快速計算代碼;

        unsigned long a=src.a;
        
//
if (a==0) return dst;
        
//else if (a==255) return src;

        unsigned long ra=255-a;
        unsigned 
long result_b=(dst.b*ra+src.b*a)/255
;
        unsigned 
long result_g=(dst.g*ra+src.g*a)/255
;
        unsigned 
long result_r=(dst.r*ra+src.r*a)/255
;
        unsigned 
long result_a=(dst.a*ra+a*a)/255
;
        unsigned 
long result=(result_b) | (result_g<<8| (result_r<<16| (result_a<<24
);
        
return *(TARGB32*)&
result;
    }

    優化AlphaBlend,顏色處理中,也可以這樣近似計算: x/255  =>  x>>8 ,所以有:

    inline TARGB32 AlphaBlend(TARGB32 dst,TARGB32 src)
    {
        unsigned 
long a=
src.a;
        unsigned 
long ra=255-
a;
        unsigned 
long result_b=(dst.b*ra+src.b*a)>>8
;
        unsigned 
long result_g=(dst.g*ra+src.g*a)>>8
;
        unsigned 
long result_r=(dst.r*ra+src.r*a)>>8
;
        unsigned 
long result_a=(dst.a*ra+a*a)>>8
;
        unsigned 
long result=(result_b) | (result_g<<8| (result_r<<16| (result_a<<24
);
        
return *(TARGB32*)&
result;
    }

    (dst*(255-alpha)+src*alpha)>>8 可以近似爲:(dst*(256-alpha)+src*alpha)>>8
         即 (dst<<8+(src-dst)*alpha)>>8  從而用一個移位代替一個乘法 (*256會被優化爲移位)

    inline TARGB32 AlphaBlend(TARGB32 dst,TARGB32 src)
    {
        
long a=
src.a;
        unsigned 
long result_b=((unsigned long)(((long)dst.b)*256+((long)src.b-(long)dst.b)*a))>>8
;
        unsigned 
long result_g=((unsigned long)(((long)dst.g)*256+((long)src.g-(long)dst.g)*a))>>8
;
        unsigned 
long result_r=((unsigned long)(((long)dst.r)*256+((long)src.r-(long)dst.r)*a))>>8
;
        unsigned 
long result_a=((unsigned long)(((long)dst.a)*256+((long)a-(long)dst.a)*a))>>8
;
        unsigned 
long result=(result_b) | (result_g<<8| (result_r<<16| (result_a<<24
);
        
return *(TARGB32*)&
result;
    }


    繼續優化,同時運算兩路顏色分量的AlphaBlend實現:

    inline TARGB32 AlphaBlend(TARGB32 dst,TARGB32 src)
    {
        unsigned 
long Src_ARGB=*(unsigned long*)&
src;
        unsigned 
long Dst_ARGB=*(unsigned long*)&
dst;
        unsigned 
long a=Src_ARGB>>24
;
        unsigned 
long ra=255-
a;
        unsigned 
long result_RB=((Dst_ARGB & 0x00FF00FF)*ra + (Src_ARGB & 0x00FF00FF)*
a);
        unsigned 
long result_AG=(((Dst_ARGB & 0xFF00FF00)>>8)*ra + ((Src_ARGB & 0xFF00FF00)>>8)*
a);
        unsigned 
long result=((result_RB & 0xFF00FF00)>>8| (result_AG & 0xFF00FF00
);
        
return *(TARGB32*)&
result;
    }

    
    回到我們的主線:完美解決“旋轉插值邊界”
    怎麼利用AlphaBlend呢?  我們可以在處理邊界的時候,對於顏色數據在圖片內的顏色,把Alpha通道分量設置爲255,
在圖片外的顏色數據(使用Pixels_Bound會返回最接近的一個內部顏色)的Alpha通道分量設置爲0;
    這個任務就交給邊界插值函數了:

    inline void BilInear_Border(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        unsigned 
long x0=(x_16>>16
);
        unsigned 
long y0=(y_16>>16
);

        TARGB32 pixel[
4
];
        
bool
 IsInPic;
        pixel[
0]=
Pixels_Bound(pic,x0,y0,IsInPic);
        
if (!IsInPic) pixel[0].a=0else pixel[0].a=255
;
        pixel[
2]=Pixels_Bound(pic,x0,y0+1
,IsInPic);
        
if (!IsInPic) pixel[2].a=0else pixel[2].a=255
;
        pixel[
1]=Pixels_Bound(pic,x0+1
,y0,IsInPic);
        
if (!IsInPic) pixel[1].a=0else pixel[1].a=255
;
        pixel[
3]=Pixels_Bound(pic,x0+1,y0+1
,IsInPic);
        
if (!IsInPic) pixel[3].a=0else pixel[3].a=255
;
        
        TPicRegion npic;
        npic.pdata     
=&pixel[0
];
        npic.byte_width
=2*sizeof
(TARGB32);
        
//
npic.width     =2;
        
//npic.height    =2;

        BilInear_Fast(npic,(unsigned short)x_16,(unsigned short)y_16,result);
    }

    返回的顏色中的Alpha的值就代表了顏色的有效強度(一般介於0..255之間);
    那麼,對邊界上的插值就可以用類似這樣的代碼處理好了:
        TARGB32 tmp_color;
        BilInear_Border(SrcPic,srcx0_16,srcy0_16,&tmp_color);
        pDstLine[x]=AlphaBlend(pDstLine[x],tmp_color);

  c. OK,給出完整的函數:

void PicRotary_BilInear_CopyLine(TARGB32* pDstLine,long dst_border_x0,long dst_in_x0,long dst_in_x1,long dst_border_x1,
                        
const TPicRegion& SrcPic,long srcx0_16,long srcy0_16,long Ax_16,long
 Ay_16)
{
    
long
 x;
    
for (x=dst_border_x0;x<dst_in_x0;++
x)
    {
        TARGB32 src_color;
        BilInear_Border(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        pDstLine[x]
=
AlphaBlend(pDstLine[x],src_color);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    
for (x=dst_in_x0;x<dst_in_x1;++
x)
    {
        BilInear_Fast(SrcPic,srcx0_16,srcy0_16,
&
pDstLine[x]);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    
for (x=dst_in_x1;x<dst_border_x1;++
x)
    {
        TARGB32 src_color;
        BilInear_Border(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        pDstLine[x]
=
AlphaBlend(pDstLine[x],src_color);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
}

void PicRotaryBilInear(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double
 move_y)
{
    
if ( (fabs(ZoomX*Src.width)<1.0e-4|| (fabs(ZoomY*Src.height)<1.0e-4) ) return//太小的縮放比例認爲已經不可見

    double tmprZoomXY=1.0/(ZoomX*ZoomY);  
    
double rZoomX=tmprZoomXY*
ZoomY;
    
double rZoomY=tmprZoomXY*
ZoomX;
    
double
 sinA,cosA;
    SinCos(RotaryAngle,sinA,cosA);
    
long Ax_16=(long)(rZoomX*cosA*(1<<16
)); 
    
long Ay_16=(long)(rZoomX*sinA*(1<<16
)); 
    
long Bx_16=(long)(-rZoomY*sinA*(1<<16
)); 
    
long By_16=(long)(rZoomY*cosA*(1<<16
)); 
    
double rx0=Src.width*0.5;  //(rx0,ry0)爲旋轉中心 

    double ry0=Src.height*0.5
    
long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16
));
    
long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16
)); 

    TRotaryClipData rcData;
    rcData.Ax_16
=
Ax_16;
    rcData.Bx_16
=
Bx_16;
    rcData.Cx_16
=
Cx_16;
    rcData.Ay_16
=
Ay_16;
    rcData.By_16
=
By_16;
    rcData.Cy_16
=
Cy_16;
    rcData.dst_width
=
Dst.width;
    rcData.dst_height
=
Dst.height;
    rcData.src_width
=
Src.width;
    rcData.src_height
=
Src.height;
    
if (!rcData.inti_clip(move_x,move_y,1)) return
;

    TARGB32
* pDstLine=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_down_y);
    
while (true//to down

    {
        
long y=
rcData.out_dst_down_y;
        
if (y>=Dst.height) break
;
        
if (y>=0
)
        {
            PicRotary_BilInear_CopyLine(pDstLine,rcData.out_dst_x0_boder,rcData.out_dst_x0_in,
                rcData.out_dst_x1_in,rcData.out_dst_x1_boder,Src,rcData.out_src_x0_16,rcData.out_src_y0_16,Ax_16,Ay_16);
        }
        
if (!rcData.next_clip_line_down()) break
;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
   
    pDstLine
=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_up_y);
    
while (rcData.next_clip_line_up()) //to up 

    {
        
long y=
rcData.out_dst_up_y;
        
if (y<0break
;
        ((TUInt8
*&)pDstLine)-=
Dst.byte_width;
        
if (y<
Dst.height)
        {
            PicRotary_BilInear_CopyLine(pDstLine,rcData.out_dst_x0_boder,rcData.out_dst_x0_in,
                rcData.out_dst_x1_in,rcData.out_dst_x1_boder,Src,rcData.out_src_x0_16,rcData.out_src_y0_16,Ax_16,Ay_16);
        }
    }
}


//注:測試圖片都是800*600的圖片旋轉到1004*1004的圖片中心 測試成績取各個旋轉角度的平均速度值
////////////////////////////////////////////////////////////////////////////////
//速度測試:                 
//==============================================================================
// PicRotaryBilInear         68.9 fps
//////////////////////////////////////////////////////////////////////////////// 


二次線性插值旋轉結果圖示:

   
                  30度                         60度                        90度

   
                 120度                        150度                       180度

  
                 210度                        240度                       270度

    
                 300度                        330度                       360度

 

C:用MMX指令來改進二次線性插值的旋轉

    inline TARGB32 AlphaBlend_MMX(TARGB32 dst,TARGB32 src)
    {
        unsigned 
long
 result;
        asm
        {
            PXOR      MM7,MM7
            MOVD      MM0,src
            MOVD      MM2,dst
            PUNPCKLBW MM0,MM7
            PUNPCKLBW MM2,MM7
            MOVQ      MM1,MM0
            PUNPCKHWD MM1,MM1
            PSUBW     MM0,MM2
            PUNPCKHDQ MM1,MM1
            PSLLW     MM2,
8

            PMULLW    MM0,MM1
            PADDW     MM2,MM0
            PSRLW     MM2,
8
            PACKUSWB  MM2,MM7
            MOVD      result,MM2
        }
        
return *(TARGB32*)&result;
    }

    
void __declspec(naked) __stdcall BilInear_Fast_MMX(const TPicRegion& pic,const long x_16,const long y_16,TARGB32*
 result)
    {
        asm
        {    
              mov       edx,[esp
+12//y_16

              mov       eax,[esp+8]  //x_16
              PXOR      mm7,mm7
              shl       edx,
16

              shl       eax,
16
              shr       edx,
24 //edx=v_8
              shr       eax,24 //eax=u_8
              MOVD      MM6,edx
              MOVD      MM5,eax
              mov       ecx,[esp
+4]//pic

              mov       edx,[esp+12]//y_16
              mov       eax,[ecx]TPicRegion.byte_width
              sar       edx,
16

              imul      edx,eax
              add       edx,[ecx]TPicRegion.pdata
              add       eax,edx

              mov       ecx,[esp
+8//x_16
              sar       ecx,16     //srcx_16>>16

              MOVD         MM2,dword ptr [eax
+ecx*4]  //MM2=Color1
              MOVD         MM0,dword ptr [eax+ecx*4+4]//MM0=Color3
              PUNPCKLWD    MM5,MM5
              PUNPCKLWD    MM6,MM6
              MOVD         MM3,dword ptr [edx
+ecx*4]  //MM3=Color0

              MOVD         MM1,dword ptr [edx+ecx*4+4]//MM1=Color2
              PUNPCKLDQ    MM5,MM5 //mm5=u_8
              PUNPCKLBW    MM0,MM7
              PUNPCKLBW    MM1,MM7
              PUNPCKLBW    MM2,MM7
              PUNPCKLBW    MM3,MM7
              PSUBw        MM0,MM2
              PSUBw        MM1,MM3
              PSLLw        MM2,
8

              PSLLw        MM3,
8
              PMULlw       MM0,MM5
              PMULlw       MM1,MM5
              PUNPCKLDQ    MM6,MM6 
//mm6=v_8
              PADDw        MM0,MM2
              PADDw        MM1,MM3

              PSRLw        MM0,
8

              PSRLw        MM1,
8
              PSUBw        MM0,MM1
              PSLLw        MM1,
8
              PMULlw       MM0,MM6
              mov       eax,[esp
+16]//result
              PADDw        MM0,MM1

              PSRLw        MM0,
8

              PACKUSwb     MM0,MM7
              movd      [eax],MM0 

              
//emms
              ret 16
        }
    }

    
void BilInear_Border_MMX(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        unsigned 
long x0=(x_16>>16
);
        unsigned 
long y0=(y_16>>16
);

        TARGB32 pixel[
4
];
        
bool
 IsInPic;
        pixel[
0]=
Pixels_Bound(pic,x0,y0,IsInPic);
        
if (!IsInPic) pixel[0].a=0else pixel[0].a=255
;
        pixel[
2]=Pixels_Bound(pic,x0,y0+1
,IsInPic);
        
if (!IsInPic) pixel[2].a=0else pixel[2].a=255
;
        pixel[
1]=Pixels_Bound(pic,x0+1
,y0,IsInPic);
        
if (!IsInPic) pixel[1].a=0else pixel[1].a=255
;
        pixel[
3]=Pixels_Bound(pic,x0+1,y0+1
,IsInPic);
        
if (!IsInPic) pixel[3].a=0else pixel[3].a=255
;
        
        TPicRegion npic;
        npic.pdata     
=&pixel[0
];
        npic.byte_width
=2*sizeof
(TARGB32);
        
//
npic.width     =2;
        
//npic.height    =2;

        BilInear_Fast_MMX(npic,(unsigned short)x_16,(unsigned short)y_16,result);
    }

void PicRotary_BilInear_CopyLine_MMX(TARGB32* pDstLine,long dst_border_x0,long dst_in_x0,long dst_in_x1,long
 dst_border_x1,
                        
const TPicRegion& SrcPic,long srcx0_16,long srcy0_16,long Ax_16,long
 Ay_16)
{
    
long
 x;
    
for (x=dst_border_x0;x<dst_in_x0;++
x)
    {
        TARGB32 src_color;
        BilInear_Border_MMX(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        pDstLine[x]
=
AlphaBlend_MMX(pDstLine[x],src_color);        
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    
for (x=dst_in_x0;x<dst_in_x1;++
x)
    {
        BilInear_Fast_MMX(SrcPic,srcx0_16,srcy0_16,
&
pDstLine[x]);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    
for (x=dst_in_x1;x<dst_border_x1;++
x)
    {
        TARGB32 src_color;
        BilInear_Border_MMX(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        pDstLine[x]
=
AlphaBlend_MMX(pDstLine[x],src_color);        
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    asm  emms
}

void PicRotaryBilInear_MMX(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double
 move_y)
{
    
if ( (fabs(ZoomX*Src.width)<1.0e-4|| (fabs(ZoomY*Src.height)<1.0e-4) ) return//太小的縮放比例認爲已經不可見

    double tmprZoomXY=1.0/(ZoomX*ZoomY);  
    
double rZoomX=tmprZoomXY*
ZoomY;
    
double rZoomY=tmprZoomXY*
ZoomX;
    
double
 sinA,cosA;
    SinCos(RotaryAngle,sinA,cosA);
    
long Ax_16=(long)(rZoomX*cosA*(1<<16
)); 
    
long Ay_16=(long)(rZoomX*sinA*(1<<16
)); 
    
long Bx_16=(long)(-rZoomY*sinA*(1<<16
)); 
    
long By_16=(long)(rZoomY*cosA*(1<<16
)); 
    
double rx0=Src.width*0.5;  //(rx0,ry0)爲旋轉中心 

    double ry0=Src.height*0.5
    
long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16
));
    
long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16
)); 

    TRotaryClipData rcData;
    rcData.Ax_16
=
Ax_16;
    rcData.Bx_16
=
Bx_16;
    rcData.Cx_16
=
Cx_16;
    rcData.Ay_16
=
Ay_16;
    rcData.By_16
=
By_16;
    rcData.Cy_16
=
Cy_16;
    rcData.dst_width
=
Dst.width;
    rcData.dst_height
=
Dst.height;
    rcData.src_width
=
Src.width;
    rcData.src_height
=
Src.height;
    
if (!rcData.inti_clip(move_x,move_y,1)) return
;

    TARGB32
* pDstLine=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_down_y);
    
while (true//to down

    {
        
long y=
rcData.out_dst_down_y;
        
if (y>=Dst.height) break
;
        
if (y>=0
)
        {
            PicRotary_BilInear_CopyLine_MMX(pDstLine,rcData.out_dst_x0_boder,rcData.out_dst_x0_in,
                rcData.out_dst_x1_in,rcData.out_dst_x1_boder,Src,rcData.out_src_x0_16,rcData.out_src_y0_16,Ax_16,Ay_16);
        }
        
if (!rcData.next_clip_line_down()) break
;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
   
    pDstLine
=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_up_y);
    
while (rcData.next_clip_line_up()) //to up 

    {
        
long y=
rcData.out_dst_up_y;
        
if (y<0break
;
        ((TUInt8
*&)pDstLine)-=
Dst.byte_width;
        
if (y<
Dst.height)
        {
            PicRotary_BilInear_CopyLine_MMX(pDstLine,rcData.out_dst_x0_boder,rcData.out_dst_x0_in,
                rcData.out_dst_x1_in,rcData.out_dst_x1_boder,Src,rcData.out_src_x0_16,rcData.out_src_y0_16,Ax_16,Ay_16);
        }
    }
}

 

//注:測試圖片都是800*600的圖片旋轉到1004*1004的圖片中心 測試成績取各個旋轉角度的平均速度值
////////////////////////////////////////////////////////////////////////////////
//速度測試:                 
//==============================================================================
// PicRotaryBilInear_MMX    100.2 fps
//////////////////////////////////////////////////////////////////////////////// 


D:三次卷積插值的旋轉
   (實現就比較簡單了,幾乎就是拷貝代碼,然後稍微改寫幾個單詞:)
  (很多代碼從《圖形圖像處理-之-高質量的快速的圖像縮放 中篇 二次線性插值和三次卷積插值》文章來的)

 

        inline double SinXDivX(double x) 
        {
            
//該函數計算插值曲線sin(x*PI)/(x*PI)的值 //
PI=3.1415926535897932385; 
            
//下面是它的近似擬合表達式

            const float a = -1//a還可以取 a=-2,-1,-0.75,-0.5等等,起到調節銳化或模糊程度的作用

            
if (x<0) x=-x; //x=abs(x);
            double x2=x*x;
            
double x3=x2*
x;
            
if (x<=1
)
              
return (a+2)*x3 - (a+3)*x2 + 1
;
            
else if (x<=2

              
return a*x3 - (5*a)*x2 + (8*a)*- (4*
a);
            
else

              
return 0;
        } 
        inline TUInt8 ColorBound(
long
 Color)
        {
            
if (Color<=0
)
                
return 0
;
            
else if (Color>=255
)
                
return 255
;
            
else

                
return Color;
        }

    
static long SinXDivX_Table_8[(2<<8)+1
];
    
class
 _CAutoInti_SinXDivX_Table {
    
private

        
void
 _Inti_SinXDivX_Table()
        {
            
for (long i=0;i<=(2<<8);++
i)
                SinXDivX_Table_8[i]
=long(0.5+256*SinXDivX(i*(1.0/(256
))));
        };
    
public
:
        _CAutoInti_SinXDivX_Table() { _Inti_SinXDivX_Table(); }
    };
    
static
 _CAutoInti_SinXDivX_Table __tmp_CAutoInti_SinXDivX_Table;



    
void ThreeOrder_Fast(const TPicRegion& pic,const long x_16,const long y_16,TARGB32*
 result)
    {
        
long u_8=(unsigned char)((x_16)>>8
);
        
long v_8=(unsigned char)((y_16)>>8
);
        
const TARGB32* pixel=&Pixels(pic,(x_16>>16)-1,(y_16>>16)-1
);
        
long pic_byte_width=
pic.byte_width;

        
long au_8[4],av_8[4
];
        
//
        au_8[0]=SinXDivX_Table_8[(1<<8)+
u_8];
        au_8[
1]=
SinXDivX_Table_8[u_8];
        au_8[
2]=SinXDivX_Table_8[(1<<8)-
u_8];
        au_8[
3]=SinXDivX_Table_8[(2<<8)-
u_8];
        av_8[
0]=SinXDivX_Table_8[(1<<8)+
v_8];
        av_8[
1]=
SinXDivX_Table_8[v_8];
        av_8[
2]=SinXDivX_Table_8[(1<<8)-
v_8];
        av_8[
3]=SinXDivX_Table_8[(2<<8)-
v_8];

        
long sR=0,sG=0,sB=0,sA=0
;
        
for (long i=0;i<4;++
i)
        {
            
long aA=au_8[0]*pixel[0].a + au_8[1]*pixel[1].a + au_8[2]*pixel[2].a + au_8[3]*pixel[3
].a;
            
long aR=au_8[0]*pixel[0].r + au_8[1]*pixel[1].r + au_8[2]*pixel[2].r + au_8[3]*pixel[3
].r;
            
long aG=au_8[0]*pixel[0].g + au_8[1]*pixel[1].g + au_8[2]*pixel[2].g + au_8[3]*pixel[3
].g;
            
long aB=au_8[0]*pixel[0].b + au_8[1]*pixel[1].b + au_8[2]*pixel[2].b + au_8[3]*pixel[3
].b;
            sA
+=aA*
av_8[i];
            sR
+=aR*
av_8[i];
            sG
+=aG*
av_8[i];
            sB
+=aB*
av_8[i];
            ((TUInt8
*&)pixel)+=
pic_byte_width;
        }

        
*(unsigned long*)result=ColorBound(sB>>16| (ColorBound(sG>>16)<<8| (ColorBound(sR>>16)<<16)| (ColorBound(sA>>16)<<24
);
    }

    
void ThreeOrder_Border(const TPicRegion& pic,const long x_16,const long y_16,TARGB32*
 result)
    {
        unsigned 
long x0_sub1=(x_16>>16)-1
;
        unsigned 
long y0_sub1=(y_16>>16)-1
;
        
long u_16_add1=((unsigned short)(x_16))+(1<<16
);
        
long v_16_add1=((unsigned short)(y_16))+(1<<16
);

        TARGB32 pixel[
16
];
        
long
 i,j;

        
for (i=0;i<4;++
i)
        {
            
long y=y0_sub1+
i;
            
for (j=0;j<4;++
j)
            {
                
long x=x0_sub1+
j;
                
bool
 IsInPic;
                pixel[i
*4+j]=
Pixels_Bound(pic,x,y,IsInPic);
                
if (!IsInPic) pixel[i*4+j].a=0else pixel[i*4+j].a=255
;
            }
        }
        
        TPicRegion npic;
        npic.pdata     
=&pixel[0
];
        npic.byte_width
=4*sizeof
(TARGB32);
        
//
npic.width     =4;
        
//npic.height    =4;

        ThreeOrder_Fast(npic,u_16_add1,v_16_add1,result);
    }

void PicRotary_ThreeOrder_CopyLine(TARGB32* pDstLine,long dst_border_x0,long dst_in_x0,long dst_in_x1,long
 dst_border_x1,
                        
const TPicRegion& SrcPic,long srcx0_16,long srcy0_16,long Ax_16,long
 Ay_16)
{
    
long
 x;
    
for (x=dst_border_x0;x<dst_in_x0;++
x)
    {

        TARGB32 src_color;
        ThreeOrder_Border(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        pDstLine[x]
=
AlphaBlend(pDstLine[x],src_color);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    
for (x=dst_in_x0;x<dst_in_x1;++
x)
    {
        ThreeOrder_Fast(SrcPic,srcx0_16,srcy0_16,
&
pDstLine[x]);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    
for (x=dst_in_x1;x<dst_border_x1;++
x)
    {
        TARGB32 src_color;
        ThreeOrder_Border(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        pDstLine[x]
=
AlphaBlend(pDstLine[x],src_color);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
}

void PicRotaryThreeOrder(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double
 move_y)
{
    
if ( (fabs(ZoomX*Src.width)<1.0e-4|| (fabs(ZoomY*Src.height)<1.0e-4) ) return//太小的縮放比例認爲已經不可見

    double tmprZoomXY=1.0/(ZoomX*ZoomY);  
    
double rZoomX=tmprZoomXY*
ZoomY;
    
double rZoomY=tmprZoomXY*
ZoomX;
    
double
 sinA,cosA;
    SinCos(RotaryAngle,sinA,cosA);
    
long Ax_16=(long)(rZoomX*cosA*(1<<16
)); 
    
long Ay_16=(long)(rZoomX*sinA*(1<<16
)); 
    
long Bx_16=(long)(-rZoomY*sinA*(1<<16
)); 
    
long By_16=(long)(rZoomY*cosA*(1<<16
)); 
    
double rx0=Src.width*0.5;  //(rx0,ry0)爲旋轉中心 

    double ry0=Src.height*0.5
    
long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16
));
    
long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16
)); 

    TRotaryClipData rcData;
    rcData.Ax_16
=
Ax_16;
    rcData.Bx_16
=
Bx_16;
    rcData.Cx_16
=
Cx_16;
    rcData.Ay_16
=
Ay_16;
    rcData.By_16
=
By_16;
    rcData.Cy_16
=
Cy_16;
    rcData.dst_width
=
Dst.width;
    rcData.dst_height
=
Dst.height;
    rcData.src_width
=
Src.width;
    rcData.src_height
=
Src.height;
    
if (!rcData.inti_clip(move_x,move_y,2)) return
;

    TARGB32
* pDstLine=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_down_y);
    
while (true//to down

    {
        
long y=
rcData.out_dst_down_y;
        
if (y>=Dst.height) break
;
        
if (y>=0
)
        {
            PicRotary_ThreeOrder_CopyLine(pDstLine,rcData.out_dst_x0_boder,rcData.out_dst_x0_in,
                rcData.out_dst_x1_in,rcData.out_dst_x1_boder,Src,rcData.out_src_x0_16,rcData.out_src_y0_16,Ax_16,Ay_16);
        }
        
if (!rcData.next_clip_line_down()) break
;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
   
    pDstLine
=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_up_y);
    
while (rcData.next_clip_line_up()) //to up 

    {
        
long y=
rcData.out_dst_up_y;
        
if (y<0break
;
        ((TUInt8
*&)pDstLine)-=
Dst.byte_width;
        
if (y<
Dst.height)
        {
            PicRotary_ThreeOrder_CopyLine(pDstLine,rcData.out_dst_x0_boder,rcData.out_dst_x0_in,
                rcData.out_dst_x1_in,rcData.out_dst_x1_boder,Src,rcData.out_src_x0_16,rcData.out_src_y0_16,Ax_16,Ay_16);
        }
    }
}

//注:測試圖片都是800*600的圖片旋轉到1004*1004的圖片中心 測試成績取各個旋轉角度的平均速度值
////////////////////////////////////////////////////////////////////////////////
//速度測試:                 
//==============================================================================
// PicRotaryThreeOrder       22.8 fps
////////////////////////////////////////////////////////////////////////////////   


三次卷積插值旋轉結果圖示:

   
                  30度                         60度                        90度

   
                 120度                        150度                       180度

  
                 210度                        240度                       270度

    
                 300度                        330度                       360度

 

E:用MMX優化三次卷積插值的旋轉

//注:測試圖片都是800*600的圖片旋轉到1004*1004的圖片中心 測試成績取各個旋轉角度的平均速度值
////////////////////////////////////////////////////////////////////////////////
//速度測試:                 
//==============================================================================
// PicRotaryThreeOrder_MMX   44.2 fps
////////////////////////////////////////////////////////////////////////////////   

    typedef   unsigned long TMMXData32;
    
static TMMXData32 SinXDivX_Table_MMX[(2<<8)+1
];
    
class
 _CAutoInti_SinXDivX_Table_MMX {
    
private

        
void
 _Inti_SinXDivX_Table_MMX()
        {
            
for (long i=0;i<=(2<<8);++
i)
            {
                unsigned 
short t=long(0.5+(1<<14)*SinXDivX(i*(1.0/(256
))));
                unsigned 
long tl=| (((unsigned long)t)<<16
);
                SinXDivX_Table_MMX[i]
=
tl;
            }
        };
    
public
:
        _CAutoInti_SinXDivX_Table_MMX() { _Inti_SinXDivX_Table_MMX(); }
    };
    
static
 _CAutoInti_SinXDivX_Table_MMX __tmp_CAutoInti_SinXDivX_Table_MMX;


    
void
 __declspec(naked) _private_ThreeOrder_Fast_MMX()
    {
        asm
        {
            movd        mm1,dword ptr [edx]
            movd        mm2,dword ptr [edx
+4
]
            movd        mm3,dword ptr [edx
+8
]
            movd        mm4,dword ptr [edx
+12
]
            movd        mm5,dword ptr [(offset SinXDivX_Table_MMX)
+256*4+eax*4
]
            movd        mm6,dword ptr [(offset SinXDivX_Table_MMX)
+eax*4
]
            punpcklbw   mm1,mm7
            punpcklbw   mm2,mm7
            punpcklwd   mm5,mm5
            punpcklwd   mm6,mm6
            psllw       mm1,
7

            psllw       mm2,
7
            pmulhw      mm1,mm5
            pmulhw      mm2,mm6
            punpcklbw   mm3,mm7
            punpcklbw   mm4,mm7
            movd        mm5,dword ptr [(offset SinXDivX_Table_MMX)
+256*4+ecx*4]
            movd        mm6,dword ptr [(offset SinXDivX_Table_MMX)
+512*4+ecx*4
]
            punpcklwd   mm5,mm5
            punpcklwd   mm6,mm6
            psllw       mm3,
7

            psllw       mm4,
7
            pmulhw      mm3,mm5
            pmulhw      mm4,mm6
            paddsw      mm1,mm2
            paddsw      mm3,mm4
            movd        mm6,dword ptr [ebx] 
//v
            paddsw      mm1,mm3
            punpcklwd   mm6,mm6

            pmulhw      mm1,mm6
            add     edx,esi  
//+pic.byte_width

            paddsw      mm0,mm1

            ret
        }
    }

    inline
void ThreeOrder_Fast_MMX(const TPicRegion& pic,const long x_16,const long y_16,TARGB32*
 result)
    {
        asm
        {
            mov     ecx,pic
            mov     eax,y_16
            mov     ebx,x_16
            movzx   edi,ah 
//v_8

            mov     edx,[ecx+TPicRegion.pdata]
            shr     eax,
16

            mov     esi,[ecx
+TPicRegion.byte_width]
            dec     eax
            movzx   ecx,bh 
//u_8

            shr     ebx,16
            imul    eax,esi
            lea     edx,[edx
+ebx*4-4]
            add     edx,eax 
//pixel


            mov     eax,ecx
            neg     ecx

            pxor    mm7,mm7  
//0
            
//mov     edx,pixel

            pxor    mm0,mm0  //result=0
            
//lea     eax,auv_7


            lea    ebx,[(offset SinXDivX_Table_MMX)
+256*4+edi*4]
            call  _private_ThreeOrder_Fast_MMX
            lea    ebx,[(offset SinXDivX_Table_MMX)
+edi*4
]
            call  _private_ThreeOrder_Fast_MMX
            neg    edi
            lea    ebx,[(offset SinXDivX_Table_MMX)
+256*4+edi*4
]
            call  _private_ThreeOrder_Fast_MMX
            lea    ebx,[(offset SinXDivX_Table_MMX)
+512*4+edi*4
]
            call  _private_ThreeOrder_Fast_MMX

            psraw     mm0,
3

            mov       eax,result
            packuswb  mm0,mm7
            movd      [eax],mm0

            emms
        }
    }

    
void ThreeOrder_Border_MMX(const TPicRegion& pic,const long x_16,const long y_16,TARGB32* result)
    {
        unsigned 
long x0_sub1=(x_16>>16)-1
;
        unsigned 
long y0_sub1=(y_16>>16)-1
;
        
long u_16_add1=((unsigned short)(x_16))+(1<<16
);
        
long v_16_add1=((unsigned short)(y_16))+(1<<16
);

        TARGB32 pixel[
16
];
        
long
 i,j;

        
for (i=0;i<4;++
i)
        {
            
long y=y0_sub1+
i;
            
for (j=0;j<4;++
j)
            {
                
long x=x0_sub1+
j;
                
bool
 IsInPic;
                pixel[i
*4+j]=
Pixels_Bound(pic,x,y,IsInPic);
                
if (!IsInPic) pixel[i*4+j].a=0else pixel[i*4+j].a=255
;
            }
        }
        
        TPicRegion npic;
        npic.pdata     
=&pixel[0
];
        npic.byte_width
=4*sizeof
(TARGB32);
        
//
npic.width     =4;
        
//npic.height    =4;

        ThreeOrder_Fast_MMX(npic,u_16_add1,v_16_add1,result);
    }

void PicRotary_ThreeOrder_CopyLine_MMX(TARGB32* pDstLine,long dst_border_x0,long dst_in_x0,long dst_in_x1,long
 dst_border_x1,
                        
const TPicRegion& SrcPic,long srcx0_16,long srcy0_16,long Ax_16,long
 Ay_16)
{
    
long
 x;
    
for (x=dst_border_x0;x<dst_in_x0;++
x)
    {
        TARGB32 src_color;
        ThreeOrder_Border_MMX(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        pDstLine[x]
=
AlphaBlend_MMX(pDstLine[x],src_color);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    
for (x=dst_in_x0;x<dst_in_x1;++
x)
    {
        ThreeOrder_Fast_MMX(SrcPic,srcx0_16,srcy0_16,
&
pDstLine[x]);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    
for (x=dst_in_x1;x<dst_border_x1;++
x)
    {
        TARGB32 src_color;
        ThreeOrder_Border_MMX(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        pDstLine[x]
=
AlphaBlend_MMX(pDstLine[x],src_color);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    asm  emms
}

void PicRotaryThreeOrder_MMX(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double
 move_y)
{
    
if ( (fabs(ZoomX*Src.width)<1.0e-4|| (fabs(ZoomY*Src.height)<1.0e-4) ) return//太小的縮放比例認爲已經不可見

    double tmprZoomXY=1.0/(ZoomX*ZoomY);  
    
double rZoomX=tmprZoomXY*
ZoomY;
    
double rZoomY=tmprZoomXY*
ZoomX;
    
double
 sinA,cosA;
    SinCos(RotaryAngle,sinA,cosA);
    
long Ax_16=(long)(rZoomX*cosA*(1<<16
)); 
    
long Ay_16=(long)(rZoomX*sinA*(1<<16
)); 
    
long Bx_16=(long)(-rZoomY*sinA*(1<<16
)); 
    
long By_16=(long)(rZoomY*cosA*(1<<16
)); 
    
double rx0=Src.width*0.5;  //(rx0,ry0)爲旋轉中心 

    double ry0=Src.height*0.5
    
long Cx_16=(long)((-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0)*(1<<16
));
    
long Cy_16=(long)((-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0)*(1<<16
)); 

    TRotaryClipData rcData;
    rcData.Ax_16
=
Ax_16;
    rcData.Bx_16
=
Bx_16;
    rcData.Cx_16
=
Cx_16;
    rcData.Ay_16
=
Ay_16;
    rcData.By_16
=
By_16;
    rcData.Cy_16
=
Cy_16;
    rcData.dst_width
=
Dst.width;
    rcData.dst_height
=
Dst.height;
    rcData.src_width
=
Src.width;
    rcData.src_height
=
Src.height;
    
if (!rcData.inti_clip(move_x,move_y,2)) return
;

    TARGB32
* pDstLine=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_down_y);
    
while (true//to down

    {
        
long y=
rcData.out_dst_down_y;
        
if (y>=Dst.height) break
;
        
if (y>=0
)
        {
            PicRotary_ThreeOrder_CopyLine_MMX(pDstLine,rcData.out_dst_x0_boder,rcData.out_dst_x0_in,
                rcData.out_dst_x1_in,rcData.out_dst_x1_boder,Src,rcData.out_src_x0_16,rcData.out_src_y0_16,Ax_16,Ay_16);
        }
        
if (!rcData.next_clip_line_down()) break
;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
   
    pDstLine
=
Dst.pdata;
    ((TUInt8
*&)pDstLine)+=(Dst.byte_width*
rcData.out_dst_up_y);
    
while (rcData.next_clip_line_up()) //to up 

    {
        
long y=
rcData.out_dst_up_y;
        
if (y<0break
;
        ((TUInt8
*&)pDstLine)-=
Dst.byte_width;
        
if (y<
Dst.height)
        {
            PicRotary_ThreeOrder_CopyLine_MMX(pDstLine,rcData.out_dst_x0_boder,rcData.out_dst_x0_in,
                rcData.out_dst_x1_in,rcData.out_dst_x1_boder,Src,rcData.out_src_x0_16,rcData.out_src_y0_16,Ax_16,Ay_16);
        }
    }
}

 


F 效果圖:
 //程序使用的調用參數:
    const long testcount=2000;
    long dst_wh=1004;
    for (int i=0;i<testcount;++i)
    {
        double zoom=rand()*(1.0/RAND_MAX)+0.5;
        PicRotary_XXX(ppicDst,ppicSrc,rand()*(PI*2/RAND_MAX),zoom,zoom,((dst_wh+ppicSrc.width)*rand()*(1.0/RAND_MAX)-ppicSrc.width),(dst_wh+ppicSrc.height)*rand()*(1.0/RAND_MAX)-ppicSrc.height);
    }

   近鄰取樣插值旋轉效果圖:

   二次線性插值旋轉效果圖:

   三次卷積插值旋轉效果圖:

G:旋轉測試的結果放到一起:

//注:測試圖片都是800*600的圖片旋轉到1004*1004的圖片中心,測試成績取各個旋轉角度的平均速度值 
////////////////////////////////////////////////////////////////////////////////
//速度測試:  (測試CPU爲AMD64x2 4200+(2.37G),單線程)
//==============================================================================
// PicRotary3               280.9 fps
// PicRotarySEE             306.3 fps
// PicRotarySEE2            304.2 fps
//
// PicRotaryBilInear         68.9 fps
// PicRotaryBilInear_MMX    100.2 fps
// PicRotaryThreeOrder       22.8 fps
// PicRotaryThreeOrder_MMX   44.2 fps
////////////////////////////////////////////////////////////////////////////////

補充Intel Core2 4400上的測試成績:

////////////////////////////////////////////////////////////////////////////////
//速度測試:  (測試CPU爲Intel Core2 4400(2.00G)單線程)
//==============================================================================
// PicRotary3               334.9 fps
// PicRotarySEE             463.1 fps
// PicRotarySEE2            449.3 fps
//
// PicRotaryBilInear         68.9 fps
// PicRotaryBilInear_MMX    109.5 fps
// PicRotaryThreeOrder       24.0 fps
// PicRotaryThreeOrder_MMX   45.9 fps
////////////////////////////////////////////////////////////////////////////////

(針對大圖片的預讀緩衝區優化的旋轉請參見《下篇 補充話題》中的優化版本)

(對於旋轉的MipMap處理和三次線性插值,可以參考《圖形圖像處理-之-高質量的快速的圖像縮放 下篇 三次線性插值和MipMap鏈》文章)

(這裏爲了函數的獨立性和容易理解,都是拷貝代碼然後稍作修改;實際的程序中,建議把他們合併到同一個函數中,
減少代碼量,提高可維護性;  對於MMX、SSE、SSE2等的使用建議用CPUID指令測試看CPU是否支持這些指令,
動態決定調用不同的實現)

(歡迎指出文章中的錯誤、我沒有做到的優化、改進意見 等)

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章