任意角度的高质量的快速的图像旋转 下篇 补充话题

来源:互联网 发布:python 智能模块 编辑:程序博客网 时间:2024/05/09 02:45
    图形图像处理-之-任意角度的高质量的快速的图像旋转 下篇 补充话题
                        
HouSisong@GMail.com   2007.06.29

 

(2009.03.09  可以到这里下载旋转算法的完整的可以编译的项目源代码: http://blog.csdn.net/housisong/archive/2009/03/09/3970925.aspx  )

(2007.07.15 添加对大图片旋转的预读缓冲区优化版本)

 

tag:图像旋转,任意角度,图像缩放,速度优化,定点数优化,近邻取样插值,二次线性插值,
   三次卷积插值,MipMap链,三次线性插值,MMX/SSE优化,CPU缓存优化,AlphaBlend,颜色混合,并行

摘要: 该文章是《任意角度的高质量的快速的图像旋转》的一些高级补充话题;
     给出了一个完整的Alpha混合的插值旋转实现;并尝试将旋转函数并行化,从而在多核电脑上获得更快的速度;添加优化预读缓冲区的函数实现版本,提高超大图片的旋转的速度;

任意角度的高质量的快速的图像旋转 全文 分为:
     上篇 纯软件的任意角度的快速旋转
     中篇 高质量的旋转
     下篇 补充话题


正文:
  为了便于讨论,这里只处理32bit的ARGB颜色;
  代码使用C++;涉及到汇编优化的时候假定为x86平台;使用的编译器为vc2005;
  为了代码的可读性,没有加入异常处理代码;
   测试使用的CPU为AMD64x2 4200+(2.37G) 和 Intel Core2 4400(2.00G);
  (基础代码参考《图形图像处理-之-任意角度的高质量的快速的图像旋转》系列前面的文章)


A:完整的Alpha混合的双线性插值旋转实现
  《高质量的旋转》中已经涉及到了边界的AlphaBlend的问题,这里顺水推舟的实现一个支持全图片Alpha通道Blend混合的双线性插值旋转函数;
   首先给出带完整Alpha通道的源图片:

              

   这张图片是带有8比特Alpha的32比特RGB真彩bmp图片;
   带的Alpha通道在工具里可能显示不出来,单独提取出来的图示:

               

   函数实现:  


    
void BilInear_BlendBorder_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
;
        pixel[
2]=Pixels_Bound(pic,x0,y0+1
,IsInPic);
        
if (!IsInPic) pixel[2].a=0

        pixel[
1]=Pixels_Bound(pic,x0+1
,y0,IsInPic);
        
if (!IsInPic) pixel[1].a=0
;
        pixel[
3]=Pixels_Bound(pic,x0+1,y0+1
,IsInPic);
        
if (!IsInPic) pixel[3].a=0
;
        
        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_BlendLine_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_BlendBorder_MMX(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        
if (src_color.a>0
)
            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)
    {
        TARGB32 src_color;
        BilInear_Fast_MMX(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        
if (src_color.a==255
)
            pDstLine[x]
=
src_color;  
        
else if (src_color.a>0
)
            pDstLine[x]
=
AlphaBlend_MMX(pDstLine[x],src_color);
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    
for (x=dst_in_x1;x<dst_border_x1;++
x)
    {
        TARGB32 src_color;
        BilInear_BlendBorder_MMX(SrcPic,srcx0_16,srcy0_16,
&
src_color);
        
if (src_color.a>0
)
            pDstLine[x]
=
AlphaBlend_MMX(pDstLine[x],src_color);        
        srcx0_16
+=
Ax_16;
        srcy0_16
+=
Ay_16;
    }
    asm  emms
}

void PicRotaryBlendBilInear_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_BlendLine_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_BlendLine_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);
        }
    }
}

  效果图:


B:在双核上并行三次卷积插值旋转的一个简单实现
  (假设图片旋转绘制到目的图片的中间)
  这里利用CWorkThreadPool来并行执行任务;
  (参见我的文章《并行计算简介和多核CPU编程Demo》,里面有CWorkThreadPool类的完整源代码)
  最容易想到的方案就是分成上下两部分分别调用PicRotaryThreeOrder_MMX,从而并行执行;

 

