最近跟一个同事讨论一个读取bmp图片显示的问题时,他提出想用汇编进行优化。于是我就写了个例子尝试了一下,代码如下:
#include <iostream>
#include <windows.h>
using namespace std;
#define SOURCE_LEN 400000
#define DEST_LEN 800000
int main()
{
char *pBuff = new char[SOURCE_LEN]; // 先分配400k
char *pDest = new char[DEST_LEN];
FILE *fp = fopen("c://test.bmp", "rb");
if (!fp)
return -1;
int iCount = fread(pBuff, 1, SOURCE_LEN, fp);
if (iCount == 0)
return -2;
BITMAPFILEHEADER *pfHeader = (BITMAPFILEHEADER*) pBuff;
int iOffSet = pfHeader->bfOffBits;
BITMAPINFOHEADER* pIheader = (BITMAPINFOHEADER*) &(pBuff[sizeof(BITMAPFILEHEADER)]);
if (pIheader->biBitCount != 24)
return -3;
int iWidth = pIheader->biWidth;
int iHeight = pIheader->biHeight;
int i24Pitch = pIheader->biSizeImage / iHeight;
int i32Pitch = iWidth * 4;
char* pb24 = &pBuff[iOffSet];
char* pb32 = &pDest[i32Pitch*(iHeight-1)]; //pb32 指向缓冲最后一行的开头, pb24指向bmp文件数据第一行
__asm
{
push esi
push edi
push ecx
push eax
mov ecx, iHeight
_loop1:
mov esi, pb24
mov edi, pb32
push ecx
mov ecx, iWidth
_loop2:
mov al, byte PTR [esi]; 按位来移动, 因为esi的值是按三增加的,不对齐
mov byte ptr[edi+2], al
mov al, byte PTR [esi+1]
mov byte ptr[edi+1], al
mov al, byte PTR [esi+2]
mov byte ptr[edi], al
mov al, 0xff
mov byte PTR[edi+3], al
add esi,3; 24 要加3
add edi,4; 32 加 4
dec ecx;
jnz _loop2;
; 每处理一行pb24 要制向下一行.pb32要指向上一行
mov eax, pb24;
add eax, i24Pitch
mov pb24, eax
mov eax, pb32
sub eax, i32Pitch
mov pb32, eax
pop ecx
dec ecx
jnz _loop1
pop eax ; 恢复寄存器
pop ecx
pop edi
pop esi
}
delete pBuff;
delete pDest;
return 0;
}
这个是显示的测试代码. 直接写点,主要是测试代码是否正确,没有考虑效率问题.
for (int i = 0; i < m_iHeight; i++)
{
for (int j = 0; j < m_iWidth; j++)
{
int iIndex = i*m_iWidth*4+j*4;
DWORD dwColor = *(DWORD*)&m_pDest[iIndex];
dwColor = dwColor & 0x00ffffff;
pDC->SetPixel(j, i, dwColor);
}
}
这个内联汇编实现了把bmp的24位格式转换为可在DirectDraw下使用的32位RGBA格式.
写完之后,发现与用c写出的代码相比,效率并无提高.以前就听说编译器优化是如何强劲,
现在是领教了哦.
其中有一些值得关注的问题,
1. BMP文件中颜色数据是以BGR的方式保存的,需要进行转换.
2. 要记得保存使用的寄存器.虽然VC inline Assemble 不要求保存寄存器,但是还是要有这个意识才可以哦.
3. 图片文件每行要4字节对齐,所以必须计算Pitch,而不能通过每次累加3来确定每行边界.
#include <iostream>
#include <windows.h>
using namespace std;
#define SOURCE_LEN 400000
#define DEST_LEN 800000
int main()
{
char *pBuff = new char[SOURCE_LEN]; // 先分配400k
char *pDest = new char[DEST_LEN];
FILE *fp = fopen("c://test.bmp", "rb");
if (!fp)
return -1;
int iCount = fread(pBuff, 1, SOURCE_LEN, fp);
if (iCount == 0)
return -2;
BITMAPFILEHEADER *pfHeader = (BITMAPFILEHEADER*) pBuff;
int iOffSet = pfHeader->bfOffBits;
BITMAPINFOHEADER* pIheader = (BITMAPINFOHEADER*) &(pBuff[sizeof(BITMAPFILEHEADER)]);
if (pIheader->biBitCount != 24)
return -3;
int iWidth = pIheader->biWidth;
int iHeight = pIheader->biHeight;
int i24Pitch = pIheader->biSizeImage / iHeight;
int i32Pitch = iWidth * 4;
char* pb24 = &pBuff[iOffSet];
char* pb32 = &pDest[i32Pitch*(iHeight-1)]; //pb32 指向缓冲最后一行的开头, pb24指向bmp文件数据第一行
__asm
{
push esi
push edi
push ecx
push eax
mov ecx, iHeight
_loop1:
mov esi, pb24
mov edi, pb32
push ecx
mov ecx, iWidth
_loop2:
mov al, byte PTR [esi]; 按位来移动, 因为esi的值是按三增加的,不对齐
mov byte ptr[edi+2], al
mov al, byte PTR [esi+1]
mov byte ptr[edi+1], al
mov al, byte PTR [esi+2]
mov byte ptr[edi], al
mov al, 0xff
mov byte PTR[edi+3], al
add esi,3; 24 要加3
add edi,4; 32 加 4
dec ecx;
jnz _loop2;
; 每处理一行pb24 要制向下一行.pb32要指向上一行
mov eax, pb24;
add eax, i24Pitch
mov pb24, eax
mov eax, pb32
sub eax, i32Pitch
mov pb32, eax
pop ecx
dec ecx
jnz _loop1
pop eax ; 恢复寄存器
pop ecx
pop edi
pop esi
}
delete pBuff;
delete pDest;
return 0;
}
这个是显示的测试代码. 直接写点,主要是测试代码是否正确,没有考虑效率问题.
for (int i = 0; i < m_iHeight; i++)
{
for (int j = 0; j < m_iWidth; j++)
{
int iIndex = i*m_iWidth*4+j*4;
DWORD dwColor = *(DWORD*)&m_pDest[iIndex];
dwColor = dwColor & 0x00ffffff;
pDC->SetPixel(j, i, dwColor);
}
}
这个内联汇编实现了把bmp的24位格式转换为可在DirectDraw下使用的32位RGBA格式.
写完之后,发现与用c写出的代码相比,效率并无提高.以前就听说编译器优化是如何强劲,
现在是领教了哦.
其中有一些值得关注的问题,
1. BMP文件中颜色数据是以BGR的方式保存的,需要进行转换.
2. 要记得保存使用的寄存器.虽然VC inline Assemble 不要求保存寄存器,但是还是要有这个意识才可以哦.
3. 图片文件每行要4字节对齐,所以必须计算Pitch,而不能通过每次累加3来确定每行边界.