⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 loadbmp.cpp

📁 游戏编程精华02-含有几十个游戏编程例子
💻 CPP
字号:
#include "priv_precompiled.h"

/*
Copyright 2001 Bruce Dawson, Cygnus Software
For more information, visit http://www.cygnus-software.com/papers/
or e-mail: comments@cygnus-software.com

This code may be redistributed as long as this notice is kept intact.
*/

#include "csbitmap.h"
#include "bmpdefs.h"
#include <fstream>
#include <vector>
using namespace std;

// Reads in the colour map, returns the number of bytes of colour map read, or -1 for error.
static short ReadColorMap(istream& inputfile, CSBitmap *DestBitmap, BITMAPINFOHEADER *infoheader)
{
	RGBQUAD ColorMap[256];

	/* Calculate the number of colours there should be in the colour map. */
	/* This will be in ClrUsed.  If ClrUsed is zero then the number of */
	/* colours is just 1 ^ bitsperpixel. */

	unsigned int ColorMapSize = infoheader->biClrUsed;
	if (infoheader->biClrUsed == 0)
		ColorMapSize = 1 << infoheader->biBitCount;
	if (ColorMapSize > 256)
		return -1;

	if (ColorMapSize)
	{
		if (!inputfile.read((char*)ColorMap, sizeof(ColorMap[0]) * ColorMapSize))
			return -1;
		// Set the palette in the bitmap, or something like that.
		DestBitmap->SetPalette(0, ColorMapSize, ColorMap);
	}
	return ColorMapSize * sizeof(ColorMap[0]);
}

static int DecompressToBitmap(CSBitmap *DestBitmap, const char *CompressedData, int DataLength)
{
	const char *DataEnd = CompressedData + DataLength;
	const char *NearEnd = DataEnd - 2;	// Handy.
	int y;
	for (y = 0; y < DestBitmap->GetHeight(); y++)
	{
		int x = 0;
		uint8_t *Output = DestBitmap->GetLinePtr(DestBitmap->GetHeight() - y - 1);
		while (x < DestBitmap->GetWidth())
		{
			uint8_t Input;
			// Make sure I have at least two bytes left.
			if (CompressedData > NearEnd)
			{
				assert(0);
				return 0;	// Not enough data left.
			}
			Input = *CompressedData++;
			if (Input == 0)
			{
				Input = *CompressedData++;
				switch (Input)
				{
					case 0:
						// End of line. I watch for this at the end of lines
						// and I have no interest in handling it in the middle
						// of lines.
						assert(0);
						return 0;
					case 1:
						// End of image. I watch for this at the end of the image
						// and I have no interest in handling it in the middle
						// of the image.
						assert(!"I don't handle this.");
						return 0;
					case 2:
						// Skip to new location! This is ill defined in on disk
						// bitmaps and I have not tested it. Therefore I will not
						// try to handle it.
						assert(!"I don't handle this!!!");
						return 0;
					default:
						if (x + Input > DestBitmap->GetWidth())
						{
							assert(0);
							return 0;
						}
						if (CompressedData + Input >= DataEnd)
						{
							assert(0);
							return 0;
						}
						memcpy(Output + x, CompressedData, Input);
						x += Input;
						CompressedData += Input;
						if ((Input & 1) != 0)
							CompressedData++;
						break;
				}
			}
			else
			{
				if (x + Input > DestBitmap->GetWidth())
				{
					assert(0);
					return 0;
				}
				memset(Output + x, *CompressedData++, Input);
				x += Input;
			}
		}
		if (CompressedData <= NearEnd && CompressedData[0] == 0 && CompressedData[1] == 0)
			CompressedData += 2;	// Skip over the end of line data.
		else
		{
			// Out of data or missing end of line stuff.
			// After complaining we will return a failure code - unless we just finished
			// the last line, in which case we'll cut them some slack.
			// PaintShopPro seems to save bitmaps like this.
			if (y != DestBitmap->GetHeight() - 1)
			{
				assert(0);
				return 0;
			}
		}
	}
	if (CompressedData <= NearEnd && CompressedData[0] == 0 && CompressedData[1] == 1)
		CompressedData += 2;
	else
	{
		// Out of data or missing end of bitmap stuff.
		assert(0);
		return 0;
	}
	assert(CompressedData == DataEnd);
	return DestBitmap->GetHeight();
}