struct TRotaryThreeOrder_WorkData
{
    
const TPicRegion*
 Dst;
    
const TPicRegion*
 Src;
    
double
 RotaryAngle;
    
double
 ZoomX;
    
double
 ZoomY;
    
double
 move_x;
    
double
 move_y;
};

void RotaryThreeOrder_callback(void*
 wd)
{
    TRotaryThreeOrder_WorkData
* WorkData=(TRotaryThreeOrder_WorkData*
)wd;
    PicRotaryThreeOrder_MMX(
*WorkData->Dst,*WorkData->Src,WorkData->RotaryAngle,WorkData->ZoomX,WorkData->ZoomY,WorkData->move_x,WorkData->
move_y);
}


void PicRotaryThreeOrder_MMX_parallel2(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double
 move_y)
{
    TRotaryThreeOrder_WorkData work_list[
2
];
    TRotaryThreeOrder_WorkData
* pwork_list[2
];
    
for (long i=0;i<2;++
i)
    {
        work_list[i].Src
=&
Src;
        work_list[i].RotaryAngle
=
RotaryAngle;
        work_list[i].ZoomX
=
ZoomX;
        work_list[i].ZoomY
=
ZoomY;
        work_list[i].move_x
=
move_x;
        work_list[i].move_y
=
move_y;
        pwork_list[i]
=&
work_list[i];
    }
    TPicRegion dst_up
=
Dst;
    dst_up.height
=Dst.height/2
;
    work_list[
0].Dst=&
dst_up;
    TPicRegion dst_down
=
Dst;
    dst_down.pdata
=&Pixels(Dst,0
,dst_up.height);
    dst_down.height
=Dst.height-
dst_up.height;
    work_list[
1].Dst=&
dst_down;
    work_list[
1].move_y=move_y-Dst.height/2
;
    CWorkThreadPool::work_execute(RotaryThreeOrder_callback,(
void**)&pwork_list,2
);
}


//注:测试图片都是800*600的图片旋转到1004*1004的图片中心 测试成绩取各个旋转角度的平均速度值
//////////////////////////////////////////////////////////////////////////////////
//速度测试:    CPU: AMD64x2 4200+             
//==============================================================================
// PicRotaryThreeOrder_MMX_parallel2  87.6 fps
//==============================================================================

//////////////////////////////////////////////////////////////////////////////////
//速度测试:    CPU: Intel Core2 4400(2.00G)             
//==============================================================================
// PicRotaryThreeOrder_MMX_parallel2  89.3 fps
////////////////////////////////////////////////////////////////////////////////  

并行化的实现PicRotaryThreeOrder_MMX_parallel2比PicRotaryThreeOrder_MMX的44.2fps快了98.2%!  (Intel Core2 4400上快了94.6%)
在双核CPU上执行速度几乎是单核上的2倍!

B':一个通用的针对任意多核并行的一个简单实现
  有了上面的并行基础,我们来实现一个更加通用一些的版本;根据CPU核心数来动态分配任务;
  实现方式为直接按照扫描行来分配(但这样处理可能不利于内存的高效访问),就懒得去估算任务量了:)

void PicRotaryThreeOrder_MMX_part(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double move_y,long part_i,long part_count)
{
    
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);
    
long run_part_i=0
;
    
while (true//to down

    {
        
long y=
rcData.out_dst_down_y;
        
if (y>=Dst.height) break
;
        
if (y>=0
)
        {
            
if (run_part_i%part_count==
part_i)
                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);
            
++
run_part_i;
        }
        
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)
        {
            
if (run_part_i%part_count==
part_i)
                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);
            
++
run_part_i;
        }
    }
}

struct
 TRotaryThreeOrder_part_WorkData
{
    
const TPicRegion*
 Dst;
    
const TPicRegion*
 Src;
    
double
 RotaryAngle;
    
double
 ZoomX;
    
double
 ZoomY;
    
double
 move_x;
    
double
 move_y;
    
long
   part_i;
    
long
   part_count;
};

