图片缩放旋转Word格式.docx
《图片缩放旋转Word格式.docx》由会员分享,可在线阅读,更多相关《图片缩放旋转Word格式.docx(50页珍藏版)》请在冰豆网上搜索。
#define
asm
__asm
typedef
unsigned
char
TUInt8;
//
[0..255]
struct
TARGB32
//32
bit
color
{
TUInt8
B,G,R,A;
A
is
alpha
};
TPicRegion
//一块颜色数据区的描述,便于参数传递
TARGB32*
pdata;
//颜色数据首地址
long
byte_width;
//一行数据的物理宽度(字节宽度);
//abs(byte_width)有可能大于等于width*sizeof(TARGB32);
width;
//像素宽度
height;
//像素高度
//那么访问一个点的函数可以写为:
inline
TARGB32&
Pixels(const
TPicRegion&
pic,const
x,const
y)
return
(
(TARGB32*)((TUInt8*)pic.pdata+pic.byte_width*y)
)[x];
}
B:
缩放原理和公式图示:
缩放后图片
原图片
(宽DW,高DH)
(宽SW,高SH)
(Sx-0)/(SW-0)=(Dx-0)/(DW-0)
(Sy-0)/(SH-0)=(Dy-0)/(DH-0)
=>
Sx=Dx*SW/DW
Sy=Dy*SH/DH
C:
缩放算法的一个参考实现
//给出一个最简单的缩放函数(插值方式为近邻取样,而且我“尽力”把它写得慢一些了:
D)
//Src.PColorData指向源数据区,Dst.PColorData指向目的数据区
//函数将大小为Src.Width*Src.Height的图片缩放到Dst.Width*Dst.Height的区域中
void
PicZoom0(const
Dst,const
Src)
if
(0==Dst.width)||(0==Dst.height)
||(0==Src.width)||(0==Src.height))
return;
for
(long
x=0;
x<
Dst.width;
++x)
y=0;
y<
Dst.height;
++y)
srcx=(x*Src.width/Dst.width);
srcy=(y*Src.height/Dst.height);
Pixels(Dst,x,y)=Pixels(Src,srcx,srcy);
//速度测试:
//PicZoom0
19.4fps
D:
优化PicZoom0函数
a.PicZoom0函数并没有按照颜色数据在内存中的排列顺序读写(内部循环递增y行
索引),将造成CPU缓存预读失败和内存颠簸导致巨大的性能损失,(很多硬件都有这种特性,
包括缓存、内存、显存、硬盘等,优化顺序访问,随机访问时会造成巨大的性能损失)
所以先交换x,y循环的顺序:
PicZoom1(const
//PicZoom1
30.1fps
b.“(x*Src.Width/Dst.Width)”表达式中有一个除法运算,它属于很慢的操作(比一般
的加减运算慢几十倍!
),使用定点数的方法来优化它;
PicZoom2(const
//函数能够处理的最大图片尺寸65536*65536
unsignedlongxrIntFloat_16=(Src.width<
<
16)/Dst.width+1;
//16.16格式定点数
unsignedlongyrIntFloat_16=(Src.height<
16)/Dst.height+1;
//可证明:
(Dst.width-1)*xrIntFloat_16<
Src.width成立
for(unsignedlongy=0;
{
for(unsignedlongx=0;
unsignedlongsrcx=(x*xrIntFloat_16)>
>
16;
unsignedlongsrcy=(y*yrIntFloat_16)>
Pixels(Dst,x,y)=Pixels(Src,srcx,srcy);
}
//PicZoom2
185.8fps
c.
在x的循环中y一直不变,那么可以提前计算与y相关的值;
1.可以发现srcy的值和x变量无关,可以提前到x轴循环之前;
2.展开Pixels函数,优化与y相关的指针计算;
voidPicZoom3(constTPicRegion&
Dst,constTPicRegion&
Src)
unsignedlongdst_width=Dst.width;
TARGB32*pDstLine=Dst.pdata;
unsignedlongsrcy_16=0;
TARGB32*pSrcLine=((TARGB32*)((TUInt8*)Src.pdata+Src.byte_width*(srcy_16>
16)));
unsignedlongsrcx_16=0;
dst_width;
pDstLine[x]=pSrcLine[srcx_16>
16];
srcx_16+=xrIntFloat_16;
srcy_16+=yrIntFloat_16;
((TUInt8*&
)pDstLine)+=Dst.byte_width;
//PicZoom3
414.4fps
d.定点数优化使函数能够处理的最大图片尺寸和缩放结果(肉眼不可察觉的误差)受到了一
定的影响,这里给出一个使用浮点运算的版本,可以在有这种需求的场合使用:
PicZoom3_float(const
//注意:
该函数需要FPU支持
doublexrFloat=1.000000001/((double)Dst.width/Src.width);
doubleyrFloat=1.000000001/((double)Dst.height/Src.height);
short
RC_Old;
RC_Edit;
//设置FPU的取整方式
为了直接使用fist浮点指令
FNSTCW
RC_Old
保存协处理器控制字,用来恢复
RC_Edit
保存协处理器控制字,用来修改
FWAIT
OR
RC_Edit,
0x0F00
改为
RC=11
使FPU向零取整
FLDCW
载入协处理器控制字,RC场已经修改
dst_width=Dst.width;
pDstLine=Dst.pdata;
double
srcy=0;
(unsigned
pSrcLine=((TARGB32*)((TUInt8*)Src.pdata+Src.byte_width*((long)srcy)));
/**//*
srcx=0;
pDstLine[x]=pSrcLine[(unsigned
long)srcx];
//因为默认的浮点取整是一个很慢
//的操作!
所以才使用了直接操作FPU的内联汇编代码。
srcx+=xrFloat;
}*/
fld
xrFloat
//st0==xrFloat
fldz
//st0==0
st1==xrFloat
fist
dword
ptr
srcx
//srcx=(long)st0
pDstLine[x]=pSrcLine[srcx];
fadd
st,st
(1)
//st0+=st1
fstp
st
srcy+=yrFloat;
((TUInt8*&
//恢复FPU的取整方式
//PicZoom3_float
286.2fps
e.注意到这样一个事实:
每一行的缩放比例是固定的;
那么可以预先建立一个缩放映射表格
来处理缩放映射算法(PicZoom3_Table和PicZoom3_float的实现等价);
PicZoom3_Table(const
long*
SrcX_Table
=
new
long[dst_width];
++x)//生成表
SrcX_Table
SrcX_Table[x]=(x*Src.width/Dst.width);
pSrcLine=((TARGB32*)((TUInt8*)Src.pdata+Src.byte_width*srcy));
pDstLine[x]=pSrcLine[SrcX_Table[x]];
delete
[]
SrcX_Table;
//PicZoom3_Table
390.1fps
f.为了加快缩放,可以采用根据缩放比例动态生成函数的方式来得到更快的缩放函数;
这
有点像编译器的工作原理;
要实现它需要的工作量比较大(或比较晦涩)就不再实现了;
(动态生成是一种不错的思路,但个人觉得对于缩放,实现它的必要性不大)
g.现代CPU中,在读取数据和写入数据时,都有自动的缓存机制;
很容易知道,算法中生
成的数据不会很快再次使用,所以不需要写入缓存的帮助;
在SSE指令集中增加了movntq
等指令来完成这个功能;
(尝试过利用CPU显式prefetcht0、prefetchnta预读指令或直接的mov读取指令等速度反
而略有下降:
但预读在copy算法中速度优化效果很明显)
PicZoom3_SSE(const
//警告:
函数需要CPU支持MMX和movntq指令
srcy_16=0;
pSrcLine=((TARGB32*)((TUInt8*)Src.pdata+Src.byte_width*(srcy_16>
asm
push
ebp
mov
esi,pSrcLine
edi,pDstLine
edx,xrIntFloat_16