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

(完)

posted @ 2014-07-02 17:16  凤舞十天  阅读(3480)  评论(0编辑  收藏  举报