首页 > 代码库 > BMP RGB888转RGB565 +上下翻转+缩放

BMP RGB888转RGB565 +上下翻转+缩放

 
典型的BMP图像文件由四部分组成:
(1) 位图头文件数据结构,它包含BMP图像文件的类型、文件大小和位图起始位置等信息;
  
typedef struct tagBITMAPFILEHEADER {        WORD    bfType;//位图文件的类型,必须为BM(1-2字节)        DWORD   bfSize;//位图文件的大小,以字节为单位(3-6字节,低位在前)        WORD    bfReserved1;//位图文件保留字,必须为0(7-8字节)        WORD    bfReserved2;//位图文件保留字,必须为0(9-10字节)        DWORD   bfOffBits;//位图数据的起始位置,以相对于位图(11-14字节,低位在前)} BITMAPFILEHEADER;
(2) 位图信息数据结构,它包含有BMP图像的宽、高、压缩方法,以及定义颜色等信息;
typedef struct tagBITMAPINFOHEADER{        DWORD      biSize;//本结构所占用字节数(15-18字节)        LONG       biWidth;//位图的宽度,以像素为单位(19-22字节)        LONG       biHeight;//位图的高度,以像素为单位(23-26字节)        WORD       biPlanes;        WORD       biBitCount;//每个像素所需的位数,必须是1(双色),(29-30字节) //4(16色),8(256色)16(高彩色)或24(真彩色)之一         DWORD      biCompression;        DWORD      biSizeImage;//位图的大小(其中包含了为了补齐行数是4的倍数而添加的空字节),以字节为单位(35-38字节)        LONG       biXPelsPerMeter;        LONG       biYPelsPerMeter;        DWORD      biClrUsed;        DWORD      biClrImportant;} BITMAPINFOHEADER;
(3) 调色板,这个部分是可选的,有些位图需要调色板,有些位图,比如真彩色图(24位的BMP)就不需要调色板;
(4) 位图数据,这部分的内容根据BMP位图使用的位数不同而不同,在24位图中直接使用RGB,而其他的小于24位的使用调色板中颜色索引值。
 
1. 打开位图并显示
    BITMAPFILEHEADER bmpHeader;//文件头    BITMAPINFOHEADER bmpInfo;//信息头    CFileDialog dlg(TRUE, "*.BMP", NULL, NULL,"位图文件(*.BMP)|*.bmp;*.BMP|",this);    CFile bmpFile;//记录打开文件    CString strFileName;//记录选择文件路径    if (!dlg.DoModal() == IDOK) return;    strFileName = dlg.GetPathName();    //以只读的方式打开文件    if(!bmpFile.Open(strFileName, CFile::modeRead|CFile::typeBinary)) return;    //读取文件头到bmpHeader    if (bmpFile.Read(&bmpHeader,sizeof(BITMAPFILEHEADER)) != sizeof(BITMAPFILEHEADER))    {        AfxMessageBox("read bmp header failed!");        return;    }    /*0x4d42=’BM’,表示是Windows支持的BMP格式。    (注意:查ascii表B 0x42,M0x4d,bfType 为两个字节,B为low字节,M为high字节    所以bfType=0x4D42,而不是0x424D    */    if (bmpHeader.bfType != 0x4d42)    {        AfxMessageBox("invalid file type!");        return;    }    //读取文件信息头bmpInfo    if (bmpFile.Read(&bmpInfo,sizeof(BITMAPINFOHEADER)) != sizeof(BITMAPINFOHEADER))    {        AfxMessageBox("read bmp infor header failed!");        return;    }    //确认是24位位图    if (bmpInfo.biBitCount != 24)//图像的位数    {        AfxMessageBox("File is not 24 bit.Application doesn‘t support this kind of file!");        return;    }    /*        typedef struct tagBITMAPINFO {            BITMAPINFOHEADER    bmiHeader;            RGBQUAD             bmiColors[1];        } BITMAPINFO;    */    pBmpInfo = (BITMAPINFO *)new char[sizeof(BITMAPINFOHEADER)];    if (!pBmpInfo)    {        AfxMessageBox("memory error!");        return;    }    //为图像数据申请空间    memcpy(pBmpInfo, &bmpInfo, sizeof(BITMAPINFOHEADER));    //计算颜色表区域大小:结构体的大小(包含颜色表)-颜色数据的偏移量    DWORD dataBytes = bmpHeader.bfSize - bmpHeader.bfOffBits;    pBmpData = (BYTE*)new char[dataBytes];    if (!pBmpData)    {        AfxMessageBox("memory error!");        delete pBmpData;        return;    }    if (bmpFile.Read(pBmpData,dataBytes) != dataBytes)    {        AfxMessageBox("Read bmp data failed!");        delete pBmpInfo;        delete pBmpData;        return;    }    //bmpFile.Close();    CWnd *pWnd=GetDlgItem(IDC_IMAGE);//获得pictrue控件窗口的句柄    CRect rect;    pWnd->GetClientRect(&rect);//获得pictrue控件所在的矩形区域    CDC *pDC=pWnd->GetDC();//获得pictrue控件的DC    //显示图片    pDC->SetStretchBltMode(COLORONCOLOR);    StretchDIBits(pDC->GetSafeHdc(),0,0,rect.Width(),rect.Height(),0,0,bmpInfo.biWidth,bmpInfo.biHeight,pBmpData,pBmpInfo,DIB_RGB_COLORS,SRCCOPY);    iBmpWidth = bmpInfo.biWidth;                         iBmpHeight = bmpInfo.biHeight;

 

2. 将24位图转化为16位位图(从RGB888到RGB565)

需要将原来的颜色表数据分离成R,G,B三组,然后R舍弃3位,G舍弃2位,B舍弃3位。

a. 分离,读取RGB数据存到三个BYTE*数组里m_pR, m_pG, m_pB;

     LARGE_INTEGER liSize;        liSize.QuadPart = 0;        ::GetFileSizeEx(bmpFile, &liSize);        int nBitmapSize = abs(iBmpHeight) * WIDTHBYTES(iBmpWidth * bmpInfo.biBitCount);        if(bmpInfo.biPlanes != 1)        {            break;        }        if(bmpInfo.biBitCount != 1 && bmpInfo.biBitCount != 4 && bmpInfo.biBitCount != 8 && bmpInfo.biBitCount != 16 && bmpInfo.biBitCount != 24 && bmpInfo.biBitCount != 32)        {            break;        }        if(bmpInfo.biCompression != BI_RGB && bmpInfo.biCompression != BI_BITFIELDS)        {            break;        }        if(bmpInfo.biWidth <= 0 || bmpInfo.biHeight == 0)        {            break;        }        if(bmpHeader.bfOffBits + nBitmapSize > liSize.QuadPart)        {            break;        }        //m_pR,m_pG,m_pB位BYTE *;        m_pR = new BYTE[bmpInfo.biWidth * abs(bmpInfo.biHeight)];        m_pG = new BYTE[bmpInfo.biWidth * abs(bmpInfo.biHeight)];        m_pB = new BYTE[bmpInfo.biWidth * abs(bmpInfo.biHeight)];        if(bmpInfo.biBitCount < 16)        {            //...        }        else if(bmpInfo.biBitCount == 16)        {            //...        }        else if(bmpInfo.biBitCount == 24)        {            ::SetFilePointer(bmpFile, bmpHeader.bfOffBits, NULL, SEEK_SET);            BYTE *pData;            pData = new BYTE[nBitmapSize];            DWORD dwByteRead = 0;            dwByteRead = 0;            ::ReadFile(bmpFile, pData, nBitmapSize, &dwByteRead, NULL);            //pR, pG, pB是临时指针            BYTE *pR = m_pR;            BYTE *pG = m_pG;            BYTE *pB = m_pB;            for(int j = 0; j < abs(bmpInfo.biHeight); j++)            {                BYTE *pTemp = pData + WIDTHBYTES(bmpInfo.biWidth * bmpInfo.biBitCount) * j;                for(int i = 0; i < bmpInfo.biWidth; i++)                {                    *pB++ = *pTemp++;                    *pG++ = *pTemp++;                    *pR++ = *pTemp++;                }            }            delete[] pData;        }        else if(bmpInfo.biBitCount == 32)        {            //...        }

 

b. 转化,从24位转化成16位

//新文件的头信息    BITMAPFILEHEADER bmfh;    BITMAPINFOHEADER bmih;    memset(&bmfh, 0, sizeof(bmfh));    memset(&bmih, 0, sizeof(bmih));    int nBitmapSize = abs(bmpInfo.biHeight) * WIDTHBYTES(bmpInfo.biWidth * 16);    length = nBitmapSize;    bmfh.bfType = MB;    bmfh.bfOffBits = sizeof(bmfh) + sizeof(bmih) + 12;    bmfh.bfSize = bmfh.bfOffBits + nBitmapSize;    bmih.biSize = sizeof(bmih);    bmih.biWidth = bmpInfo.biWidth;    bmih.biHeight = bmpInfo.biHeight;    bmih.biPlanes = 1;    bmih.biBitCount = 16;    bmih.biCompression = BI_BITFIELDS;    bmih.biSizeImage = nBitmapSize;    /* 转化后的颜色表保存在pData中 */    BYTE *pData;    pData = new BYTE[nBitmapSize];    memset(pData, 0, nBitmapSize);    myData = new char[nBitmapSize];    memset(myData,0,nBitmapSize);    char * inverseData = http://www.mamicode.com/new char[nBitmapSize];    memset(inverseData, 0, nBitmapSize);    BYTE *pR = m_pR;    BYTE *pG = m_pG;    BYTE *pB = m_pB;    /* 以下是转化的核心代码 */    /* 转化过程图像的宽和高是不变的,变得是每个像素点从3个字节变成了2个字节 */    for(int j = 0; j < abs(bmih.biHeight); j++)    {        /* 临时指针pTemp 每次从新的一行开始 */        WORD *pTemp = (WORD *)(pData + WIDTHBYTES(bmih.biWidth * 16) * j);        for(int i = 0; i < bmih.biWidth; i++)        {#if 0            *pTemp++ = ((WORD)(*pR++ << 8) & 0xf800) | ((WORD)(*pG++ << 3) & 0x07e0) | ((WORD)(*pB++ >> 3) & 0x001f);#else            /* 分别去掉低3,2,3位 */            int nR = (*pR++ + 4) >> 3;            int nG = (*pG++ + 2) >> 2;            int nB = (*pB++ + 4) >> 3;            /* nR位5位,不能超过31,nG为6位,不能超过63,nB同nR */            if(nR > 31) nR = 31;            if(nG > 63) nG = 63;            if(nB > 31) nB = 31;            /* 将新的R,G,B数据拼到2个字节里,比例为5:6:5 */            *pTemp++ = (nR << 11) | (nG << 5) | nB;#endif        }    }

 

3. 将图片上下对称翻转。图像点阵是image_width * image_height 的大小,翻转时只需要将 上下两半对应的位置的点对换就行了。经上面的转换后,图像中每个点占2个字节了,所以每个点换两个字节就行。
  int image_width = bmih.biWidth;    int image_height = bmih.biHeight;    int index = 2;//表示16色,占2个字节    for(int h = 0; h < image_height/2; h++)        for (int w = 0; w < image_width; w++)        {            /* iCoordM 和 iCoordN分别是上下对称的点在颜色表字节数组中的坐标,                交换iCoordM位置和iCoordM+1位置2个字节就行了。            */            const int iCoordM = index*(h*image_width + w);            const int iCoordN = index*((image_height - h -1)*image_width + w);            BYTE Tmp = pData[iCoordM];            pData[iCoordM] = pData[iCoordN];            pData[iCoordN] = Tmp;            Tmp = pData[iCoordM+1];            pData[iCoordM + 1] = pData[iCoordN + 1];             pData[iCoordN + 1] = Tmp;            /*如果是24位图像,就加上下面的内容,就是再交换一个字节*/            /*Tmp = pData[iCoordM + 2];            pData[iCoordM + 2] = pData[iCoordN + 2];            pData[iCoordN + 2] = Tmp;*/        }

 

4.  缩放 

 

/* new_heigth和new_width为目标图片的大小,可自定义 */    int new_height = 430;    int new_width = 160;    int newSize = new_width*new_height*2;    length = newSize;    BYTE * newData = http://www.mamicode.com/new BYTE[newSize];    /* 分配新的内存 */    memset(newData, 0, new_width*new_height*2);    for(int h = 0; h < image_height; h++)        for (int w = 0; w < image_width; w++)        {            /* 计算每个像素的起始位置 */            const int iCoordM = index * (h * image_width + w);            //const int iCoordN = index*((image_height - h -1)*image_width + w);            BYTE Tmp = pData[iCoordM];            int x = int( double(h)/image_height * new_height);//新行数            int y = int( double(w)/image_width  * new_width);//新列数            /* 将原来图片的每个像素按比例位置映射到新的图片上             原来的位置是(w, h),通过比例就可以计算出新的位置是             (int(double(w)/image_width*new_width), int(double(h)/image_height*new_height)),            然后将新的位置开始的2个字节等于原来位置的2个字节,就完成了缩放。                */            newData[(x*new_width + y)*2] = Tmp;            newData[(x*new_width + y)*2 + 1] = pData[iCoordM+1];        }

(完)