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 = 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 = 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];

        }

(完)

你可能感兴趣的:(缩放)