(转)任意角度的高质量的快速的图…
2016-12-25 12:21
148 查看
图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转 收藏
图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转
HouSisong@GMail.com
2007.04.26
(2009.03.09
可以到这里下载旋转算法的完整的可以编译的项目源代码: http://blog.csdn.net/housisong/archive/2009/03/09/3970925.aspx
)
(2007.06.22 优化PicRotary3加快13.6%,优化PicRotarySSE加快6.1%,
尝试了一下使用SSE2的写缓冲优化MOVNTI)
(2007.04.29 修正一个TRotaryClipData.find_begin的bug)
(2007.05.16 更换测试用电脑和编译器,为了保持测试数据一致和可对比性,更新了测试数据)
tag:图像旋转,任意角度,图像缩放,速度优化,定点数优化,近邻取样插值,二次线性插值,
三次卷积插值,MipMap链,三次线性插值,MMX\SSE优化,CPU缓存优化,AlphaBlend,颜色混合,并行
摘要:首先给出一个基本的图像旋转算法,然后一步一步的优化其速度和旋转质量,打破不能软件旋转的神话!
任意角度的高质量的快速的图像旋转 全文 分为:
上篇 纯软件的任意角度的快速旋转
中篇 高质量的旋转
下篇 补充话题(完整AlphaBlend旋转、旋转函数并行化、针对大图片的预读缓冲区优化)
正文:
为了便于讨论,这里只处理32bit的ARGB颜色;
代码使用C++;涉及到汇编优化的时候假定为x86平台;使用的编译器为vc2005;
为了代码的可读性,没有加入异常处理代码;
测试使用的CPU为赛扬2G(新的测试平台的CPU为AMD64x2 4200+(2.37G),测试时使用的单线程执行);
(一些基础代码和插值原理的详细说明参见作者的《图形图像处理-之-高质量的快速的图像缩放》系列文章)
速度测试说明:
只测试内存数据到内存数据的缩放
测试图片都是800*600旋转到1004*1004,测试成绩取各个旋转角度的平均速度值;
fps表示每秒钟的帧数,值越大表示函数越快
A:旋转原理和旋转公式:
推导旋转公式:
旋转示意图
有:
tg(b)=y/x
----(1)
tg(a+b)=y'/x'
----(2)
x*x + y*y = x'*x' +
y'*y'
----(3)
有公式:tg(a+b) = ( tg(a)+tg(b) )
/ ( 1-tg(a)*tg(b) ) ----(4)
把(1)代入(4)从而消除参数b;
tg(a)+y/x = y'/x'*( 1-tg(a)*y/x
)
----(5)
由(5)可以得x'=y'*(x-y*tg(a))/( x*tg(a)+y
)
----(6)
把(6)代入(3)从而消除参数x',化简后求得:
y'=x*sin(a)+y*cos(a);
----(7)
把(7)代入(6),有:
x'=x*cos(a)-y*sin(a);
----(8)
OK,旋转公式有了,那么来看看在图片旋转中的应用;
假设对图片上任意点(x,y),绕一个坐标点(rx0,ry0)逆时针旋转RotaryAngle角度后的新的坐标设为(x',
y'),有公式:
(x平移rx0,y平移ry0,角度a对应-RotaryAngle ,
带入方程(7)、(8)后有: )
x'= (x - rx0)*cos(RotaryAngle) + (y -
ry0)*sin(RotaryAngle) + rx0 ;
y'=-(x - rx0)*sin(RotaryAngle) + (y -
ry0)*cos(RotaryAngle) + ry0 ;
那么,根据新的坐标点求源坐标点的公式为:
x=(x'- rx0)*cos(RotaryAngle) - (y'-
ry0)*sin(RotaryAngle) + rx0 ;
y=(x'- rx0)*sin(RotaryAngle) + (y'-
ry0)*cos(RotaryAngle) + ry0 ;
旋转的时候还可以顺便加入x轴和y轴的缩放和平移,而不影响速度,那么完整的公式为:
x=(x'- move_x-rx0)/ZoomX*cos(RotaryAngle) - (y'-
move_y-ry0)/ZoomY*sin(RotaryAngle) + rx0 ;
y=(x'- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y'-
move_y-ry0)/ZoomY*cos(RotaryAngle) + ry0 ;
其中: RotaryAngle为逆时针旋转的角度;
ZoomX,ZoomY为x轴y轴的缩放系数(支持负的系数,相当于图像翻转);
move_x,move_y为x轴y轴的平移量;
一些颜色和图片的数据定义:
#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(const
TPicRegion& pic,const long x,const long y)
{
return (
(TARGB32*)((TUInt8*)pic.pdata+pic.byte_width*y) )[x];
}
//判断一个点是否在图片中
inline bool PixelsIsInPic(const TPicRegion&
pic,const long x,const long y)
{
return (
(x>=0)&&(x<pic.width)
&&
(y>=0)&&(y<pic.height)
);
}
B:一个简单的浮点实现版本
//////////////////////////////////////////////////////////////////////////////////////////////////////
//函数假设以原图片的中心点坐标为旋转和缩放的中心
void PicRotary0(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
rx0=Src.width*0.5; //(rx0,ry0)为旋转中心
double
ry0=Src.height*0.5;
for (long
y=0;y<Dst.height;++y)
{
for (long x=0;x<Dst.width;++x)
{
long srcx=(long)((x- move_x-rx0)/ZoomX*cos(RotaryAngle) - (y-
move_y-ry0)/ZoomY*sin(RotaryAngle) + rx0) ;
long srcy=(long)((x- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y-
move_y-ry0)/ZoomY*cos(RotaryAngle) + ry0) ;
if (PixelsIsInPic(Src,srcx,srcy))
Pixels(Dst,x,y)=Pixels(Src,srcx,srcy);
}
}
}
(调用方法比如:
PicRotary0(ppicDst,ppicSrc,PI/6,0.9,0.9,(dst_wh-ppicSrc.width)*0.5,(dst_wh-ppicSrc.height)*0.5);
//作用:将图片ppicSrc按0.9的缩放比例旋转PI/6幅度后绘制到图片ppicDst的中心
)
//注:测试图片都是800*600的图片旋转到1004*1004的图片中心 测试成绩取各个旋转角度的平均速度值
////////////////////////////////////////////////////////////////////////////////
//速度测试:
//==============================================================================
//
PicRotary0
34.9 fps
////////////////////////////////////////////////////////////////////////////////
旋转结果图示(小图):
30度
60度
90度
120度
150度
180度
210度
240度
270度
300度
330度
360度
C:优化循环内部,化简系数
1.sin和cos函数是很慢的计算函数,可以在循环前预先计算好sin(RotaryAngle)和cos(RotaryAngle)的值:
double
sinA=sin(RotaryAngle);
double
cosA=cos(RotaryAngle);
2.可以将除以ZoomX、ZoomY改成乘法,预先计算出倒数:
double
rZoomX=1.0/ZoomX;
double
rZoomY=1.0/ZoomY;
3.优化内部的旋转公式,将能够预先计算的部分提到循环外(即:拆解公式):
原: long srcx=(long)((x-
move_x-rx0)/ZoomX*cos(RotaryAngle) - (y-
move_y-ry0)/ZoomY*sin(RotaryAngle) + rx0) ;
long srcy=(long)((x- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y-
move_y-ry0)/ZoomY*cos(RotaryAngle) + ry0) ;
变形为:
long srcx=(long)( Ax*x + Bx*y +Cx ) ;
long srcy=(long)( Ay*x + By*y +Cy ) ;
其中: Ax=(rZoomX*cosA); Bx=(-rZoomY*sinA);
Cx=(-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0);
Ay=(rZoomX*sinA); By=(rZoomY*cosA);
Cy=(-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0);
(提示: Ax,Bx,Cx,Ay,By,Cy都可以在旋转之前预先计算出来)
改进后的函数为:
void PicRotary1(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
rZoomX=1.0/ZoomX;
double
rZoomY=1.0/ZoomY;
double
sinA=sin(RotaryAngle);
double
cosA=cos(RotaryAngle);
double
Ax=(rZoomX*cosA);
double
Ay=(rZoomX*sinA);
double
Bx=(-rZoomY*sinA);
double
By=(rZoomY*cosA);
double
rx0=Src.width*0.5; //(rx0,ry0)为旋转中心
double
ry0=Src.height*0.5;
double
Cx=(-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0);
double
Cy=(-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0);
TARGB32*
pDstLine=Dst.pdata;
double
srcx0_f=(Cx);
double
srcy0_f=(Cy);
for (long
y=0;y<Dst.height;++y)
{
double srcx_f=srcx0_f;
double srcy_f=srcy0_f;
for (long x=0;x<Dst.width;++x)
{
long srcx=(long)(srcx_f);
long srcy=(long)(srcy_f);
if (PixelsIsInPic(Src,srcx,srcy))
pDstLine[x]=Pixels(Src,srcx,srcy);
srcx_f+=Ax;
srcy_f+=Ay;
}
srcx0_f+=Bx;
srcy0_f+=By;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
}
////////////////////////////////////////////////////////////////////////////////
//速度测试:
//==============================================================================
//
PicRotary1
62.0 fps
////////////////////////////////////////////////////////////////////////////////
( 在AMD64x2
4200+和VC2005编译下PicRotary1(51.8fps)比PicRotary0(27.1fps)快90%;
在AMD64x2
4200+和VC6编译下PicRotary1(20.3fps)比PicRotary0(16.1fps)快26%;
以前在赛扬2G和VC6编译下PicRotary1(8.4fps)反而比PicRotary0(12.7fps)慢50%! :(
)
D:更深入的优化、定点数优化
(浮点数到整数的转化也是应该优化的一个地方,这里不再处理,可以参见
<图形图像处理-之-高质量的快速的图像缩放 上篇
近邻取样插值和其速度优化>中的PicZoom3_float函数)
1.优化除法:
原: double
rZoomX=1.0/ZoomX;
double rZoomY=1.0/ZoomY;
改写为(优化掉了一次除法):
double tmprZoomXY=1.0/(ZoomX*ZoomY);
double rZoomX=tmprZoomXY*ZoomY;
double rZoomY=tmprZoomXY*ZoomX;
2.x86的浮点计算单元(FPU)有一条指令"fsincos"可以同时计算出sin和cos值
//定义SinCos函数: 同时计算sin(Angle)和cos(Angle)的内嵌x86汇编函数
void
__declspec(naked) __stdcall SinCos(const double
Angle,double& sina,double&
cosa)
{
asm
{
fld qword ptr
[esp+4]//Angle
mov eax,[esp+12]//&sina
mov edx,[esp+16]//&cosa
fsincos
fstp qword ptr [edx]
fstp qword ptr [eax]
ret 16
}
}
3.用定点数代替浮点计算
void PicRotary2(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));
TARGB32*
pDstLine=Dst.pdata;
long
srcx0_16=(Cx_16);
long
srcy0_16=(Cy_16);
for (long
y=0;y<Dst.height;++y)
{
long srcx_16=srcx0_16;
long srcy_16=srcy0_16;
for (long x=0;x<Dst.width;++x)
{
long srcx=(srcx_16>>16);
long srcy=(srcy_16>>16);
if (PixelsIsInPic(Src,srcx,srcy))
pDstLine[x]=Pixels(Src,srcx,srcy);
srcx_16+=Ax_16;
srcy_16+=Ay_16;
}
srcx0_16+=Bx_16;
srcy0_16+=By_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
}
////////////////////////////////////////////////////////////////////////////////
//速度测试:
//==============================================================================
//
PicRotary2
134.2 fps
////////////////////////////////////////////////////////////////////////////////
图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转
HouSisong@GMail.com
2007.04.26
(2009.03.09
可以到这里下载旋转算法的完整的可以编译的项目源代码: http://blog.csdn.net/housisong/archive/2009/03/09/3970925.aspx
)
(2007.06.22 优化PicRotary3加快13.6%,优化PicRotarySSE加快6.1%,
尝试了一下使用SSE2的写缓冲优化MOVNTI)
(2007.04.29 修正一个TRotaryClipData.find_begin的bug)
(2007.05.16 更换测试用电脑和编译器,为了保持测试数据一致和可对比性,更新了测试数据)
tag:图像旋转,任意角度,图像缩放,速度优化,定点数优化,近邻取样插值,二次线性插值,
三次卷积插值,MipMap链,三次线性插值,MMX\SSE优化,CPU缓存优化,AlphaBlend,颜色混合,并行
摘要:首先给出一个基本的图像旋转算法,然后一步一步的优化其速度和旋转质量,打破不能软件旋转的神话!
任意角度的高质量的快速的图像旋转 全文 分为:
上篇 纯软件的任意角度的快速旋转
中篇 高质量的旋转
下篇 补充话题(完整AlphaBlend旋转、旋转函数并行化、针对大图片的预读缓冲区优化)
正文:
为了便于讨论,这里只处理32bit的ARGB颜色;
代码使用C++;涉及到汇编优化的时候假定为x86平台;使用的编译器为vc2005;
为了代码的可读性,没有加入异常处理代码;
测试使用的CPU为赛扬2G(新的测试平台的CPU为AMD64x2 4200+(2.37G),测试时使用的单线程执行);
(一些基础代码和插值原理的详细说明参见作者的《图形图像处理-之-高质量的快速的图像缩放》系列文章)
速度测试说明:
只测试内存数据到内存数据的缩放
测试图片都是800*600旋转到1004*1004,测试成绩取各个旋转角度的平均速度值;
fps表示每秒钟的帧数,值越大表示函数越快
A:旋转原理和旋转公式:
推导旋转公式:
旋转示意图
有:
tg(b)=y/x
----(1)
tg(a+b)=y'/x'
----(2)
x*x + y*y = x'*x' +
y'*y'
----(3)
有公式:tg(a+b) = ( tg(a)+tg(b) )
/ ( 1-tg(a)*tg(b) ) ----(4)
把(1)代入(4)从而消除参数b;
tg(a)+y/x = y'/x'*( 1-tg(a)*y/x
)
----(5)
由(5)可以得x'=y'*(x-y*tg(a))/( x*tg(a)+y
)
----(6)
把(6)代入(3)从而消除参数x',化简后求得:
y'=x*sin(a)+y*cos(a);
----(7)
把(7)代入(6),有:
x'=x*cos(a)-y*sin(a);
----(8)
OK,旋转公式有了,那么来看看在图片旋转中的应用;
假设对图片上任意点(x,y),绕一个坐标点(rx0,ry0)逆时针旋转RotaryAngle角度后的新的坐标设为(x',
y'),有公式:
(x平移rx0,y平移ry0,角度a对应-RotaryAngle ,
带入方程(7)、(8)后有: )
x'= (x - rx0)*cos(RotaryAngle) + (y -
ry0)*sin(RotaryAngle) + rx0 ;
y'=-(x - rx0)*sin(RotaryAngle) + (y -
ry0)*cos(RotaryAngle) + ry0 ;
那么,根据新的坐标点求源坐标点的公式为:
x=(x'- rx0)*cos(RotaryAngle) - (y'-
ry0)*sin(RotaryAngle) + rx0 ;
y=(x'- rx0)*sin(RotaryAngle) + (y'-
ry0)*cos(RotaryAngle) + ry0 ;
旋转的时候还可以顺便加入x轴和y轴的缩放和平移,而不影响速度,那么完整的公式为:
x=(x'- move_x-rx0)/ZoomX*cos(RotaryAngle) - (y'-
move_y-ry0)/ZoomY*sin(RotaryAngle) + rx0 ;
y=(x'- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y'-
move_y-ry0)/ZoomY*cos(RotaryAngle) + ry0 ;
其中: RotaryAngle为逆时针旋转的角度;
ZoomX,ZoomY为x轴y轴的缩放系数(支持负的系数,相当于图像翻转);
move_x,move_y为x轴y轴的平移量;
一些颜色和图片的数据定义:
#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(const
TPicRegion& pic,const long x,const long y)
{
return (
(TARGB32*)((TUInt8*)pic.pdata+pic.byte_width*y) )[x];
}
//判断一个点是否在图片中
inline bool PixelsIsInPic(const TPicRegion&
pic,const long x,const long y)
{
return (
(x>=0)&&(x<pic.width)
&&
(y>=0)&&(y<pic.height)
);
}
B:一个简单的浮点实现版本
//////////////////////////////////////////////////////////////////////////////////////////////////////
//函数假设以原图片的中心点坐标为旋转和缩放的中心
void PicRotary0(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
rx0=Src.width*0.5; //(rx0,ry0)为旋转中心
double
ry0=Src.height*0.5;
for (long
y=0;y<Dst.height;++y)
{
for (long x=0;x<Dst.width;++x)
{
long srcx=(long)((x- move_x-rx0)/ZoomX*cos(RotaryAngle) - (y-
move_y-ry0)/ZoomY*sin(RotaryAngle) + rx0) ;
long srcy=(long)((x- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y-
move_y-ry0)/ZoomY*cos(RotaryAngle) + ry0) ;
if (PixelsIsInPic(Src,srcx,srcy))
Pixels(Dst,x,y)=Pixels(Src,srcx,srcy);
}
}
}
(调用方法比如:
PicRotary0(ppicDst,ppicSrc,PI/6,0.9,0.9,(dst_wh-ppicSrc.width)*0.5,(dst_wh-ppicSrc.height)*0.5);
//作用:将图片ppicSrc按0.9的缩放比例旋转PI/6幅度后绘制到图片ppicDst的中心
)
//注:测试图片都是800*600的图片旋转到1004*1004的图片中心 测试成绩取各个旋转角度的平均速度值
////////////////////////////////////////////////////////////////////////////////
//速度测试:
//==============================================================================
//
PicRotary0
34.9 fps
////////////////////////////////////////////////////////////////////////////////
旋转结果图示(小图):
30度
60度
90度
120度
150度
180度
210度
240度
270度
300度
330度
360度
C:优化循环内部,化简系数
1.sin和cos函数是很慢的计算函数,可以在循环前预先计算好sin(RotaryAngle)和cos(RotaryAngle)的值:
double
sinA=sin(RotaryAngle);
double
cosA=cos(RotaryAngle);
2.可以将除以ZoomX、ZoomY改成乘法,预先计算出倒数:
double
rZoomX=1.0/ZoomX;
double
rZoomY=1.0/ZoomY;
3.优化内部的旋转公式,将能够预先计算的部分提到循环外(即:拆解公式):
原: long srcx=(long)((x-
move_x-rx0)/ZoomX*cos(RotaryAngle) - (y-
move_y-ry0)/ZoomY*sin(RotaryAngle) + rx0) ;
long srcy=(long)((x- move_x-rx0)/ZoomX*sin(RotaryAngle) + (y-
move_y-ry0)/ZoomY*cos(RotaryAngle) + ry0) ;
变形为:
long srcx=(long)( Ax*x + Bx*y +Cx ) ;
long srcy=(long)( Ay*x + By*y +Cy ) ;
其中: Ax=(rZoomX*cosA); Bx=(-rZoomY*sinA);
Cx=(-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0);
Ay=(rZoomX*sinA); By=(rZoomY*cosA);
Cy=(-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0);
(提示: Ax,Bx,Cx,Ay,By,Cy都可以在旋转之前预先计算出来)
改进后的函数为:
void PicRotary1(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
rZoomX=1.0/ZoomX;
double
rZoomY=1.0/ZoomY;
double
sinA=sin(RotaryAngle);
double
cosA=cos(RotaryAngle);
double
Ax=(rZoomX*cosA);
double
Ay=(rZoomX*sinA);
double
Bx=(-rZoomY*sinA);
double
By=(rZoomY*cosA);
double
rx0=Src.width*0.5; //(rx0,ry0)为旋转中心
double
ry0=Src.height*0.5;
double
Cx=(-(rx0+move_x)*rZoomX*cosA+(ry0+move_y)*rZoomY*sinA+rx0);
double
Cy=(-(rx0+move_x)*rZoomX*sinA-(ry0+move_y)*rZoomY*cosA+ry0);
TARGB32*
pDstLine=Dst.pdata;
double
srcx0_f=(Cx);
double
srcy0_f=(Cy);
for (long
y=0;y<Dst.height;++y)
{
double srcx_f=srcx0_f;
double srcy_f=srcy0_f;
for (long x=0;x<Dst.width;++x)
{
long srcx=(long)(srcx_f);
long srcy=(long)(srcy_f);
if (PixelsIsInPic(Src,srcx,srcy))
pDstLine[x]=Pixels(Src,srcx,srcy);
srcx_f+=Ax;
srcy_f+=Ay;
}
srcx0_f+=Bx;
srcy0_f+=By;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
}
////////////////////////////////////////////////////////////////////////////////
//速度测试:
//==============================================================================
//
PicRotary1
62.0 fps
////////////////////////////////////////////////////////////////////////////////
( 在AMD64x2
4200+和VC2005编译下PicRotary1(51.8fps)比PicRotary0(27.1fps)快90%;
在AMD64x2
4200+和VC6编译下PicRotary1(20.3fps)比PicRotary0(16.1fps)快26%;
以前在赛扬2G和VC6编译下PicRotary1(8.4fps)反而比PicRotary0(12.7fps)慢50%! :(
)
D:更深入的优化、定点数优化
(浮点数到整数的转化也是应该优化的一个地方,这里不再处理,可以参见
<图形图像处理-之-高质量的快速的图像缩放 上篇
近邻取样插值和其速度优化>中的PicZoom3_float函数)
1.优化除法:
原: double
rZoomX=1.0/ZoomX;
double rZoomY=1.0/ZoomY;
改写为(优化掉了一次除法):
double tmprZoomXY=1.0/(ZoomX*ZoomY);
double rZoomX=tmprZoomXY*ZoomY;
double rZoomY=tmprZoomXY*ZoomX;
2.x86的浮点计算单元(FPU)有一条指令"fsincos"可以同时计算出sin和cos值
//定义SinCos函数: 同时计算sin(Angle)和cos(Angle)的内嵌x86汇编函数
void
__declspec(naked) __stdcall SinCos(const double
Angle,double& sina,double&
cosa)
{
asm
{
fld qword ptr
[esp+4]//Angle
mov eax,[esp+12]//&sina
mov edx,[esp+16]//&cosa
fsincos
fstp qword ptr [edx]
fstp qword ptr [eax]
ret 16
}
}
3.用定点数代替浮点计算
void PicRotary2(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));
TARGB32*
pDstLine=Dst.pdata;
long
srcx0_16=(Cx_16);
long
srcy0_16=(Cy_16);
for (long
y=0;y<Dst.height;++y)
{
long srcx_16=srcx0_16;
long srcy_16=srcy0_16;
for (long x=0;x<Dst.width;++x)
{
long srcx=(srcx_16>>16);
long srcy=(srcy_16>>16);
if (PixelsIsInPic(Src,srcx,srcy))
pDstLine[x]=Pixels(Src,srcx,srcy);
srcx_16+=Ax_16;
srcy_16+=Ay_16;
}
srcx0_16+=Bx_16;
srcy0_16+=By_16;
((TUInt8*&)pDstLine)+=Dst.byte_width;
}
}
////////////////////////////////////////////////////////////////////////////////
//速度测试:
//==============================================================================
//
PicRotary2
134.2 fps
////////////////////////////////////////////////////////////////////////////////
相关文章推荐
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 中篇 高质量的旋转
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转
- (转)任意角度的高质量的快速的图…
- 图形图像处理-之-任意角度的高质量的快速的图像旋转
- (转)任意角度的高质量的快速的图…
- 任意角度的高质量的快速的图像旋转
- 图形图像处理 —— 任意角度的高质量的快速的图像旋转
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 下篇 补充话题
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 下篇 补充话题
- 任意角度的高质量的快速的图像旋转 中篇 高质量的旋转
- 基于C++图像处理-任意角度的高质量的快速的图像旋转
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 中篇 高质量的旋转
- 任意角度的高质量的快速的图像旋转 下篇 补充话题
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 下篇 高质量的旋转
- 图形图像处理-之-任意角度的高质量的快速的图像旋转 上篇 纯软件的任意角度的快速旋转
- 图形图像处理-之-任意角度的高质量的快速的图像旋转