📄 bmpdecoder.cpp
字号:
//
// Copyright (c) 1997,1998, 1999 Colosseum Builders, Inc.
// All rights reserved.
//
// Colosseum Builders, Inc. makes no warranty, expressed or implied
// with regards to this software. It is provided as is.
//
// See the README.TXT file that came with this software for restrictions
// on the use and redistribution of this file or send E-mail to
// info@colosseumbuilders.com
//
//
// BMP Decoder Library.
//
// Title: BmpDecoder Class Implementation
//
// Author: John M. Miano (miano@colosseumbuilders.com)
//
// Date: April 15, 1999
//
// Version: 3
//
// Description:
//
// Windows BMP Encoder
//
// Revision History:
//
#include <windows.h>
#include <fstream>
#include <climits>
#include "bmpdecoder.h"
#include "buffer.h"
using namespace std ;
namespace
{
//
// This function takes a contiguous bit mask and determines the
// offset and size of the bit mask.
//
void FindMask (UBYTE4 mask, UBYTE4 &offset, UBYTE4 &size)
{
offset = 0 ;
while ((mask & (1 << offset)) == 0 && offset < 32)
++ offset ;
size = 0 ;
while (offset + size < 32 && (mask & (1 << (offset + size))) != 0)
++ size ;
return ;
}
}
namespace Colosseum
{
//
// Description:
//
// Class default constructor
//
//
// Description:
//
// Default Constructor
//
BmpDecoder::BmpDecoder ()
{
return ;
}
//
// Description:
//
// Class Copy Constructor
//
BmpDecoder::BmpDecoder (const BmpDecoder &source)
: BitmapImageDecoder (source)
{
return ;
}
//
// Description:
//
// Class Destructor
//
BmpDecoder::~BmpDecoder ()
{
return ;
}
//
// Description:
//
// Assignment operator.
//
// Parameters:
// source: The object to copy
//
BmpDecoder &BmpDecoder::operator=(const BmpDecoder &source)
{
BitmapImageDecoder::operator=(source) ;
return *this ;
}
//
// Description:
//
// This function reads an image from a Windows BMP stream.
//
// Parameters:
// strm: The input stream
// image: The image to be read
//
void BmpDecoder::readImage (std::istream &strm, BitmapImage &image)
{
callProgressFunction (0) ;
// We need this because MSVC++ does not follow standard scoping
// rules in for statements.
unsigned int ii ;
unsigned int bytesread = 0 ;
BITMAPFILEHEADER fileheader ;
strm.read (reinterpret_cast<char *>(&fileheader), sizeof (fileheader)) ;
if (strm.gcount () != sizeof(fileheader))
throw BmpError ("Premature End of Stream") ;
bytesread += sizeof (fileheader) ;
const UBYTE2 signature = 'B' | ('M' << CHAR_BIT) ;
if (fileheader.bfType != signature)
throw BmpError ("Not a BMP Image") ;
// The header can come in one of two flavors. They both
// begin with a 4-byte headersize.
UBYTE4 headersize ;
strm.read (reinterpret_cast<char *>(&headersize), sizeof (headersize)) ;
if (strm.gcount () != sizeof(headersize))
throw BmpError ("Premature End of Stream") ;
headersize = LittleEndianToSystem (headersize) ;
Buffer<UBYTE1> headerbuffer (headersize) ;
strm.read (reinterpret_cast<char*>(&headerbuffer [sizeof (headersize)]),
headersize - sizeof (headersize)) ;
bytesread += headersize ;
unsigned long width ;
long height ;
unsigned int bitcount ;
unsigned int compression ;
color_table_size = 0 ;
if (headersize == sizeof (BITMAPCOREHEADER))
{
BITMAPCOREHEADER header = reinterpret_cast<BITMAPCOREHEADER&>(headerbuffer [0]) ;
width = LittleEndianToSystem (header.bcWidth) ;
height = LittleEndianToSystem (header.bcHeight) ;
bitcount = LittleEndianToSystem (header.bcBitCount) ;
color_table_size = 1 << bitcount ;
bytesread += readOs2ColorTable (strm, color_table_size) ;
compression = BI_RGB ;
}
else if (headersize >= sizeof (BITMAPINFOHEADER))
{
BITMAPINFOHEADER &header = reinterpret_cast<BITMAPINFOHEADER&>(headerbuffer [0]) ;
compression = LittleEndianToSystem (static_cast<UBYTE4>(header.biCompression)) ;
width = LittleEndianToSystem (static_cast<UBYTE4>(header.biWidth)) ;
height = LittleEndianToSystem (static_cast<UBYTE4>(header.biHeight)) ;
bitcount = LittleEndianToSystem (static_cast<UBYTE4>(header.biBitCount)) ;
color_table_size = LittleEndianToSystem (static_cast<UBYTE4>(header.biClrUsed)) ;
if (color_table_size == 0 && bitcount < 16)
{
// If the colors used field is non-zero, it gives the size of the
// color table. Otherwise, the number of bits per pixel gives the size.
color_table_size = 1 << bitcount ;
}
// Validate Compression
switch (bitcount)
{
case 1:
if (compression != BI_RGB)
throw BmpError ("Unsupported Compression") ;
break ;
case 4:
if (compression != BI_RGB && compression != BI_RLE4)
throw BmpError ("Unsupported Compression") ;
break ;
case 8:
if (compression != BI_RGB && compression != BI_RLE8)
throw BmpError ("Unsupported Compression") ;
break ;
case 24:
if (compression != BI_RGB)
throw BmpError ("Unsupported Compression") ;
break ;
case 16: case 32:
if (compression != BI_RGB && compression != BI_BITFIELDS)
throw BmpError ("Unsupported Compression") ;
break ;
default:
throw BmpError ("Unsupported bit count") ;
}
if (color_table_size != 0)
bytesread += readColorTable (strm, color_table_size) ;
}
else
{
throw BmpError ("Invalid header size") ;
}
// Allocate storage for the image.
if (height >= 0)
image.setSize (width, height) ;
else
image.setSize (width, -height) ;
// It is poss ible to have a file where the image data does not
// immediately follow the color map (or headers). If there is
// padding we skip over it.
if (bytesread > fileheader.bfOffBits)
throw BmpError ("Corrupt File") ;
for (ii = bytesread ; ii < fileheader.bfOffBits ; ++ ii)
{
UBYTE1 data ;
strm.read (reinterpret_cast<char *>(&data), sizeof (data)) ;
}
switch (bitcount)
{
case 1: case 2: case 4:
if (compression == BI_RGB)
readFractionalByteData (strm, bitcount, height, width, image) ;
else
readRle4 (strm, height, width, image) ;
break ;
case 8:
if (compression == BI_RGB)
readFractionalByteData (strm, bitcount, height, width, image) ;
else
readRle8 (strm, height, width, image) ;
break ;
case 16:
{
BITMAPV4HEADER &header = reinterpret_cast<BITMAPV4HEADER&>(headerbuffer [0]) ;
unsigned long redmask = LittleEndianToSystem (static_cast<UBYTE4>(header.bV4RedMask)) ;
if (redmask == 0)
redmask = 0x7C00U ;
unsigned long greenmask = LittleEndianToSystem (static_cast<UBYTE4>((header.bV4GreenMask))) ;
if (greenmask == 0)
greenmask = 0x3E0U ;
unsigned long bluemask = LittleEndianToSystem (static_cast<UBYTE4>(header.bV4BlueMask)) ;
if (bluemask == 0)
bluemask = 0x1FU ;
unsigned long alphamask = 0 ;
if (headersize >= sizeof (BITMAPV4HEADER))
alphamask = LittleEndianToSystem (static_cast<UBYTE4>(header.bV4AlphaMask)) ;
read16BitData (strm, height, width, image,
redmask, greenmask, bluemask, alphamask) ;
}
break ;
case 24:
read24BitData (strm, height, width, image) ;
break ;
case 32:
{
BITMAPV4HEADER &header = reinterpret_cast<BITMAPV4HEADER&>(headerbuffer [0]) ;
unsigned long redmask = LittleEndianToSystem (static_cast<UBYTE4>(header.bV4RedMask)) ;
if (redmask == 0)
redmask = 0xFF0000U ;
unsigned long greenmask = LittleEndianToSystem (static_cast<UBYTE4>(header.bV4GreenMask)) ;
if (greenmask == 0)
greenmask = 0xFF00U ;
unsigned long bluemask = LittleEndianToSystem (static_cast<UBYTE4>(header.bV4BlueMask)) ;
if (bluemask == 0)
bluemask = 0xFFU ;
unsigned long alphamask = 0 ;
if (headersize >= sizeof (BITMAPV4HEADER))
alphamask = LittleEndianToSystem (static_cast<UBYTE4>(header.bV4AlphaMask)) ;
read32BitData (strm, height, width, image,
redmask, greenmask, bluemask, alphamask) ;
}
break ;
}
callProgressFunction (100) ;
return ;
}
//
// Description:
//
// This function is used to call the progres function.
//
// Parameters:
// percent: The percent complete (0..100)
//
void BmpDecoder::callProgressFunction (unsigned int percent)
{
if (progress_function == 0)
return ;
bool cancel = false ;
progress_function (*this, progress_data, 1, 1, percent, cancel) ;
if (cancel)
throw GraphicsAbort () ;
return ;
}
void BmpDecoder::readImageFile (const std::string &filename, BitmapImage &image)
{
ifstream inputstream (filename.c_str (), ios::binary) ;
if (! inputstream)
throw BmpError ("Can't open input file") ;
readImage (inputstream, image) ;
return ;
}
void BmpDecoder::readFractionalByteData (std::istream &strm,
unsigned int bitcount,
int height, unsigned int width,
BitmapImage &image)
{
// Number of bits required for each pixel row.
unsigned int bitwidth = bitcount * width ;
// Number of bytes need to store each pixel row.
unsigned int rowwidth = (bitwidth + CHAR_BIT - 1)/CHAR_BIT ;
// Number of bytes used to store each row in the BMP file.
// This is is rowwidth rounded up to the nearest 4 bytes.
unsigned int physicalrowsize = (rowwidth + 0x3) & ~0x3 ;
Buffer<UBYTE1> buffer (physicalrowsize) ;
// Images with positive heights are stored bottom up. Images with
// negative height are stored top down.
if (height > 0)
{
for (unsigned int ii = 0 ; ii < height ; ++ ii)
{
callProgressFunction (ii * 100 / height) ;
strm.read (reinterpret_cast<char *>(&buffer [0]), physicalrowsize) ;
if (strm.gcount () != physicalrowsize)
throw BmpError ("Premature End of Stream") ;
// The pixel rows are stored in reverse order.
unsigned int index = (height - ii - 1) * width ;
BitmapImage::Pixel *pixel = &image [index] ;
for (unsigned int jj = 0 ; jj < width ; ++ jj, ++ pixel)
{
unsigned int color = (buffer [jj/(CHAR_BIT/bitcount)]
>> (bitcount * ((CHAR_BIT/bitcount-1) - jj % (CHAR_BIT/bitcount))))
& ((1<<bitcount) - 1) ;
pixel->red = color_table [color].rgbRed ;
pixel->green = color_table [color].rgbGreen ;
pixel->blue = color_table [color].rgbBlue ;
}
}
}
else
{
BitmapImage::Pixel *pixel = &image [0] ;
for (unsigned int ii = 0 ; ii < - height ; ++ ii)
{
callProgressFunction (ii * 100 / height) ;
strm.read (reinterpret_cast<char *>(&buffer [0]), physicalrowsize) ;
if (strm.gcount () != physicalrowsize)
throw BmpError ("Premature End of Stream") ;
for (unsigned int jj = 0 ; jj < width ; ++ jj, ++ pixel)
{
unsigned int color = (buffer [jj/(CHAR_BIT/bitcount)]
>> (bitcount * ((CHAR_BIT/bitcount-1) - jj % (CHAR_BIT/bitcount))))
& ((1<<bitcount) - 1) ;
pixel->red = color_table [color].rgbRed ;
pixel->green = color_table [color].rgbGreen ;
pixel->blue = color_table [color].rgbBlue ;
}
}
}
return ;
}
void BmpDecoder::read8BitData (std::istream &strm,
int height, int width,
BitmapImage &image)
{
// This is is rowwidth rounded up to the nearest 4 bytes.
unsigned int physicalrowsize = (width + 0x3) & ~0x3 ;
Buffer<UBYTE1> buffer (physicalrowsize) ;
// Images with positive heights are stored bottom up. Images with
// negative height are stored top down.
if (height > 0)
{
for (unsigned int ii = 0 ; ii < height ; ++ ii)
{
callProgressFunction (ii * 100 / height) ;
strm.read (reinterpret_cast<char *>(&buffer [0]), physicalrowsize) ;
if (strm.gcount () != physicalrowsize)
throw BmpError ("Premature End of Stream") ;
// The pixel rows are stored in reverse order.
unsigned int index = (height - ii - 1) * width ;
BitmapImage::Pixel *pixel = &image [index] ;
for (unsigned int jj = 0 ; jj < width ; ++ jj, ++ pixel)
{
unsigned int color = buffer [jj] ;
pixel->red = color_table [color].rgbRed ;
pixel->green = color_table [color].rgbGreen ;
pixel->blue = color_table [color].rgbBlue ;
}
}
}
else
{
BitmapImage::Pixel *pixel = &image [0] ;
for (unsigned int ii = 0 ; ii < - height ; ++ ii)
{
callProgressFunction (ii * 100 / height) ;
strm.read (reinterpret_cast<char *>(&buffer [0]), physicalrowsize) ;
if (strm.gcount () != physicalrowsize)
throw BmpError ("Premature End of Stream") ;
for (unsigned int jj = 0 ; jj < width ; ++ jj, ++ pixel)
{
unsigned int color = buffer [jj] ;
pixel->red = color_table [color].rgbRed ;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -