//////////////////////////////////////////////////////////////////// // ImageBMP.cpp // // Copyright 2007 cDc@seacave // Distributed under the Boost Software License, Version 1.0 // (See http://www.boost.org/LICENSE_1_0.txt) #include "Common.h" #ifdef _IMAGE_BMP #include "ImageBMP.h" using namespace SEACAVE; // D E F I N E S /////////////////////////////////////////////////// // S T R U C T S /////////////////////////////////////////////////// #ifndef _MSC_VER typedef long LONG; typedef struct tagBITMAPFILEHEADER { WORD bfType; DWORD bfSize; WORD bfReserved1; WORD bfReserved2; DWORD bfOffBits; } BITMAPFILEHEADER; typedef struct tagBITMAPINFOHEADER { DWORD biSize; LONG biWidth; LONG biHeight; WORD biPlanes; WORD biBitCount; DWORD biCompression; DWORD biSizeImage; LONG biXPelsPerMeter; LONG biYPelsPerMeter; DWORD biClrUsed; DWORD biClrImportant; } BITMAPINFOHEADER; /* constants for the biCompression field */ #define BI_RGB 0L #define BI_RLE8 1L #define BI_RLE4 2L #define BI_BITFIELDS 3L #define BI_JPEG 4L #define BI_PNG 5L #endif CImageBMP::CImageBMP() { } // Constructor CImageBMP::~CImageBMP() { } // Destructor /*----------------------------------------------------------------*/ HRESULT CImageBMP::ReadHeader() { // Jump to the beginning of the file ((ISTREAM*)m_pStream)->setPos(0); // Read the BITMAPFILEHEADER // and check the type field to make sure we have a .bmp file // bfType should contain the letters "BM" BITMAPFILEHEADER bmp_fileheader; if (sizeof(BITMAPFILEHEADER) != m_pStream->read(&bmp_fileheader, sizeof(BITMAPFILEHEADER)) || memcmp(&bmp_fileheader.bfType, "BM", 2)) { LOG(LT_IMAGE, _T("error: invalid BMP image")); return _INVALIDFILE; } // Read the BITMAPINFOHEADER. // and double Check to make sure we got a correct BITMAPINFOHEADER BITMAPINFOHEADER bmp_infoheader; if (sizeof(BITMAPINFOHEADER) != m_pStream->read(&bmp_infoheader, sizeof(BITMAPINFOHEADER)) || bmp_infoheader.biSize != sizeof(bmp_infoheader)) { LOG(LT_IMAGE, _T("error: invalid BMP image")); return _INVALIDFILE; } // Check for unsupported format: biPlanes MUST equal 1 // and we can only handle uncompressed .bmps if (bmp_infoheader.biPlanes != 1 || (bmp_infoheader.biCompression != BI_RGB && bmp_infoheader.biCompression != BI_BITFIELDS)) { LOG(LT_IMAGE, "error: unsupported BMP image"); return _INVALIDFILE; } // Initililize our width, height and format as the .bmp we are loading m_dataWidth = m_width = bmp_infoheader.biWidth; m_dataHeight= m_height = bmp_infoheader.biHeight; m_numLevels = 0; m_level = 0; m_lineWidth = (bmp_fileheader.bfSize-bmp_fileheader.bfOffBits)/bmp_infoheader.biHeight; //bmp_infoheader.biSizeImage may be set to zero for BI_RGB bitmaps m_stride = bmp_infoheader.biBitCount>>3; switch (m_stride) { case 1: m_format = PF_GRAY8; break; case 2: m_format = PF_R5G6B5; break; case 3: m_format = PF_R8G8B8; break; case 4: m_format = PF_R8G8B8A8; break; default: LOG(LT_IMAGE, "error: unsupported BMP image"); return _INVALIDFILE; } // Ensure m_lineWidth is DWORD aligned while ((m_lineWidth%4) != 0) ++m_lineWidth; // Jump to the location where the bitmap data is stored ((ISTREAM*)m_pStream)->setPos(bmp_fileheader.bfOffBits); return _OK; } // ReadHeader /*----------------------------------------------------------------*/ HRESULT CImageBMP::ReadData(void* pData, PIXELFORMAT dataFormat, Size nStride, Size lineWidth) { // read data const size_t nSize = m_width*m_stride; const size_t nPad = m_lineWidth-nSize; CAutoPtrArr const bufferPad(nPad ? new uint8_t[nPad] : NULL); if (dataFormat == m_format && nStride == m_stride) { // read image directly to the data buffer (BYTE*&)pData += (m_height-1)*lineWidth; for (Size j=0; jread(pData, nSize) || (nPad && nPad != m_pStream->read(bufferPad, nPad))) return _INVALIDFILE; } else { // read image to a buffer and convert it CAutoPtrArr const buffer(new uint8_t[m_lineWidth]); for (Size j=0; jread(buffer, m_lineWidth)) return _INVALIDFILE; if (!FilterFormat((uint8_t*)pData+(m_height-j-1)*lineWidth, dataFormat, nStride, buffer, m_format, m_stride, m_width)) return _FAIL; } } return _OK; } // ReadData /*----------------------------------------------------------------*/ HRESULT CImageBMP::WriteHeader(PIXELFORMAT imageFormat, Size width, Size height, BYTE /*numLevels*/) { // write header m_numLevels = 0; m_level = 0; DWORD dwCompression; switch (imageFormat) { case PF_A8: case PF_GRAY8: m_stride = 1; dwCompression = BI_RGB; m_format = PF_GRAY8; break; case PF_R5G6B5: m_stride = 2; dwCompression = BI_BITFIELDS; m_format = PF_R5G6B5; break; case PF_B8G8R8: case PF_R8G8B8: m_stride = 3; dwCompression = BI_RGB; m_format = PF_R8G8B8; break; case PF_R8G8B8A8: case PF_A8R8G8B8: case PF_B8G8R8A8: case PF_A8B8G8R8: m_stride = 4; dwCompression = BI_BITFIELDS; m_format = PF_R8G8B8A8; break; default: LOG(LT_IMAGE, "error: unsupported BMP image format"); return _INVALIDFILE; } m_dataWidth = m_width = width; m_dataHeight= m_height = height; m_lineWidth = m_width*m_stride; // Ensure m_lineWidth is DWORD aligned while ((m_lineWidth%4) != 0) ++m_lineWidth; ((OSTREAM*)m_pStream)->setPos(0); // Write the BITMAPFILEHEADER and the BITMAPINFOHEADER. const BITMAPFILEHEADER bmp_fileheader = { 0x4d42, //BM (DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER)+m_lineWidth*m_height, //total file size 0, 0, //reserved (DWORD)sizeof(BITMAPFILEHEADER)+(DWORD)sizeof(BITMAPINFOHEADER) //offset to the bitmap bits }; const BITMAPINFOHEADER bmp_infoheader = { (DWORD)sizeof(BITMAPINFOHEADER), //size of this structure (LONG)m_width, (LONG)m_height, //dim (WORD)1, //number of color planes being used (must be set to 1) (WORD)(m_stride<<3), //number of bits per pixel dwCompression, //compression method being used m_lineWidth*m_height, //raw image size 0, 0, //pixels-per-meter 0, //number of colors in the color palette 0 //number of important colors used }; if (sizeof(BITMAPFILEHEADER) != m_pStream->write(&bmp_fileheader, sizeof(BITMAPFILEHEADER)) || sizeof(BITMAPINFOHEADER) != m_pStream->write(&bmp_infoheader, sizeof(BITMAPINFOHEADER))) { LOG(LT_IMAGE, "error: failed writing the BMP image"); return _INVALIDFILE; } return _OK; } // WriteHeader /*----------------------------------------------------------------*/ HRESULT CImageBMP::WriteData(void* pData, PIXELFORMAT dataFormat, Size nStride, Size lineWidth) { // write data const size_t nSize = m_width*m_stride; const size_t nPad = m_lineWidth-nSize; CAutoPtrArr const bufferPad(nPad ? new uint8_t[nPad] : NULL); if (nPad) memset(bufferPad, 0, nPad); if (dataFormat == m_format && nStride == m_stride) { // write data buffer directly to the image for (Size j=0; jwrite((uint8_t*)pData+(m_height-j-1)*lineWidth, nSize) || (nPad && nPad != m_pStream->write(bufferPad, nPad))) return _INVALIDFILE; } else { // convert data to a buffer and write it CAutoPtrArr const buffer(new uint8_t[m_lineWidth]); for (Size j=0; jwrite(buffer, m_lineWidth)) return _INVALIDFILE; } } return _OK; } // WriteData /*----------------------------------------------------------------*/ #endif // _IMAGE_BMP