当我们要扫描一幅图350*350=122,500个像素.其实很小的一幅图.原代码需要3.284秒 优化后只需0.13秒,最后再优化只需0.081秒.
当我们在用CORE i5 i7的飞机时代,同志们有没有想象因为要在ARM(拖拉机时代)的800MHz内存32M的感觉呢?
说回正题:
在用DC.GetPixel(j, i)对这个图逐点扫描,发觉时间竟要3.284秒.实在难以接受.
那么是否有好的办法加快呢?
答案是肯定的.内存直接操作.
首先需要将DC转为内存块
好在有这么一个函数可用CreateDIBSection
HBITMAP m_hDIBitmap1 = CreateDIBSection(m_hDIBDC1, (BITMAPINFO *)&m_hdr, DIB_RGB_COLORS,(void **)&m_Bitmap1, NULL, 0);
具体用法查看MSDN
BitBlt(m_hDIBDC1,0,0,m_nWidth,m_nHeight,m_memDC,0,0,SRCCOPY); //从m_memDC图中能转到我们的内存m_Bitmap1中了,这是关键.
//当然这两三步因为是大遍的内存拷贝,耗时可以不计. 当然用DWORD dwS=GetTickCount();来查看也看出转为内存的时间也只需几毫秒.
到我们的具体代码段
for (i=0;i<m_nHeight;i++)
{
j=0;
do
{
while (j <= m_nWidth-1 && m_memDC.GetPixel(j, i) == colorTransparent)
{
j++;
}
iLeftX= j;
while (j <= m_nWidth-1 && m_memDC.GetPixel(j, i) != colorTransparent)
{
++j;
}
pRect->left = m_iLeftX+iLeftX;
pRect->right = m_iLeftX+j;
pRect->top = m_iTopY+i;
pRect->bottom = m_iTopY+i+1;
pRgnData->rdh.nCount++;
pRect++;
//这里要判断是否多于申请的内存
if (pRgnData->rdh.nCount>=MAXNUM)
{
}
}while(j <m_nWidth-1 );
}
//这段代码用时达到惊人的3秒多.
为了代码的简结,将判断是否相等的代码写成函数
bool GetColorYes(BYTE* byte,int iWidth,int iHeight ,int x,int y, BYTE r,BYTE g,BYTE b)
{ //计算内存的其中一个点是否相同颜色
int nPixelSize=4;
BYTE btB2,btG2,btR2;
btB2 = byte[(iHeight-y-1) * iWidth * nPixelSize + x * nPixelSize ] ;
btG2 = byte[(iHeight-y-1) * iWidth * nPixelSize + x * nPixelSize + 1 ] ;
btR2 = byte[(iHeight-y-1) * iWidth * nPixelSize + x * nPixelSize + 2 ] ;
if ( btB2==b && btG2==g && btR2==r )
return true;
else
return false;
}
//对应的汇编
汇编指令
.text:00013668 CDZQSPLASH_DLG__GetColorYes ; CODE XREF: CDZQSPLASH_DLG__SetRgnSplash+368p
.text:00013668 ; CDZQSPLASH_DLG__SetRgnSplash+3CCp
.text:00013668 ; DATA XREF: ...
.text:00013668
.text:00013668 arg_0 = 0 //参数1
.text:00013668 arg_4 = 4 //参数2
.text:00013668 arg_8 = 8 //参数3
.text:00013668 arg_C = 0xC //参数4
.text:00013668 arg_10 = 0x10 //参数5
.text:00013668
01.text:00013668 STMFD SP!, {R4,LR} //将寄存器列表中的R4-LR存入堆栈
02.text:0001366C LDR R0, [SP,#8+arg_4] //读入参数2
03.text:00013670 LDR LR, [SP,#8+arg_0] //读入参数1
04.text:00013674 LDRB R4, [SP,#8+arg_10] //读入参数5
05.text:00013678 SUB R3, R3, R0 //减法
06.text:0001367C SUB R3, R3, #1 //再减 1
07.text:00013680 MLA R2, R3, R2, LR
08.text:00013684 LDRB R3, [R1,R2,LSL#2]!
09.text:00013688 CMP R3, R4
10.text:0001368C BNE loc_136B0
11.text:00013690 LDRB R2, [R1,#1]
12.text:00013694 LDRB R3, [SP,#8+arg_C]
13.text:00013698 CMP R2, R3
14.text:0001369C LDREQB R2, [R1,#2]
15.text:000136A0 LDREQB R3, [SP,#8+arg_8]
16.text:000136A4 CMPEQ R2, R3
17.text:000136A8 MOVEQ R0, #1
18.text:000136AC LDMEQFD SP!, {R4,PC}
19.text:000136B0
20.text:000136B0 loc_136B0 ; CODE XREF: CDZQSPLASH_DLG__GetColorYes+24j
21.text:000136B0 MOV R0, #0
22.text:000136B4 LDMFD SP!, {R4,PC}
.text:000136B4 ; End of function CDZQSPLASH_DLG__GetColorYes
//可以看到比较CMP执行为3次,指令流水线还是很长,效果比直接用GetPixel已好了很多很多,快了接近300倍。别小看这300倍,当那大图像处理只需要运行一天的程序,没优化的代码则需要用300天?有点吓人。。。
for (i=0;i<m_nHeight;i++)
{
j=0;
do
{
while (j <= m_nWidth-1 && GetColorYes(m_Bitmap1,m_nWidth,m_nHeight,j,i,btR1,btG1,btB1))
{
j++;
}
iLeftX= j;
while (j <= m_nWidth-1 && !GetColorYes(m_Bitmap1,m_nWidth,m_nHeight,j,i,btR1,btG1,btB1))
{
++j;
}
pRect->left = m_iLeftX+iLeftX;
pRect->right = m_iLeftX+j;
pRect->top = m_iTopY+i;
pRect->bottom = m_iTopY+i+1;
pRgnData->rdh.nCount++;
pRect++;
//这里要判断是否多于申请的内存
if (pRgnData->rdh.nCount>=MAXNUM)
{
}
}while(j <m_nWidth-1 );
}
//这段代码耗时0.13秒.
是否还有优化的可能呢?回过头来看,因为图像的掩色刚好是B 还算好,只需一次CMP就跳了,但如果是R呢?那真是可悲的一件事。。。
刚好内存中的数据为BGRP.32位与一个int的值对应。再作优化存在可能了。
那就是构造一个int类型.
unsigned int *iRGB;// 无符号int
pRGB[0]=btB1;
pRGB[1]=btG1;
pRGB[2]=btR1;
pRGB[3]=0;
iRGB=(unsigned int *)pRGB; //强制转换
bool GetColorYesInt(BYTE* byte,int iWidth,int iHeight ,int x,int y,unsigned int *iRGB )
{ //计算内存的其中一个点是否相同颜色
int nPixelSize=4;
unsigned int *iBtRGB;// 无符号int
iBtRGB= (unsigned int *)(byte+((iHeight-y-1) * iWidth * nPixelSize + x * nPixelSize ));
if (iBtRGB[0]==iRGB[0] )
return true;
else
return false;
}
对应汇编指令
汇编指令
.text:00013668 CDZQSPLASH_DLG__GetColorYesInt ; DATA XREF: .pdata:00039338o
.text:00013668
.text:00013668 arg_0 = 0
.text:00013668 arg_4 = 4
.text:00013668 arg_8 = 8
.text:00013668
01.text:00013668 STMFD SP!, {R4,LR}
02.text:0001366C LDR R0, [SP,#8+arg_4]
03.text:00013670 LDR LR, [SP,#8+arg_0]
04.text:00013674 LDR R4, [SP,#8+arg_8]
05.text:00013678 SUB R3, R3, R0
06.text:0001367C SUB R3, R3, #1
07.text:00013680 MLA R2, R3, R2, LR
08.text:00013684 LDR R0, [R4]
09.text:00013688 LDR R3, [R1,R2,LSL#2]
10.text:0001368C CMP R3, R0
11.text:00013690 MOVEQ R0, #1
12.text:00013694 MOVNE R0, #0
13.text:00013698 LDMFD SP!, {R4,PC}
.text:00013698 ; End of function CDZQSPLASH_DLG__GetColorYesInt
从上面的汇编可以看到流水线减至13行指令,并且,只进行一次比较。
unsigned int *iRGB;// 无符号int
pRGB[0]=btB1;
pRGB[1]=btG1;
pRGB[2]=btR1;
pRGB[3]=0;
iRGB=(unsigned int *)pRGB; //强制转换
for (i=0;i<m_nHeight;i++)
{
j=0;
do
{
while (j <= m_nWidth-1 && GetColorYesInt(m_Bitmap1,m_nWidth,m_nHeight,j,i,iRGB))
{
j++;
}
iLeftX= j;
while (j <= m_nWidth-1 && !GetColorYesInt(m_Bitmap1,m_nWidth,m_nHeight,j,i,iRGB))
{
++j;
}
pRect->left = m_iLeftX+iLeftX;
pRect->right = m_iLeftX+j;
pRect->top = m_iTopY+i;
pRect->bottom = m_iTopY+i+1;
pRgnData->rdh.nCount++;
pRect++;
//这里要判断是否多于申请的内存
if (pRgnData->rdh.nCount>=MAXNUM)
{
}
}while(j <m_nWidth-1 );
}
最后的疗效,实在不错..时间只是用了 0.08秒.
学会看汇编是优代码的一个好方法..
小试挖空一个图片看看看...
原图
最后效果图
另:CSDN的这个编辑器好弱.代码段没显示出来的东东好垃圾,只好粘算了...搞得都没兴趣写什么文了...