圖形圖像處理-之-任意角度的高質量的快速的圖像旋轉 中篇 高質量的旋轉
[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:一些顏色和圖片的數據定義:
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=0; else if (x>=pic.width ) x=pic.width -1;
if (y<0) y=0; else 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. 邊界插值的特殊處理
對於“插值邊界以外”很簡單,不用處理直接跳過插值;
對於“插值邊界以內”,也比較容易處理,直接調用快速的差值算法就可以了,不用擔心內存訪問問題;
插值實現:
(從《圖形圖像處理-之-高質量的快速的圖像縮放 中篇 二次線性插值和三次卷積插值》文章來的,後面不再說明)
{
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]的整數,那麼完整的實現函數爲:
{
//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 ,所以有:
{
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會被優化爲移位)
{
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實現:
{
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;
這個任務就交給邊界插值函數了:
{
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=0; else pixel[0].a=255;
pixel[2]=Pixels_Bound(pic,x0,y0+1,IsInPic);
if (!IsInPic) pixel[2].a=0; else pixel[2].a=255;
pixel[1]=Pixels_Bound(pic,x0+1,y0,IsInPic);
if (!IsInPic) pixel[1].a=0; else pixel[1].a=255;
pixel[3]=Pixels_Bound(pic,x0+1,y0+1,IsInPic);
if (!IsInPic) pixel[3].a=0; else 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,給出完整的函數:
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<0) break;
((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指令來改進二次線性插值的旋轉
{
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=0; else pixel[0].a=255;
pixel[2]=Pixels_Bound(pic,x0,y0+1,IsInPic);
if (!IsInPic) pixel[2].a=0; else pixel[2].a=255;
pixel[1]=Pixels_Bound(pic,x0+1,y0,IsInPic);
if (!IsInPic) pixel[1].a=0; else pixel[1].a=255;
pixel[3]=Pixels_Bound(pic,x0+1,y0+1,IsInPic);
if (!IsInPic) pixel[3].a=0; else 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<0) break;
((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:三次卷積插值的旋轉
(實現就比較簡單了,幾乎就是拷貝代碼,然後稍微改寫幾個單詞:)
(很多代碼從《圖形圖像處理-之-高質量的快速的圖像縮放 中篇 二次線性插值和三次卷積插值》文章來的)
{
//該函數計算插值曲線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)*x - (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=0; else 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<0) break;
((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
////////////////////////////////////////////////////////////////////////////////
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=t | (((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=0; else 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<0) break;
((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是否支持這些指令,
動態決定調用不同的實現)
(歡迎指出文章中的錯誤、我沒有做到的優化、改進意見 等)