void RotaryThreeOrder_part_callback(void*
 wd)
{
    TRotaryThreeOrder_part_WorkData
* WorkData=(TRotaryThreeOrder_part_WorkData*
)wd;
    PicRotaryThreeOrder_MMX_part(
*WorkData->Dst,*WorkData->Src,WorkData->RotaryAngle,WorkData->ZoomX,WorkData->
ZoomY,
        WorkData
->move_x,WorkData->move_y,WorkData->part_i,WorkData->
part_count);
}

void PicRotaryThreeOrder_MMX_parallel(const TPicRegion& Dst,const TPicRegion& Src,double RotaryAngle,double ZoomX,double ZoomY,double move_x,double
 move_y)
{
    
long work_count=
CWorkThreadPool::best_work_count();
    std::vector
<TRotaryThreeOrder_part_WorkData>
   work_list(work_count);
    std::vector
<TRotaryThreeOrder_part_WorkData*>
  pwork_list(work_count);
    
long
 i;
    
for (i=0;i<work_count;++
i)
    {
        work_list[i].Dst
=&
Dst;
        work_list[i].Src
=&
Src;
        work_list[i].RotaryAngle
=
RotaryAngle;
        work_list[i].ZoomX
=
ZoomX;
        work_list[i].ZoomY
=
ZoomY;
        work_list[i].move_x
=
move_x;
        work_list[i].move_y
=
move_y;
        work_list[i].part_i
=
i;
        work_list[i].part_count
=
work_count;
        pwork_list[i]
=&
work_list[i];
    }
    CWorkThreadPool::work_execute(RotaryThreeOrder_part_callback,(
void**)&pwork_list[0
],work_count);
}

//注:测试图片都是800*600的图片旋转到1004*1004的图片中心 测试成绩取各个旋转角度的平均速度值
//////////////////////////////////////////////////////////////////////////////////
//速度测试: CPU: AMD64x2 4200+                
//==============================================================================
// PicRotaryThreeOrder_MMX_parallel   81.0 fps
////////////////////////////////////////////////////////////////////////////////  

//////////////////////////////////////////////////////////////////////////////////
//速度测试: CPU: Intel Core2 4400(2.00G)                
//==============================================================================
// PicRotaryThreeOrder_MMX_parallel   89.5 fps
////////////////////////////////////////////////////////////////////////////////   

 这个实现能应付大多数时候的并行需求了,包括以后的4核8核...


(注意:这里的并行任务分割方案仅仅是简单的举例(用了代码改动最小的方案),你应该根据你的需求来更好的并行化你的任务; 如果分割后的单个任务太小,并行的优势可能就体现不出来,甚至于更慢;)

C:超大图片旋转优化

  1.使用PicRotary*、PicRotaryBilInear*、PicRotaryThreeOrder*等函数在旋转大图片的时候,会出现一个速度变慢问题:就是旋转不同的角度,速度差异巨大(甚至达到8倍以上!)

速度测试:
//注:CPU: AMD64x2 4200+(2.37G)
////////////////////////////////////////////////////////////////////////////////
//   800x600的源图片             各角度平均帧数     角度中最小帧数     角度中最大帧数
//==============================================================================
// PicRotarySSE2                  304.2 fps       250.7 fps       565.3 fps
// PicRotaryBilInear_MMX          100.2 fps        87.8 fps       130.3 fps
// PicRotaryThreeOrder_MMX         44.2 fps        41.4 fps        49.7 fps
////////////////////////////////////////////////////////////////////////////////  
//  3200x2400的源图片            各角度平均帧数     角度中最小帧数     角度中最大帧数
//==============================================================================
// PicRotarySSE2                   12.2 fps         4.6 fps        36.3 fps
// PicRotaryBilInear_MMX            5.0 fps         1.1 fps         8.7 fps
// PicRotaryThreeOrder_MMX          2.6 fps         0.9 fps         3.5 fps
////////////////////////////////////////////////////////////////////////////////  

