259 lines
6.9 KiB
C++
259 lines
6.9 KiB
C++
/*=============================================================================
|
|
BmpImage.cpp : BMP image file format implementation.
|
|
Copyright (c) 2001 Crytek Studios. All Rights Reserved.
|
|
|
|
Revision history:
|
|
* Created by Khonich Andrey
|
|
|
|
=============================================================================*/
|
|
|
|
#include "RenderPCH.h"
|
|
#include "CImage.h"
|
|
#include "BmpImage.h"
|
|
|
|
#include "SHEndian.h"
|
|
|
|
//===========================================================================
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Some platforms require strict-alignment, which means that values of
|
|
// primitive types must be accessed at memory locations which are multiples
|
|
// of the size of those types. For instance, a 'long' can only be accessed
|
|
// at a memory location which is a multiple of four. Consequently, the
|
|
// following endian-conversion functions first copy the raw data into a
|
|
// variable of the proper data type using memcpy() prior to attempting to
|
|
// access it as the given type.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static inline ushort us_endian (const byte* ptr)
|
|
{
|
|
short n;
|
|
memcpy(&n, ptr, sizeof(n));
|
|
return convert_endian(n);
|
|
}
|
|
|
|
static inline unsigned long ul_endian (const byte* ptr)
|
|
{
|
|
long n;
|
|
memcpy(&n, ptr, sizeof(n));
|
|
return convert_endian(n);
|
|
}
|
|
|
|
static inline long l_endian (const byte* ptr)
|
|
{
|
|
long n;
|
|
memcpy(&n, ptr, sizeof(n));
|
|
return convert_endian(n);
|
|
}
|
|
|
|
#define BFTYPE(x) us_endian((x) + 0)
|
|
#define BFSIZE(x) ul_endian((x) + 2)
|
|
#define BFOFFBITS(x) ul_endian((x) + 10)
|
|
#define BISIZE(x) ul_endian((x) + 14)
|
|
#define BIWIDTH(x) l_endian ((x) + 18)
|
|
#define BIHEIGHT(x) l_endian ((x) + 22)
|
|
#define BITCOUNT(x) us_endian((x) + 28)
|
|
#define BICOMP(x) ul_endian((x) + 30)
|
|
#define IMAGESIZE(x) ul_endian((x) + 34)
|
|
#define BICLRUSED(x) ul_endian((x) + 46)
|
|
#define BICLRIMP(x) ul_endian((x) + 50)
|
|
#define BIPALETTE(x) ((x) + 54)
|
|
|
|
// Type ID
|
|
#define BM "BM" // Windows 3.1x, 95, NT, ...
|
|
#define BA "BA" // OS/2 Bitmap Array
|
|
#define CI "CI" // OS/2 Color Icon
|
|
#define CP "CP" // OS/2 Color Pointer
|
|
#define IC "IC" // OS/2 Icon
|
|
#define PT "PT" // OS/2 Pointer
|
|
|
|
// Possible values for the header size
|
|
#define WinHSize 0x28
|
|
#define OS21xHSize 0x0C
|
|
#define OS22xHSize 0xF0
|
|
|
|
// Possible values for the BPP setting
|
|
#define Mono 1 // Monochrome bitmap
|
|
#define _16Color 4 // 16 color bitmap
|
|
#define _256Color 8 // 256 color bitmap
|
|
#define HIGHCOLOR 16 // 16bit (high color) bitmap
|
|
#define TRUECOLOR24 24 // 24bit (true color) bitmap
|
|
#define TRUECOLOR32 32 // 32bit (true color) bitmap
|
|
|
|
// Compression Types
|
|
#ifndef BI_RGB
|
|
#define BI_RGB 0 // none
|
|
#define BI_RLE8 1 // RLE 8-bit / pixel
|
|
#define BI_RLE4 2 // RLE 4-bit / pixel
|
|
#define BI_BITFIELDS 3 // Bitfields
|
|
#endif
|
|
|
|
//===========================================================================
|
|
|
|
void CImageBmpFile::mfLoadWindowsBitmap (byte* iBuffer, long iSize)
|
|
{
|
|
mfSet_dimensions (BIWIDTH(iBuffer), BIHEIGHT(iBuffer));
|
|
const int bmp_size = m_Width * m_Height;
|
|
m_eFormat = eIF_Bmp;
|
|
|
|
byte *iPtr = iBuffer + BFOFFBITS(iBuffer);
|
|
|
|
// The last scanline in BMP corresponds to the top line in the image
|
|
int buffer_y = m_Width * (m_Height - 1);
|
|
bool blip = false;
|
|
|
|
if (BITCOUNT(iBuffer) == _256Color && BICLRUSED(iBuffer))
|
|
{
|
|
mfSet_ImageSize(m_Width * m_Height);
|
|
byte *buffer = mfGet_image();
|
|
m_pPal = new SRGBPixel [256];
|
|
SRGBPixel *pwork = m_pPal;
|
|
byte *inpal = BIPALETTE(iBuffer);
|
|
mfSet_bps(8);
|
|
|
|
for (int color=0; color<256; color++, pwork++)
|
|
{
|
|
// Whacky BMP palette is in BGR order.
|
|
pwork->blue = *inpal++;
|
|
pwork->green = *inpal++;
|
|
pwork->red = *inpal++;
|
|
pwork->alpha = 255;
|
|
inpal++; // Skip unused byte.
|
|
}
|
|
|
|
if (BICOMP(iBuffer) == BI_RGB)
|
|
{
|
|
// Read the pixels from "top" to "bottom"
|
|
while (iPtr < iBuffer + iSize && buffer_y >= 0)
|
|
{
|
|
memcpy (buffer + buffer_y, iPtr, m_Width);
|
|
iPtr += m_Width;
|
|
buffer_y -= m_Width;
|
|
} /* endwhile */
|
|
}
|
|
else
|
|
if (BICOMP(iBuffer) == BI_RLE8)
|
|
{
|
|
// Decompress pixel data
|
|
byte rl, rl1, i; // runlength
|
|
byte clridx, clridx1; // colorindex
|
|
int buffer_x = 0;
|
|
while (iPtr < iBuffer + iSize && buffer_y >= 0)
|
|
{
|
|
rl = rl1 = *iPtr++;
|
|
clridx = clridx1 = *iPtr++;
|
|
if (rl == 0)
|
|
if (clridx == 0)
|
|
{
|
|
// new scanline
|
|
if (!blip)
|
|
{
|
|
// if we didnt already jumped to the new line, do it now
|
|
buffer_x = 0;
|
|
buffer_y -= m_Width;
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
if (clridx == 1)
|
|
// end of bitmap
|
|
break;
|
|
else
|
|
if (clridx == 2)
|
|
{
|
|
// next 2 bytes mean column- and scanline- offset
|
|
buffer_x += *iPtr++;
|
|
buffer_y -= (m_Width * (*iPtr++));
|
|
continue;
|
|
}
|
|
else
|
|
if (clridx > 2)
|
|
rl1 = clridx;
|
|
|
|
for ( i = 0; i < rl1; i++ )
|
|
{
|
|
if (!rl)
|
|
clridx1 = *iPtr++;
|
|
buffer [buffer_y + buffer_x] = clridx1;
|
|
|
|
if (++buffer_x >= m_Width)
|
|
{
|
|
buffer_x = 0;
|
|
buffer_y -= m_Width;
|
|
blip = true;
|
|
}
|
|
else
|
|
blip = false;
|
|
}
|
|
// pad in case rl == 0 and clridx in [3..255]
|
|
if (rl == 0 && (clridx & 0x01))
|
|
iPtr++;
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
if (!BICLRUSED(iBuffer) && BITCOUNT(iBuffer) == TRUECOLOR24)
|
|
{
|
|
mfSet_ImageSize(m_Width * m_Height * 4);
|
|
mfSet_bps (24);
|
|
SRGBPixel *buffer = (SRGBPixel *)mfGet_image();
|
|
|
|
while (iPtr < iBuffer + iSize && buffer_y >= 0)
|
|
{
|
|
SRGBPixel *d = buffer + buffer_y;
|
|
for (int x = m_Width; x; x--)
|
|
{
|
|
d->blue = *iPtr++;
|
|
d->green = *iPtr++;
|
|
d->red = *iPtr++;
|
|
d->alpha = 255;
|
|
d++;
|
|
} /* endfor */
|
|
|
|
buffer_y -= m_Width;
|
|
}
|
|
return;
|
|
}
|
|
else
|
|
if (!BICLRUSED(iBuffer) && BITCOUNT(iBuffer) == TRUECOLOR32)
|
|
{
|
|
mfSet_ImageSize(m_Width * m_Height * 4);
|
|
mfSet_bps (32);
|
|
SRGBPixel *buffer = (SRGBPixel *)mfGet_image();
|
|
|
|
while (iPtr < iBuffer + iSize && buffer_y >= 0)
|
|
{
|
|
SRGBPixel *d = buffer + buffer_y;
|
|
for (int x = m_Width; x; x--)
|
|
{
|
|
d->blue = *iPtr++;
|
|
d->green = *iPtr++;
|
|
d->red = *iPtr++;
|
|
d->alpha = *iPtr++;
|
|
d++;
|
|
} /* endfor */
|
|
|
|
buffer_y -= m_Width;
|
|
}
|
|
return;
|
|
}
|
|
|
|
mfSet_error (eIFE_BadFormat, "Unknown BMP image format");
|
|
|
|
return;
|
|
}
|
|
|
|
CImageBmpFile::CImageBmpFile (byte* ptr, long filesize) : CImageFile ()
|
|
{
|
|
if ((memcmp (ptr, BM, 2) == 0) && BISIZE(ptr) == WinHSize)
|
|
mfLoadWindowsBitmap (ptr, filesize);
|
|
else
|
|
mfSet_error (eIFE_BadFormat, "Not a Windows BMP file");
|
|
return;
|
|
}
|
|
|
|
CImageBmpFile::~CImageBmpFile ()
|
|
{
|
|
}
|