You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

261 lines
7.6 KiB

////////////////////////////////////////////////////////////////////
// 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<uint8_t> 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; j<m_height; ++j,(uint8_t*&)pData-=lineWidth)
if (nSize != m_pStream->read(pData, nSize) ||
(nPad && nPad != m_pStream->read(bufferPad, nPad)))
return _INVALIDFILE;
} else {
// read image to a buffer and convert it
CAutoPtrArr<uint8_t> const buffer(new uint8_t[m_lineWidth]);
for (Size j=0; j<m_height; ++j) {
if (m_lineWidth != m_pStream->read(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<uint8_t> 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; j<m_height; ++j)
if (nSize != m_pStream->write((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<uint8_t> const buffer(new uint8_t[m_lineWidth]);
for (Size j=0; j<m_height; ++j) {
if (!FilterFormat(buffer, m_format, m_stride, (uint8_t*)pData+(m_height-j-1)*lineWidth, dataFormat, nStride, m_width))
return _FAIL;
if (m_lineWidth != m_pStream->write(buffer, m_lineWidth))
return _INVALIDFILE;
}
}
return _OK;
} // WriteData
/*----------------------------------------------------------------*/
#endif // _IMAGE_BMP