forked from aya/aya
Initial commit
This commit is contained in:
843
engine/3d/src/GImage_bmp.cpp
Normal file
843
engine/3d/src/GImage_bmp.cpp
Normal file
@@ -0,0 +1,843 @@
|
||||
/**
|
||||
@file GImage_bmp.cpp
|
||||
@author Morgan McGuire, http://graphics.cs.williams.edu
|
||||
@created 2002-05-27
|
||||
@edited 2006-05-10
|
||||
*/
|
||||
#include "platform.hpp"
|
||||
#include "GImage.hpp"
|
||||
#include "BinaryOutput.hpp"
|
||||
|
||||
namespace G3D
|
||||
{
|
||||
|
||||
#ifndef G3D_WIN32
|
||||
/**
|
||||
This is used by the Windows bitmap I/O.
|
||||
*/
|
||||
static const int BI_RGB = 0;
|
||||
#endif
|
||||
|
||||
void GImage::encodeBMP(BinaryOutput& out) const
|
||||
{
|
||||
|
||||
debugAssert(m_channels == 1 || m_channels == 3);
|
||||
out.setEndian(G3D_LITTLE_ENDIAN);
|
||||
|
||||
uint8 red;
|
||||
uint8 green;
|
||||
uint8 blue;
|
||||
int pixelBufferSize = m_width * m_height * 3;
|
||||
int fileHeaderSize = 14;
|
||||
int infoHeaderSize = 40;
|
||||
int BMScanWidth;
|
||||
int BMPadding;
|
||||
|
||||
// First write the BITMAPFILEHEADER
|
||||
//
|
||||
// WORD bfType;
|
||||
// DWORD bfSize;
|
||||
// WORD bfReserved1;
|
||||
// WORD bfReserved2;
|
||||
// DWORD bfOffBits;
|
||||
|
||||
// Type
|
||||
out.writeUInt8('B');
|
||||
out.writeUInt8('M');
|
||||
|
||||
// File size
|
||||
out.writeUInt32(fileHeaderSize + infoHeaderSize + pixelBufferSize);
|
||||
|
||||
// Two reserved fields set to zero
|
||||
out.writeUInt16(0);
|
||||
out.writeUInt16(0);
|
||||
|
||||
// The offset, in bytes, from the BITMAPFILEHEADER structure
|
||||
// to the bitmap bits.
|
||||
out.writeUInt32(infoHeaderSize + fileHeaderSize);
|
||||
|
||||
// Now the BITMAPINFOHEADER
|
||||
//
|
||||
// DWORD biSize;
|
||||
// LONG biWidth;
|
||||
// LONG biHeight;
|
||||
// WORD biPlanes;
|
||||
// WORD biBitCount
|
||||
// DWORD biCompression;
|
||||
// DWORD biSizeImage;
|
||||
// LONG biXPelsPerMeter;
|
||||
// LONG biYPelsPerMeter;
|
||||
// DWORD biClrUsed;
|
||||
// DWORD biClrImportant;
|
||||
|
||||
// Size of the info header
|
||||
out.writeUInt32(infoHeaderSize);
|
||||
|
||||
// Width and height of the image
|
||||
out.writeUInt32(m_width);
|
||||
out.writeUInt32(m_height);
|
||||
|
||||
// Planes ("must be set to 1")
|
||||
out.writeUInt16(1);
|
||||
|
||||
// BitCount and CompressionType
|
||||
out.writeUInt16(24);
|
||||
out.writeUInt32(BI_RGB);
|
||||
|
||||
// Image size ("may be zero for BI_RGB bitmaps")
|
||||
out.writeUInt32(0);
|
||||
|
||||
// biXPelsPerMeter
|
||||
out.writeUInt32(0);
|
||||
// biYPelsPerMeter
|
||||
out.writeUInt32(0);
|
||||
|
||||
// biClrUsed
|
||||
out.writeUInt32(0);
|
||||
|
||||
// biClrImportant
|
||||
out.writeUInt32(0);
|
||||
|
||||
BMScanWidth = m_width * 3;
|
||||
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMPadding = 4 - (BMScanWidth & 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
BMPadding = 0;
|
||||
}
|
||||
|
||||
int hStart = m_height - 1;
|
||||
int hEnd = -1;
|
||||
int hDir = -1;
|
||||
int dest;
|
||||
|
||||
// Write the pixel data
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
dest = m_channels * h * m_width;
|
||||
for (int w = 0; w < m_width; ++w)
|
||||
{
|
||||
|
||||
if (m_channels == 3)
|
||||
{
|
||||
red = m_byte[dest];
|
||||
green = m_byte[dest + 1];
|
||||
blue = m_byte[dest + 2];
|
||||
}
|
||||
else
|
||||
{
|
||||
red = m_byte[dest];
|
||||
green = m_byte[dest];
|
||||
blue = m_byte[dest];
|
||||
}
|
||||
|
||||
out.writeUInt8(blue);
|
||||
out.writeUInt8(green);
|
||||
out.writeUInt8(red);
|
||||
|
||||
dest += m_channels;
|
||||
}
|
||||
|
||||
if (BMPadding > 0)
|
||||
{
|
||||
out.skip(BMPadding);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool isDataSizeValid(int height, int paddedWidth, int headerSize, int palleteSize, int inputLength)
|
||||
{
|
||||
int64 dataSize = height * paddedWidth + headerSize + palleteSize;
|
||||
return inputLength >= dataSize;
|
||||
}
|
||||
|
||||
void GImage::decodeBMP(BinaryInput& input)
|
||||
{
|
||||
|
||||
// The BMP decoding uses these flags.
|
||||
static const uint16 PICTURE_NONE = 0x0000;
|
||||
static const uint16 PICTURE_BITMAP = 0x1000;
|
||||
|
||||
// Compression Flags
|
||||
static const uint16 PICTURE_UNCOMPRESSED = 0x0100;
|
||||
static const uint16 PICTURE_MONOCHROME = 0x0001;
|
||||
static const uint16 PICTURE_4BIT = 0x0002;
|
||||
static const uint16 PICTURE_8BIT = 0x0004;
|
||||
static const uint16 PICTURE_16BIT = 0x0008;
|
||||
static const uint16 PICTURE_24BIT = 0x0010;
|
||||
static const uint16 PICTURE_32BIT = 0x0020;
|
||||
|
||||
static const uint16 BMP_HEADERSIZE = 14;
|
||||
|
||||
(void)PICTURE_16BIT;
|
||||
(void)PICTURE_32BIT;
|
||||
|
||||
// This is a simple BMP loader that can handle uncompressed BMP files.
|
||||
|
||||
input.reset();
|
||||
|
||||
if (input.getLength() < BMP_HEADERSIZE)
|
||||
throw Error("BMB header corrupted - incomplete file");
|
||||
|
||||
// Verify this is a BMP file by looking for the BM tag.
|
||||
std::string tag = input.readString(2);
|
||||
if (tag != "BM")
|
||||
{
|
||||
throw Error("Not a supported BMP file");
|
||||
}
|
||||
|
||||
m_channels = 3;
|
||||
// Skip to the BITMAPINFOHEADER's width and height
|
||||
input.skip(12);
|
||||
|
||||
int32 BMPInfoHeaderSize = input.readUInt32();
|
||||
int32 wholeHeaderSize = BMP_HEADERSIZE + BMPInfoHeaderSize;
|
||||
|
||||
if (input.getLength() < wholeHeaderSize)
|
||||
throw Error("BMP data header corrupted - incomplete file");
|
||||
|
||||
m_width = input.readUInt32();
|
||||
m_height = input.readUInt32();
|
||||
|
||||
// Skip to the bit count and compression type
|
||||
input.skip(2);
|
||||
|
||||
uint16 bitCount = input.readUInt16();
|
||||
uint32 compressionType = input.readUInt32();
|
||||
|
||||
uint8 red;
|
||||
uint8 green;
|
||||
uint8 blue;
|
||||
uint8 blank;
|
||||
|
||||
// Only uncompressed bitmaps are supported by this code
|
||||
if ((int32)compressionType != BI_RGB)
|
||||
{
|
||||
throw Error("BMP images must be uncompressed");
|
||||
}
|
||||
|
||||
uint8* palette = NULL;
|
||||
|
||||
uint32 paletteDataSize = 0;
|
||||
// Create the palette if needed
|
||||
if (bitCount <= 8)
|
||||
{
|
||||
|
||||
// Skip to the palette color count in the header
|
||||
input.skip(12);
|
||||
|
||||
int numColors = input.readUInt32();
|
||||
|
||||
// if numColor == 0 its full size pallete
|
||||
if (numColors == 0)
|
||||
switch (bitCount)
|
||||
{
|
||||
case 1:
|
||||
numColors = 2;
|
||||
break;
|
||||
case 4:
|
||||
numColors = 16;
|
||||
break;
|
||||
case 8:
|
||||
numColors = 256;
|
||||
break;
|
||||
|
||||
default:
|
||||
numColors = 0;
|
||||
break; // well, we know nothing about this crap. Its gonna fail lower in a code
|
||||
}
|
||||
|
||||
paletteDataSize = numColors * 4;
|
||||
palette = (uint8*)System::malloc(numColors * 3);
|
||||
debugAssert(palette);
|
||||
|
||||
if (input.getLength() < wholeHeaderSize + paletteDataSize)
|
||||
throw Error("BMB palette corruption - incomplete file");
|
||||
|
||||
// Skip past the end of the header to the palette info
|
||||
input.skip(4);
|
||||
|
||||
int c;
|
||||
for (c = 0; c < numColors * 3; c += 3)
|
||||
{
|
||||
// Palette information in bitmaps is stored in BGR_ format.
|
||||
// That means it's blue-green-red-blank, for each entry.
|
||||
blue = input.readUInt8();
|
||||
green = input.readUInt8();
|
||||
red = input.readUInt8();
|
||||
blank = input.readUInt8();
|
||||
|
||||
palette[c] = red;
|
||||
palette[c + 1] = green;
|
||||
palette[c + 2] = blue;
|
||||
}
|
||||
}
|
||||
|
||||
int hStart = 0;
|
||||
int hEnd = 0;
|
||||
int hDir = 0;
|
||||
|
||||
if (m_height < 0)
|
||||
{
|
||||
m_height = -m_height;
|
||||
hStart = 0;
|
||||
hEnd = m_height;
|
||||
hDir = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// height = height;
|
||||
hStart = m_height - 1;
|
||||
hEnd = -1;
|
||||
hDir = -1;
|
||||
}
|
||||
|
||||
uint32 hSize = std::abs(hStart - hEnd);
|
||||
|
||||
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * 3);
|
||||
debugAssert(m_byte);
|
||||
|
||||
int BMScanWidth;
|
||||
int BMPadding;
|
||||
uint8 BMGroup;
|
||||
uint8 BMPixel8;
|
||||
int currPixel;
|
||||
int dest;
|
||||
int flags = PICTURE_NONE;
|
||||
|
||||
if (bitCount == 1)
|
||||
{
|
||||
// Note that this file is not necessarily grayscale, since it's possible
|
||||
// the palette is blue-and-white, or whatever. But of course most image
|
||||
// programs only write 1-bit images if they're black-and-white.
|
||||
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_MONOCHROME;
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
BMScanWidth = (m_width + 7) >> 3;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
if (!isDataSizeValid(hSize, BMScanWidth, wholeHeaderSize, paletteDataSize, input.getLength()))
|
||||
throw Error("BMP file corrupted");
|
||||
|
||||
// Powers of 2
|
||||
int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
dest = 3 * h * m_width;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; ++w)
|
||||
{
|
||||
|
||||
BMGroup = input.readUInt8();
|
||||
|
||||
// Now we read the pixels. Usually there are eight pixels per byte,
|
||||
// since each pixel is represented by one bit, but if the width
|
||||
// is not a multiple of eight, the last byte will have some bits
|
||||
// set, with the others just being extra. Plus there's the
|
||||
// dword-alignment padding. So we keep checking to see if we've
|
||||
// already read "width" number of pixels.
|
||||
for (int i = 7; i >= 0; --i)
|
||||
{
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
int src = 3 * ((BMGroup & pow2[i]) >> i);
|
||||
|
||||
m_byte[dest] = palette[src];
|
||||
m_byte[dest + 1] = palette[src + 1];
|
||||
m_byte[dest + 2] = palette[src + 2];
|
||||
|
||||
++currPixel;
|
||||
dest += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitCount == 4)
|
||||
{
|
||||
|
||||
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_4BIT;
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
int BMScanWidth = (m_width + 1) >> 1;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
if (!isDataSizeValid(hSize, BMScanWidth, wholeHeaderSize, paletteDataSize, input.getLength()))
|
||||
throw Error("BMP file corrupted");
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
dest = 3 * h * m_width;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; w++)
|
||||
{
|
||||
|
||||
BMGroup = input.readUInt8();
|
||||
int src[2];
|
||||
src[0] = 3 * ((BMGroup & 0xF0) >> 4);
|
||||
src[1] = 3 * (BMGroup & 0x0F);
|
||||
|
||||
// Now we read the pixels. Usually there are two pixels per byte,
|
||||
// since each pixel is represented by four bits, but if the width
|
||||
// is not a multiple of two, the last byte will have only four bits
|
||||
// set, with the others just being extra. Plus there's the
|
||||
// dword-alignment padding. So we keep checking to see if we've
|
||||
// already read "Width" number of pixels.
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
int tsrc = src[i];
|
||||
|
||||
m_byte[dest] = palette[tsrc];
|
||||
m_byte[dest + 1] = palette[tsrc + 1];
|
||||
m_byte[dest + 2] = palette[tsrc + 2];
|
||||
|
||||
++currPixel;
|
||||
dest += 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitCount == 8)
|
||||
{
|
||||
|
||||
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_8BIT;
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
BMScanWidth = m_width;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
if (!isDataSizeValid(hSize, BMScanWidth, wholeHeaderSize, paletteDataSize, input.getLength()))
|
||||
throw Error("BMP file corrupted");
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; ++w)
|
||||
{
|
||||
|
||||
BMPixel8 = input.readUInt8();
|
||||
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
dest = 3 * ((h * m_width) + currPixel);
|
||||
int src = 3 * BMPixel8;
|
||||
|
||||
m_byte[dest] = palette[src];
|
||||
m_byte[dest + 1] = palette[src + 1];
|
||||
m_byte[dest + 2] = palette[src + 2];
|
||||
|
||||
++currPixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitCount == 16)
|
||||
{
|
||||
|
||||
m_memMan->free(m_byte);
|
||||
m_byte = NULL;
|
||||
System::free(palette);
|
||||
palette = NULL;
|
||||
throw Error("16-bit bitmaps not supported");
|
||||
}
|
||||
else if (bitCount == 24)
|
||||
{
|
||||
input.skip(20);
|
||||
|
||||
flags = PICTURE_BITMAP | PICTURE_UNCOMPRESSED | PICTURE_24BIT;
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
BMScanWidth = m_width * 3;
|
||||
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMPadding = 4 - (BMScanWidth & 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
BMPadding = 0;
|
||||
}
|
||||
|
||||
if (!isDataSizeValid(hSize, BMScanWidth + BMPadding, wholeHeaderSize, paletteDataSize, input.getLength()))
|
||||
throw Error("BMP file corrupted");
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
dest = 3 * h * m_width;
|
||||
for (int w = 0; w < m_width; ++w)
|
||||
{
|
||||
|
||||
blue = input.readUInt8();
|
||||
green = input.readUInt8();
|
||||
red = input.readUInt8();
|
||||
|
||||
m_byte[dest] = red;
|
||||
m_byte[dest + 1] = green;
|
||||
m_byte[dest + 2] = blue;
|
||||
|
||||
dest += 3;
|
||||
}
|
||||
|
||||
if (BMPadding)
|
||||
{
|
||||
input.skip(BMPadding);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitCount == 32)
|
||||
{
|
||||
|
||||
m_memMan->free(m_byte);
|
||||
m_byte = NULL;
|
||||
System::free(palette);
|
||||
palette = NULL;
|
||||
throw Error("32 bit bitmaps not supported");
|
||||
}
|
||||
else
|
||||
{
|
||||
// We support all possible bit depths, so if the
|
||||
// code gets here, it's not even a real bitmap.
|
||||
m_memMan->free(m_byte);
|
||||
m_byte = NULL;
|
||||
throw Error("Not a bitmap!");
|
||||
}
|
||||
|
||||
System::free(palette);
|
||||
palette = NULL;
|
||||
}
|
||||
|
||||
|
||||
void GImage::decodeICO(BinaryInput& input)
|
||||
{
|
||||
|
||||
// Header
|
||||
uint16 r = input.readUInt16();
|
||||
debugAssert(r == 0);
|
||||
r = input.readUInt16();
|
||||
debugAssert(r == 1);
|
||||
|
||||
// Read the number of icons, although we'll only load the
|
||||
// first one.
|
||||
int count = input.readUInt16();
|
||||
|
||||
m_channels = 4;
|
||||
|
||||
debugAssert(count > 0);
|
||||
|
||||
const uint8* headerBuffer = input.getCArray() + input.getPosition();
|
||||
int maxWidth = 0, maxHeight = 0;
|
||||
int maxHeaderNum = 0;
|
||||
for (int currentHeader = 0; currentHeader < count; ++currentHeader)
|
||||
{
|
||||
|
||||
const uint8* curHeaderBuffer = headerBuffer + (currentHeader * 16);
|
||||
int tmpWidth = curHeaderBuffer[0];
|
||||
int tmpHeight = curHeaderBuffer[1];
|
||||
// Just in case there is a non-square icon, checking area
|
||||
if ((tmpWidth * tmpHeight) > (maxWidth * maxHeight))
|
||||
{
|
||||
maxWidth = tmpWidth;
|
||||
maxHeight = tmpHeight;
|
||||
maxHeaderNum = currentHeader;
|
||||
}
|
||||
}
|
||||
|
||||
input.skip(maxHeaderNum * 16);
|
||||
|
||||
m_width = input.readUInt8();
|
||||
m_height = input.readUInt8();
|
||||
int numColors = input.readUInt8();
|
||||
|
||||
m_byte = (uint8*)m_memMan->alloc(m_width * m_height * m_channels);
|
||||
debugAssert(m_byte);
|
||||
|
||||
// Bit mask for packed bits
|
||||
int mask = 0;
|
||||
|
||||
int bitsPerPixel = 8;
|
||||
|
||||
switch (numColors)
|
||||
{
|
||||
case 2:
|
||||
mask = 0x01;
|
||||
bitsPerPixel = 1;
|
||||
break;
|
||||
|
||||
case 16:
|
||||
mask = 0x0F;
|
||||
bitsPerPixel = 4;
|
||||
break;
|
||||
|
||||
case 0:
|
||||
numColors = 256;
|
||||
mask = 0xFF;
|
||||
bitsPerPixel = 8;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw Error("Unsupported ICO color count.");
|
||||
}
|
||||
|
||||
input.skip(5);
|
||||
// Skip 'size' unused
|
||||
input.skip(4);
|
||||
|
||||
int offset = input.readUInt32();
|
||||
|
||||
// Skip over any other icon descriptions
|
||||
input.setPosition(offset);
|
||||
|
||||
// Skip over bitmap header; it is redundant
|
||||
input.skip(40);
|
||||
|
||||
Array<Color4uint8> palette;
|
||||
palette.resize(numColors, true);
|
||||
for (int c = 0; c < numColors; ++c)
|
||||
{
|
||||
palette[c].b = input.readUInt8();
|
||||
palette[c].g = input.readUInt8();
|
||||
palette[c].r = input.readUInt8();
|
||||
palette[c].a = input.readUInt8();
|
||||
}
|
||||
|
||||
// The actual image and mask follow
|
||||
|
||||
// The XOR Bitmap is stored as 1-bit, 4-bit or 8-bit uncompressed Bitmap
|
||||
// using the same encoding as BMP files. The AND Bitmap is stored in as
|
||||
// 1-bit uncompressed Bitmap.
|
||||
//
|
||||
// Pixels are stored bottom-up, left-to-right. Pixel lines are padded
|
||||
// with zeros to end on a 32bit (4byte) boundary. Every line will have the
|
||||
// same number of bytes. Color indices are zero based, meaning a pixel color
|
||||
// of 0 represents the first color table entry, a pixel color of 255 (if there
|
||||
// are that many) represents the 256th entry.
|
||||
/*
|
||||
int bitsPerRow = width * bitsPerPixel;
|
||||
int bytesPerRow = iCeil((double)bitsPerRow / 8);
|
||||
// Rows are padded to 32-bit boundaries
|
||||
bytesPerRow += bytesPerRow % 4;
|
||||
|
||||
// Read the XOR values into the color channel
|
||||
for (int y = height - 1; y >= 0; --y) {
|
||||
int x = 0;
|
||||
// Read the row
|
||||
for (int i = 0; i < bytesPerRow; ++i) {
|
||||
uint8 byte = input.readUInt8();
|
||||
for (int j = 0; (j < 8) && (x < width); ++x, j += bitsPerPixel) {
|
||||
int bit = ((byte << j) >> (8 - bitsPerPixel)) & mask;
|
||||
pixel4(x, y) = colorTable[bit];
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
int hStart = 0;
|
||||
int hEnd = 0;
|
||||
int hDir = 0;
|
||||
|
||||
if (m_height < 0)
|
||||
{
|
||||
m_height = -m_height;
|
||||
hStart = 0;
|
||||
hEnd = m_height;
|
||||
hDir = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// height = height;
|
||||
hStart = m_height - 1;
|
||||
hEnd = -1;
|
||||
hDir = -1;
|
||||
}
|
||||
|
||||
int BMScanWidth;
|
||||
uint8 BMGroup;
|
||||
uint8 BMPixel8;
|
||||
int currPixel;
|
||||
int dest;
|
||||
|
||||
if (bitsPerPixel == 1)
|
||||
{
|
||||
// Note that this file is not necessarily grayscale, since it's possible
|
||||
// the palette is blue-and-white, or whatever. But of course most image
|
||||
// programs only write 1-bit images if they're black-and-white.
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
BMScanWidth = (m_width + 7) >> 3;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
// Powers of 2
|
||||
int pow2[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
dest = 3 * h * m_width;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; ++w)
|
||||
{
|
||||
|
||||
BMGroup = input.readUInt8();
|
||||
|
||||
// Now we read the pixels. Usually there are eight pixels per byte,
|
||||
// since each pixel is represented by one bit, but if the width
|
||||
// is not a multiple of eight, the last byte will have some bits
|
||||
// set, with the others just being extra. Plus there's the
|
||||
// dword-alignment padding. So we keep checking to see if we've
|
||||
// already read "width" number of pixels.
|
||||
for (int i = 7; i >= 0; --i)
|
||||
{
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
int src = ((BMGroup & pow2[i]) >> i);
|
||||
|
||||
m_byte[dest] = palette[src].r;
|
||||
m_byte[dest + 1] = palette[src].g;
|
||||
m_byte[dest + 2] = palette[src].b;
|
||||
|
||||
++currPixel;
|
||||
dest += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitsPerPixel == 4)
|
||||
{
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
int BMScanWidth = (m_width + 1) >> 1;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
dest = 4 * h * m_width;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; w++)
|
||||
{
|
||||
|
||||
BMGroup = input.readUInt8();
|
||||
int src[2];
|
||||
src[0] = ((BMGroup & 0xF0) >> 4);
|
||||
src[1] = (BMGroup & 0x0F);
|
||||
|
||||
// Now we read the pixels. Usually there are two pixels per byte,
|
||||
// since each pixel is represented by four bits, but if the width
|
||||
// is not a multiple of two, the last byte will have only four bits
|
||||
// set, with the others just being extra. Plus there's the
|
||||
// dword-alignment padding. So we keep checking to see if we've
|
||||
// already read "Width" number of pixels.
|
||||
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
int tsrc = src[i];
|
||||
|
||||
m_byte[dest] = palette[tsrc].r;
|
||||
m_byte[dest + 1] = palette[tsrc].g;
|
||||
m_byte[dest + 2] = palette[tsrc].b;
|
||||
|
||||
++currPixel;
|
||||
dest += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bitsPerPixel == 8)
|
||||
{
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
BMScanWidth = m_width;
|
||||
if (BMScanWidth & 3)
|
||||
{
|
||||
BMScanWidth += 4 - (BMScanWidth & 3);
|
||||
}
|
||||
|
||||
for (int h = hStart; h != hEnd; h += hDir)
|
||||
{
|
||||
|
||||
currPixel = 0;
|
||||
|
||||
for (int w = 0; w < BMScanWidth; ++w)
|
||||
{
|
||||
|
||||
BMPixel8 = input.readUInt8();
|
||||
|
||||
if (currPixel < m_width)
|
||||
{
|
||||
dest = 4 * ((h * m_width) + currPixel);
|
||||
int src = BMPixel8;
|
||||
|
||||
m_byte[dest] = palette[src].r;
|
||||
m_byte[dest + 1] = palette[src].g;
|
||||
m_byte[dest + 2] = palette[src].b;
|
||||
|
||||
++currPixel;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Read the mask into the alpha channel
|
||||
int bitsPerRow = m_width;
|
||||
int bytesPerRow = iCeil((double)bitsPerRow / 8);
|
||||
|
||||
// For bitmaps, each scanline is dword-aligned.
|
||||
// BMScanWidth = (width + 1) >> 1;
|
||||
if (bytesPerRow & 3)
|
||||
{
|
||||
bytesPerRow += 4 - (bytesPerRow & 3);
|
||||
}
|
||||
|
||||
for (int y = m_height - 1; y >= 0; --y)
|
||||
{
|
||||
int x = 0;
|
||||
// Read the row
|
||||
for (int i = 0; i < bytesPerRow; ++i)
|
||||
{
|
||||
uint8 byte = input.readUInt8();
|
||||
for (int j = 0; (j < 8) && (x < m_width); ++x, ++j)
|
||||
{
|
||||
int bit = (byte >> (7 - j)) & 0x01;
|
||||
pixel4(x, y).a = (1 - bit) * 0xFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace G3D
|
||||
Reference in New Issue
Block a user