//注:CPU: Intel Core2 4400(2.00G)
////////////////////////////////////////////////////////////////////////////////
//   800x600的源图片             各角度平均帧数     角度中最小帧数     角度中最大帧数
//==============================================================================
// PicRotarySSE2                  449.3 fps       250.5 fps       753.4 fps
// PicRotaryBilInear_MMX          109.5 fps        95.3 fps       132.4 fps
// PicRotaryThreeOrder_MMX         45.9 fps        41.5 fps        50.3 fps
////////////////////////////////////////////////////////////////////////////////  
//  3200x2400的源图片            各角度平均帧数     角度中最小帧数     角度中最大帧数
//==============================================================================
// PicRotarySSE2                   18.3 fps        12.0 fps        44.8 fps
// PicRotaryBilInear_MMX            6.7 fps         3.5 fps         8.7 fps
// PicRotaryThreeOrder_MMX          2.9 fps         2.2 fps         3.4 fps
////////////////////////////////////////////////////////////////////////////////  

  在我的AMD64下 4200+ CPU上800x600的源图片对旋转的角度不是很敏感,但当源图片为3200x2400的时候,最小速度和平均速度差异巨大;(Intel Core2 4400上稍好)

  2.先来分析一下问题出现的原因,对于某些角度(比如90度和270度),按以前的函数实现,访问源图片内存的方式将是列方向的,当图片比较大的时候,内存的读取访问将变得非常低效;一般CPU访问内存的时候都会一次性读取连续相邻的64字节放到缓存,但很明显对于某些角度,预读的大部分数据都没有用(甚至只使用了其中的4个字节,浪费了60字节完全没有用就被新的数据挤出了缓存);对于小缓存的CPU和较大的源图片,这种情况会更严重;
  能想到的一些解决方案:a.使用CPU的预读指令来手工预读数据,但是没有办法指定预读的内存块大小从而避免带宽浪费;而且以后的硬件趋势也只会朝一次读取更大的块发展,所以该方案不可行;b.针对不同的角度方向分别编码,使读取的内存方向尽量按行方向(从而使预读生效),比如靠近90度旋转的时候写内存的方向将变为列方向,因为写内存指令中可以禁止写缓存,应该可以降低列写入带来的性能损失;该方案还有一个缺点是代码编写稍嫌麻烦:)  C:利用内存访问的局部性来使缓存的数据有效,就是分成小块来处理旋转算法,使内存访问在任何角度时都有相关性;

  3.分块局部性旋转算法的实现方案;
      
             以前的扫描算法图示                        新的分块扫描算法图示

     为了实现新的扫描路径,有一个简单的改进办法,把以前的扫描行(起始和结束位置)先保存起来,然后在处理这是扫描行;代码就很简单了,如下:
(没有给出的基础代码参见该系列的其他文章)

  4.近邻取样插值的分块扫描函数实现PicRotarySSE2_Block

    struct TBlockLineWork //用来保存一个扫描行
    {
    
public
:
        TARGB32
*
    pdst;
        
long
        width_border0;
        
long
        width_in;
        
long
        width_border1;
        
long
        src_x0_16;
        
long
        src_y0_16;
        TBlockLineWork(TARGB32
* _pdst,long _width_in,long _src_x0_16,long
 _src_y0_16)
            :pdst(_pdst),width_in(_width_in),src_x0_16(_src_x0_16),src_y0_16(_src_y0_16),width_border0(
0),width_border1(0
) {}
        TBlockLineWork(TARGB32
* _pdst,long _width_border0,long _width_in,long _width_border1,long _src_x0_16,long
 _src_y0_16)
            :pdst(_pdst),width_in(_width_in),src_x0_16(_src_x0_16),src_y0_16(_src_y0_16),width_border0(_width_border0),width_border1(_width_border1) {}
    };
    typedef std::vector
<TBlockLineWork>
 TBlockWork;

    