static bool ReadBMPLine(istream& inputfile, CSBitmap *DestBitmap, int y)
{
	// We have to flip the y-coordinate because our bitmap class
	// puts line zero at the top of the screen, and the BMP format
	// on disk does the opposite.
	uint8_t *LinePtr = DestBitmap->GetLinePtr(DestBitmap->GetHeight() - 1 - y);
	unsigned int BytesPerLine = ((DestBitmap->GetWidth() * DestBitmap->GetChannels() + 3) & ~3);
	unsigned int UsedBytes = DestBitmap->GetWidth() * DestBitmap->GetChannels();
	unsigned int Padding = BytesPerLine - UsedBytes;
	// Read the precise number of bytes that the line requires into the bitmap.
	// Don't read the padding bytes, because the in memory alignment requirements
	// may vary - we don't want our reading code to depend on our bitmap class
	// implementation.
	if (!inputfile.read((char*)LinePtr, UsedBytes))
		return false;
	// Skip over any padding bytes.
	if (!inputfile.seekg(Padding, ios_base::cur))
		return false;
	return true;
}

static bool InternalLoadBMPFile(istream& inputfile, CSBitmap *DestBitmap)
{
	BITMAPFILEHEADER fileheader;
	BITMAPINFOHEADER infoheader;

	// Read the file header.
	if (!inputfile.read((char*)&fileheader, sizeof(fileheader)))
		return false;

	// Check the type field to make sure we have a BMP file.
	if (fileheader.bfType != ('M' * 256 + 'B'))
		return false;

	// Read the info header.
	if (!inputfile.read((char*)&infoheader, sizeof(infoheader)))
		return false;

	// Sanity check the info header.
	if (infoheader.biSize != sizeof(infoheader))
		return false;

	if (infoheader.biPlanes != 1)
		return false;

	// These are the only depths I'm interested in handling. 1 and 4 bits per pixel
	// are also legal, but too much hassle.
	if (infoheader.biBitCount != 8 && infoheader.biBitCount != 24 && infoheader.biBitCount != 32)
		return false;

	if (!DestBitmap->SetSize(infoheader.biWidth, infoheader.biHeight, infoheader.biBitCount / 8))
		return false;

	// Read the color map, if any.
	if (infoheader.biBitCount == 8 && ReadColorMap(inputfile, DestBitmap, &infoheader) <= 0)
	{
		// Illegal ColorMap value
		return false;
	}

	// Jump to the location where the bitmap data is stored.
	if (!inputfile.seekg(fileheader.bfOffBits, ios_base::beg))
		return false;

	// Read in the bitmap data.
	if (infoheader.biCompression == BI_RLE8)
	{
		vector<char> CompressedData;
		int NumLines;
		// We only support 8-bit RLE8 files - logically enough.
		if (infoheader.biBitCount != 8)
			return false;
		// Allocate enough space for all of the compressed data.
		CompressedData.resize(infoheader.biSizeImage);
		// Read all of the compressed data - this is much easier than constantly
		// worrying about fread() failures.
		if (!inputfile.read(&CompressedData[0], infoheader.biSizeImage))
			return false;
		// Decompress.
		NumLines = DecompressToBitmap(DestBitmap, &CompressedData[0], infoheader.biSizeImage);
		if (NumLines != DestBitmap->GetHeight())
			return false;
	}
	else if (infoheader.biCompression == BI_RGB)
	{
		int y;
		for (y = 0; y < infoheader.biHeight; y++)
			if (!ReadBMPLine(inputfile, DestBitmap, y))
				return false;
	}
	else
		return false;

	return true;
}

bool LoadBMPFile(const char* FileName, CSBitmap* DestBitmap)
{
	bool	Result = false;
	assert(FileName);
	assert(DestBitmap);
	ifstream	inputfile(FileName, ios_base::binary);

	if (inputfile)
		Result = InternalLoadBMPFile(inputfile, DestBitmap);
	if (!Result)
		DestBitmap->FreeBitmap();
	return Result;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -