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
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
|
|
|