//分小块遍历

    void do_PicRotarySSE2_Block(TBlockWork& BlockWork,const TPicRegion& Src,long Ax_16,long Ay_16)
    {
        
//我测试的分成64x64的小块比较合适,也可以尝试一下其它块大小

        const long rotary_block_width=64;  
        
const long rotary_block_height=
rotary_block_width;
        
long height=
BlockWork.size();
        
for (long y=0;y<height;y+=
rotary_block_height)
        { 
            
long
 cur_block_height;
            
if (rotary_block_height<=(height-
y))
                cur_block_height
=
rotary_block_height;
            
else
 
                cur_block_height
=(height-
y);
            
bool is_line_filish=false
;
            
while (!
is_line_filish)
            {
                is_line_filish
=true
;
                
for (long yi=y;yi<y+cur_block_height;++
yi)
                {
                    TBlockLineWork
* BlockLine=&
BlockWork[yi];
                    
long cur_block_width=BlockLine->
width_in;
                    
if (cur_block_width>0
)
                    {
                        is_line_filish
=false
;
                        
if (cur_block_width>
rotary_block_width)
                           cur_block_width
=
rotary_block_width;
                        PicRotarySSE2_CopyLine(BlockLine
->
pdst,cur_block_width,Ax_16,Ay_16,
                            BlockLine
->src_x0_16,BlockLine->
src_y0_16,Src);
                        BlockLine
->pdst=&BlockLine->
pdst[cur_block_width];
                        BlockLine
->width_in-=
cur_block_width;
                        BlockLine
->src_x0_16+=(Ax_16*
cur_block_width);
                        BlockLine
->src_y0_16+=(Ay_16*
cur_block_width);
                    }
                }
            }
        }
    }

void PicRotarySSE2_Block(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,0)) return
;


    TBlockWork BlockWork;

    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
)
        {
            
long x0=
rcData.out_dst_x0_in;
            BlockWork.push_back(TBlockLineWork(
&pDstLine[x0],rcData.out_dst_x1_in-
x0,rcData.out_src_x0_16,rcData.out_src_y0_16));
        }
        
if (!rcData.next_clip_line_down()) break
;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
    
for (long sleft=0,sright=BlockWork.size()-1;sleft<sright;++sleft,--
sright)
        std::swap(BlockWork[sleft],BlockWork[sright]);
   
    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)
        {
            
long x0=
rcData.out_dst_x0_in;
            BlockWork.push_back(TBlockLineWork(
&pDstLine[x0],rcData.out_dst_x1_in-
x0,rcData.out_src_x0_16,rcData.out_src_y0_16));
        }
    }
    do_PicRotarySSE2_Block(BlockWork,Src,Ax_16,Ay_16);

    asm  sfence 
//刷新写入

}

  5.二次线性插值的分块扫描函数实现PicRotaryBilInear_MMX_Block
    这里比近邻取样的相应函数多出一些边界处理代码

    inline void PicRotary_BilInear_CopyLine_Fast_MMX(TARGB32* pDstLine,long width,
                            
long Ax_16,long Ay_16,long srcx0_16,long srcy0_16,const TPicRegion&
 SrcPic)
    {
        
for (long x=0;x<width;++
x)
        {
            BilInear_Fast_MMX(SrcPic,srcx0_16,srcy0_16,
&
pDstLine[x]);
            srcx0_16
+=
Ax_16;
            srcy0_16
+=
Ay_16;
        }
    }
    inline 
void PicRotary_BilInear_CopyLine_Border_MMX(TARGB32* pDstLine,long
 width,
                            
long Ax_16,long Ay_16,long srcx0_16,long srcy0_16,const TPicRegion&
 SrcPic)
    {
        
for (long x=0;x<width;++
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;
        }
    }

    
void do_PicRotary_BilInear_MMX_Block(TBlockWork& BlockWork,const TPicRegion& Src,long Ax_16,long
 Ay_16)
    {
        
const long rotary_block_width=64;  //128 

        const long rotary_block_height=rotary_block_width;
        
long height=
BlockWork.size();
        
for (long y=0;y<height;y+=
rotary_block_height)
        { 
            
long
 cur_block_height;
            
if (rotary_block_height<=(height-
y))
                cur_block_height
=
rotary_block_height;
            
else
 
                cur_block_height
=(height-
y);

            
for (long yi=y;yi<y+cur_block_height;++
yi)
            {
                TBlockLineWork
* BlockLine=&
BlockWork[yi];
                
long cur_block_width=BlockLine->
width_border0;
                
if (cur_block_width>0
)
                {
                    PicRotary_BilInear_CopyLine_Border_MMX(BlockLine
->
pdst,cur_block_width,Ax_16,Ay_16,
                        BlockLine
->src_x0_16,BlockLine->
src_y0_16,Src);
                    BlockLine
->pdst=&BlockLine->
pdst[cur_block_width];
                    BlockLine
->src_x0_16+=(Ax_16*
cur_block_width);
                    BlockLine
->src_y0_16+=(Ay_16*
cur_block_width);
                }
            }

            
bool is_line_filish=false
;
            
while (!
is_line_filish)
            {
                is_line_filish
=true
;
                
for (long yi=y;yi<y+cur_block_height;++
yi)
                {
                    TBlockLineWork
* BlockLine=&
BlockWork[yi];
                    
long cur_block_width=BlockLine->
width_in;
                    
if (cur_block_width>0
)
                    {
                        is_line_filish
=false
;
                        
if (cur_block_width>
rotary_block_width)
                           cur_block_width
=
rotary_block_width;
                        PicRotary_BilInear_CopyLine_Fast_MMX(BlockLine
->
pdst,cur_block_width,Ax_16,Ay_16,
                            BlockLine
->src_x0_16,BlockLine->
src_y0_16,Src);
                        BlockLine
->pdst=&BlockLine->
pdst[cur_block_width];
                        BlockLine
->width_in-=
cur_block_width;
                        BlockLine
->src_x0_16+=(Ax_16*
cur_block_width);
                        BlockLine
->src_y0_16+=(Ay_16*
cur_block_width);
                    }
                }
            }

            
for (long yi=y;yi<y+cur_block_height;++
yi)
            {
                TBlockLineWork
* BlockLine=&
BlockWork[yi];
                
long cur_block_width=BlockLine->
width_border1;
                
if (cur_block_width>0
)
                {
                    PicRotary_BilInear_CopyLine_Border_MMX(BlockLine
->
pdst,cur_block_width,Ax_16,Ay_16,
                        BlockLine
->src_x0_16,BlockLine->
src_y0_16,Src);
                    
//
BlockLine->pdst=&BlockLine->pdst[cur_block_width];
                    
//
BlockLine->src_x0_16+=(Ax_16*cur_block_width);
                    
//BlockLine->src_y0_16+=(Ay_16*cur_block_width);

                }
            }
        }
    
    
        asm  emms
    }

void PicRotaryBilInear_MMX_Block(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
;

    TBlockWork BlockWork;

    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
)
        {
            BlockWork.push_back(TBlockLineWork(
&
pDstLine[rcData.out_dst_x0_boder],
                rcData.out_dst_x0_in
-rcData.out_dst_x0_boder,rcData.out_dst_x1_in-
rcData.out_dst_x0_in,
                rcData.out_dst_x1_boder
-
rcData.out_dst_x1_in,rcData.out_src_x0_16,rcData.out_src_y0_16));
        }
        
if (!rcData.next_clip_line_down()) break
;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
   
    
for (long sleft=0,sright=BlockWork.size()-1;sleft<sright;++sleft,--
sright)
        std::swap(BlockWork[sleft],BlockWork[sright]);

    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)
        {
            BlockWork.push_back(TBlockLineWork(
&
pDstLine[rcData.out_dst_x0_boder],
                rcData.out_dst_x0_in
-rcData.out_dst_x0_boder,rcData.out_dst_x1_in-
rcData.out_dst_x0_in,
                rcData.out_dst_x1_boder
-
rcData.out_dst_x1_in,rcData.out_src_x0_16,rcData.out_src_y0_16));
        }
    }

    do_PicRotary_BilInear_MMX_Block(BlockWork,Src,Ax_16,Ay_16);
}

  6.三次卷积插值的分块扫描函数实现PicRotaryThreeOrder_MMX_Block
   几乎就是拷贝PicRotaryBilInear_MMX_Block,然后稍做改动;
   (实际项目中的代码和文章中的代码还是有很多不同的,要是实际代码中也有这么多长篇长篇的拷贝然后稍作修改的代码,那就要疯了:)

    inline void PicRotary_ThreeOrder_CopyLine_Fast_MMX(TARGB32* pDstLine,long width,
                            
long Ax_16,long Ay_16,long srcx0_16,long srcy0_16,const TPicRegion&
 SrcPic)
    {
        
for (long x=0;x<width;++
x)
        {
            ThreeOrder_Fast_MMX(SrcPic,srcx0_16,srcy0_16,
&
pDstLine[x]);
            srcx0_16
+=
Ax_16;
            srcy0_16
+=
Ay_16;
        }
    }
    inline 
void PicRotary_ThreeOrder_CopyLine_Border_MMX(TARGB32* pDstLine,long
 width,
                            
long Ax_16,long Ay_16,long srcx0_16,long srcy0_16,const TPicRegion&
 SrcPic)
    {
        
for (long x=0;x<width;++
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;
        }
    }

    
void do_PicRotary_ThreeOrder_MMX_Block(TBlockWork& BlockWork,const TPicRegion& Src,long Ax_16,long
 Ay_16)
    {
        
const long rotary_block_width=64;  //128 

        const long rotary_block_height=rotary_block_width;
        
long height=
BlockWork.size();
        
for (long y=0;y<height;y+=
rotary_block_height)
        { 
            
long
 cur_block_height;
            
if (rotary_block_height<=(height-
y))
                cur_block_height
=
rotary_block_height;
            
else
 
                cur_block_height
=(height-
y);

            
for (long yi=y;yi<y+cur_block_height;++
yi)
            {
                TBlockLineWork
* BlockLine=&
BlockWork[yi];
                
long cur_block_width=BlockLine->
width_border0;
                
if (cur_block_width>0
)
                {
                    PicRotary_ThreeOrder_CopyLine_Border_MMX(BlockLine
->
pdst,cur_block_width,Ax_16,Ay_16,
                        BlockLine
->src_x0_16,BlockLine->
src_y0_16,Src);
                    BlockLine
->pdst=&BlockLine->
pdst[cur_block_width];
                    BlockLine
->src_x0_16+=(Ax_16*
cur_block_width);
                    BlockLine
->src_y0_16+=(Ay_16*
cur_block_width);
                }
            }

            
bool is_line_filish=false
;
            
while (!
is_line_filish)
            {
                is_line_filish
=true
;
                
for (long yi=y;yi<y+cur_block_height;++
yi)
                {
                    TBlockLineWork
* BlockLine=&
BlockWork[yi];
                    
long cur_block_width=BlockLine->
width_in;
                    
if (cur_block_width>0
)
                    {
                        is_line_filish
=false
;
                        
if (cur_block_width>
rotary_block_width)
                           cur_block_width
=
rotary_block_width;
                        PicRotary_ThreeOrder_CopyLine_Fast_MMX(BlockLine
->
pdst,cur_block_width,Ax_16,Ay_16,
                            BlockLine
->src_x0_16,BlockLine->
src_y0_16,Src);
                        BlockLine
->pdst=&BlockLine->
pdst[cur_block_width];
                        BlockLine
->width_in-=
cur_block_width;
                        BlockLine
->src_x0_16+=(Ax_16*
cur_block_width);
                        BlockLine
->src_y0_16+=(Ay_16*
cur_block_width);
                    }
                }
            }

            
for (long yi=y;yi<y+cur_block_height;++
yi)
            {
                TBlockLineWork
* BlockLine=&
BlockWork[yi];
                
long cur_block_width=BlockLine->
width_border1;
                
if (cur_block_width>0
)
                {
                    PicRotary_ThreeOrder_CopyLine_Border_MMX(BlockLine
->
pdst,cur_block_width,Ax_16,Ay_16,
                        BlockLine
->src_x0_16,BlockLine->
src_y0_16,Src);
                    
//
BlockLine->pdst=&BlockLine->pdst[cur_block_width];
                    
//
BlockLine->src_x0_16+=(Ax_16*cur_block_width);
                    
//BlockLine->src_y0_16+=(Ay_16*cur_block_width);

                }
            }
        }
    
    
        asm  emms
    }

void PicRotaryThreeOrder_MMX_Block(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
;

    TBlockWork BlockWork;

    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
)
        {
            BlockWork.push_back(TBlockLineWork(
&
pDstLine[rcData.out_dst_x0_boder],
                rcData.out_dst_x0_in
-rcData.out_dst_x0_boder,rcData.out_dst_x1_in-
rcData.out_dst_x0_in,
                rcData.out_dst_x1_boder
-
rcData.out_dst_x1_in,rcData.out_src_x0_16,rcData.out_src_y0_16));
        }
        
if (!rcData.next_clip_line_down()) break
;
        ((TUInt8
*&)pDstLine)+=
Dst.byte_width;
    }
   
    
for (long sleft=0,sright=BlockWork.size()-1;sleft<sright;++sleft,--
sright)
        std::swap(BlockWork[sleft],BlockWork[sright]);

    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)
        {
            BlockWork.push_back(TBlockLineWork(
&
pDstLine[rcData.out_dst_x0_boder],
                rcData.out_dst_x0_in
-rcData.out_dst_x0_boder,rcData.out_dst_x1_in-
rcData.out_dst_x0_in,
                rcData.out_dst_x1_boder
-
rcData.out_dst_x1_in,rcData.out_src_x0_16,rcData.out_src_y0_16));
        }
    }

    do_PicRotary_ThreeOrder_MMX_Block(BlockWork,Src,Ax_16,Ay_16);
}

 

  7.分块处理的旋转函数的速度测试:
//注:CPU: AMD64x2 4200+(2.37G)
////////////////////////////////////////////////////////////////////////////////
//   800x600的源图片             各角度平均帧数    角度中最小帧数    角度中最大帧数
//==============================================================================
// PicRotarySSE2                  304.2 fps       250.7 fps       565.3 fps
// PicRotarySSE2_Block            316.6  fps      255.5 fps       495.6 fps
// PicRotaryBilInear_MMX          100.2 fps        87.8 fps       130.3 fps
// PicRotaryBilInear_MMX_Block     99.5  fps       92.7 fps       122.3 fps
// PicRotaryThreeOrder_MMX         44.2 fps        41.4 fps        49.7 fps

/
/ PicRotaryThreeOrder_MMX_Block   43.2 fps        41.3 fps        47.8 fps
////////////////////////////////////////////////////////////////////////////////  
//  3200x2400的源图片            各角度平均帧数    角度中最小帧数    角度中最大帧数
//==============================================================================
// PicRotarySSE2                   12.2 fps         4.6 fps        36.3 fps
// PicRotarySSE2_Block             19.2 fps       15.9 fps        33.3 fps
// PicRotaryBilInear_MMX            5.0 fps         1.1 fps         8.7 fps
// PicRotaryBilInear_MMX_Block      6.3 fps        5.8 fps         8.3 fps
// PicRotaryThreeOrder_MMX          2.6 fps         0.9 fps         3.5 fps
// PicRotaryThreeOrder_MMX_Block    2.9 fps        2.7 fps         3.4 fps
////////////////////////////////////////////////////////////////////////////////
 

//注:CPU: Intel Core2 4400(2.00G)
////////////////////////////////////////////////////////////////////////////////
//   800x600的源图片             各角度平均帧数    角度中最小帧数    角度中最大帧数
//==============================================================================
// PicRotarySSE2                  449.3 fps       250.5 fps       753.4 fps
// PicRotarySSE2_Block            351.3 fps       219.0 fps       436.3 fps
// PicRotaryBilInear_MMX          109.5 fps        95.3 fps       132.4 fps
// PicRotaryBilInear_MMX_Block    112.2 fps        98.1 fps       124.9 fps
// PicRotaryThreeOrder_MMX         45.9 fps        41.5 fps        50.3 fps
// PicRotaryThreeOrder_MMX_Block   47.7 fps        44.8 fps        50.1 fps
////////////////////////////////////////////////////////////////////////////////  
//  3200x2400的源图片            各角度平均帧数     角度中最小帧数     角度中最大帧数
//==============================================================================
// PicRotarySSE2                   18.3 fps        12.0 fps        44.8 fps
// PicRotarySSE2_Block             18.4 fps        15.5 fps        23.8 fps
// PicRotaryBilInear_MMX            6.7 fps         3.5 fps         8.7 fps
// PicRotaryBilInear_MMX_Block      7.3 fps         6.8 fps         8.3 fps
// PicRotaryThreeOrder_MMX          2.9 fps         2.2 fps         3.4 fps
// PicRotaryThreeOrder_MMX_Block    3.2 fps         2.9 fps         3.4 fps
////////////////////////////////////////////////////////////////////////////////
 

测试更大的源图片(6400x4800)或者CPU的缓存越小的时候*_Block优化版本优势越明显!