示例#1
0
// Loads a PCX file
bool CBitmap::loadPCX(const char *fileName)
{
	FILE *filePtr = fopen(fileName,"rb"); // Open the file
	BYTE *scanLine = NULL; // A pointer to contain a row of pixel data in the PCX

	SPCXHeader header = {0}; // Create a zeroed out SPCXHeader
	
	// We want to do robust error checking so we'll wrap the loading code in a 
	// try...catch block so we can deal with errors in only place
	try
	{
		// Error Check
		if(!filePtr)
			throw "Couldn't open .pcx file";
		
		// Read in the .pcx file header
		if(!fread(&header,sizeof(SPCXHeader),1,filePtr))
			throw "Couldn't read .pcx header";

		// Error Check -- manufacturer better equal 10 otherwise this 
		// for sure isn't a .pcx file
		if((int)header.manufacturer != 10)
			throw "Not a valid .pcx file";

		// Okay we have the header.  Let's determine the width, height, and number of channels
		// that are in the .pcx file.  Notice the +1 for width and height
		int width = header.xMax - header.xMin + 1;
		int height = header.yMax - header.yMin + 1; 
		int channels = header.planes;

		// Okay let's set the size of our CBitmap
		if(!setSize(width,height,channels))
			throw "Couldn't create bitmap for loading .pcx into";

		// Now we need to test to see if there is a palette
		// If the header version equals 5, there is a chance that there is a palette
		if((int)header.version == 5)
		{
			// Since it's a version 5 .pcx it's possible that it has a palette so
			// now we must test further
			
			BYTE test_byte; // byte to check to see if it's a 8-bit .pcx file

			// Seek to where the test byte is
			if(fseek(filePtr,-PCX_PALETTE_TEST,SEEK_END))
				throw "PCX file is corrupt";
			
			// Read in the test byte
			if(!fread(&test_byte,sizeof(BYTE),1,filePtr))
				throw "PCX file is corrupt";
			
			// Check the test byte -- If it EQUALS '12' we have a palette to read in
			if(test_byte == 12)
			{
				// Load the palette
				if(!loadPCXPalette(filePtr))
					throw "Couldn't load palette";
			}
		}
		
		// Next seek past the SPCXHeader
		if(fseek(filePtr,PCX_HEADER_SIZE,SEEK_SET))
			throw "PCX file is corrupt";		
		
		// The total number of bytes in a scanline
		int totalBytes = (int)header.planes * header.bytesPerLine;

		// Allocate the scanline
		scanLine = new BYTE[totalBytes];

		// Error Check
		if(scanLine == NULL)
			throw "Couldn't allocate memory";

		// Now loop over the entire .pcx file -- We'll fill each "scanLine" and then
		// convert that scanLine to our CBitmap depending on what type of .pcx we're dealing with
		for(int lineCount = 0; lineCount < height; lineCount++)
		{
			if(!readScanLine(filePtr, scanLine, totalBytes))
				throw "Error reading scanLine";

			// If it's an 8-bit .pcx file, simply copy over the scanline
			if((int)header.planes == 1) 
				memcpy(getLinePtr(lineCount),scanLine,getStride());

			else // Converge "scanLine" into the same form as a CBitmap 24-bit scanline
			{
				uchar *pixels = getLinePtr(lineCount);
				
				// Loop through the entire line of pixels.
				for(int i = 0; i < width; ++i)
				{
					/* 
						What does this mess do?  Well lets break down what each part is doing.
						
						i * header.planes - This puts "pixels" at the current pixel
						+ RED_BITMAP_OFFSET - Offset from the current pixel to the 'R' color component
						+ GREEN_BITMAP_OFFSET - Offset from the current pixel to the 'G' color component 
						+ BLUE_BITMAP_OFFSET - Offset from the current pixel to the 'B' color component
						
						PCX stores the scanline as a sequence of 'R', then a sequence of 'G', 
						then a sequence of 'B' so "i + header.bytesPerLine" moves to the sequence
						containing the 'G' components and "i + header.bytesPerLine * 2" moves to 
						the sequence containing the 'B' components
					*/				
					pixels[i * header.planes + RED_BITMAP_OFFSET] = scanLine[i];
					pixels[i * header.planes + GREEN_BITMAP_OFFSET] = scanLine[i + header.bytesPerLine];
					pixels[i * header.planes + BLUE_BITMAP_OFFSET] = scanLine[i + header.bytesPerLine * 2];
				}
			
			}  // end of if...else ((int)header.planes == 1)

		} // end of for(int lineCount = 0; lineCount < height; lineCount++)
	}
	catch (char *str) // If an error happened, catch it here and cleanup
	{
		MessageBox(NULL, str, "ERROR", MB_OK | MB_ICONERROR);
		
		if(filePtr)
			fclose(filePtr);
			
		if(scanLine)
			delete[] scanLine;
			
		return false;
	}

	delete[] scanLine; // Free mem
	fclose(filePtr); // Close the file
		return true; // PCX was successfully loaded
}
示例#2
0
// This is the function	that "manually" loads an 8-bit bitmap
// It will return true for success, false for failure
bool CBitmap::loadBMP(char *file_name, HDC hdc)
{
		// Make sure we got a legal file name passed in
		if(file_name == NULL)
			return false;

	// These structures will hold our bitmap file header (top of bitmap)
	// and our bitmap info header (directly underneath bitmap file header)
	BITMAPFILEHEADER bmp_fileheader = {0};
	BITMAPINFOHEADER bmp_infoheader = {0};

	FILE *bmpFile = fopen(file_name,"rb");

	bool status = false; // This the "status" of our attempt at loading the 8-bit bitmap
						 // When we have successful loaded it, we'll set this to true

		// Make sure we could open the file
		if(bmpFile == NULL)
			return false;

	// Read the file header -- Making sure the read is
	if(!fread(&bmp_fileheader, sizeof(BITMAPFILEHEADER), 1, bmpFile))
		goto CLEAN_UP; // Now normally "goto's" are frowned upon, but in this case
					   // it's a reasonable solution -- There are MANY things that could fail
					   // along the way, so instead of rewriting the code for each 
					   // possible failure, we'll just "goto" the "CLEAN_UP:" code and
					   // return our "status" flag of how the loading of the bitmap went :)

	// Check the type field to make sure we have a .bmp file -- This will always be "BM" for .bmps
	if(memcmp(&bmp_fileheader.bfType, "BM", 2))
		goto CLEAN_UP;

	// Read the info header -- You'll notice it's positioned directly after the 
	// BITMAPFILEHEADER.  That's roughly how a bitmap is laid out:  BITMAPFILEHEADER
	//																BITMAPINFOHEADER
	//																Pixel Data
	if(!fread(&bmp_infoheader, sizeof(BITMAPINFOHEADER), 1, bmpFile))
		goto CLEAN_UP;

	// It's always good to double check and make sure the fread() went as planned :)
	// Obviously, these two numbers better be equal or there was a problem
	if(bmp_infoheader.biSize != sizeof(BITMAPINFOHEADER))
		goto CLEAN_UP;

	// This had better equal one (MSDN says this will always equal one)
	if(bmp_infoheader.biPlanes != 1)
		goto CLEAN_UP;

	// If the bitmap isn't a 8-bit bitmap, WE CAN'T LOAD IT
	// .biBitCount represents the number of bits per pixel our bitmap is
	if(bmp_infoheader.biBitCount != 8)
		goto CLEAN_UP;	

	// Now we're creating an "empty" CBitmap for storing the 8-bit bitmap
	// If this fails we can't load the 8-bit bitmap (darn!)
	// You'll notice that the BITMAPINFOHEADER contains the width (biWidth) in pixels and
	// the height (biHeight) in pixels of the bitmap
	if(!setSize(hdc,bmp_infoheader.biWidth, bmp_infoheader.biHeight))
		goto CLEAN_UP;

/************* HERE WE'RE GOING TO READ THE COLOR MAP INFORMATION ***************/
	{
	
	RGBQUAD rgbQuad[256] = {0};

	// Let's determine the size of the color table (how many colors are used by the bitmap)
	uint tableSize = bmp_infoheader.biClrUsed;
	
	// If the size equals zero, this means the number of colors used by bitmap is equal
	// to the maximum number of colors allowed for this type of bitmap (this is dependent on the bits per pixel)
	// For 8-bit bitmaps, the maximum number of colors is 256
	if(tableSize == 0)
		tableSize = 256;

	// 8-bit bitmaps can't have more than 256 colors
	// Also 8-bit bitmaps can't have a negative number of colors
	// Make sure there isn't an error :)
	if((tableSize > 256) || (tableSize < 0))
		goto CLEAN_UP;

	// Okay we're ready to read the RGBQUAD (the color table (a.k.a. color map) )
	if(!fread(rgbQuad, sizeof(RGBQUAD) * tableSize, 1, bmpFile))
		goto CLEAN_UP;

	// Set the palette in the bitmap
	if(!setPalette(0,tableSize,rgbQuad))
		goto CLEAN_UP;
	
	} // If you noticed the {} it's because I wanted to make a "local scope" to separate
	  // the color table reading for clarity sake (also makes the compiler not puke)
	
/*************** DONE READING THE COLOR MAP INFORMATION *************************/

	// Jump to the location where the bitmap data is stored
	// bfOffBits is the number of bytes from the beginning of the file to where the bitmaps bits reside 
	if(fseek(bmpFile, bmp_fileheader.bfOffBits, SEEK_SET))
		goto CLEAN_UP;

	// Read in the bitmap is compressed (has been saved with Run Length Encoding)
	// We can't load it
	if(bmp_infoheader.biCompression == BI_RLE8)
		goto CLEAN_UP;

	else if(bmp_infoheader.biCompression == BI_RGB) // There is NO compression
	{
		/* Okay a little explaining is in order :)
		   The total number of bytes that make a scan line (this would be one line of pixel data)
		   is equal to: (width of bitmap * bitmap channels)

		   Now a bitmap's channels is defined as "the number of bytes used per pixel"
		   So an 8-bit bitmaps channel would equal 1, an 24-bit bitmap channel would equal 3  -- Remember (8 bits == 1 byte) :)

		   Sometime bitmaps have padding bytes, if we're reading in bitmap and it has 
		   padding bytes it's our responsibility to skip over 'em
		   You'll recall we calculate the "bmp_stride" in the set_size() method -- This can be 
		   thought us as "the actual number of bytes in scan line"
		   The bmp_width would be the "the actual number of bytes in a scan line that are pixel data"

		   This of course follows our formula (bmp_width * 1)
		*/

		uint padding = bmp_stride - bmp_width; // Calculate the number of 
											  // padding bytes if there is any
		
		// Okay now all we have to do is loop over all the pixel data
		// and store what we read in, in our CBitmap :)
		for(int y = 0; y < bmp_infoheader.biHeight; y++)
		{
			// We have to flip the y-coordinate because our CBitmap
			// puts line zero at the top of the screen, and the .bmp format
			// on disk puts the first bytes at the bottom of the screen.
			uchar *scanLine = getLinePtr(bmp_height - 1 - y);

			// Read the precise number of bytes that the line requires into the bitmap.
			// This will be the width of the bitmap,  DON'T read the padding bytes.
			// Remember memory alignment requirements may vary.
			// We want our 8-bit bitmap loader to be ROBUST %)
			if(!fread(scanLine, bmp_width, 1, bmpFile))
				goto CLEAN_UP; // In case you weren't aware, goto's will break out of for loops

			// Skip over the padding bytes (if there are any, if padding == 0, no harm done)
			if(fseek(bmpFile, padding, SEEK_CUR))
				goto CLEAN_UP;
		}

		// If we get here the bitmap WAS LOADED!!!!!!!!!!!!
		// So we better set status to true
		status = true;
	
	} // end of else if(bmp_infoheader.biCompression == BI_RGB) // There is NO compression

CLEAN_UP:
		
		fclose(bmpFile); // Close the file we opened
			return status;
}
示例#3
0
bool DIB_BITMAP::loadBMP(const char *file_name)
{
	// If DIB_BITMAP has already been set -- clear it out first
	FreeDIB_BMP();

		// Error Check -- Make sure they passed in a valid file name
		if(!file_name)
			return false;

	FILE *bmp_file = fopen(file_name, "rb");

		// Error Check -- Make sure the file could be opened
		if(!bmp_file)
			return false;

	BITMAPFILEHEADER bmp_fileheader;

	// Read the BITMAPFILEHEADER
	if(!fread(&bmp_fileheader, sizeof(BITMAPFILEHEADER), 1, bmp_file))
	{
		fclose(bmp_file);
			return false;
	}

	// Check the type field to make sure we have a .bmp file
	if(memcmp(&bmp_fileheader.bfType, "BM", 2))
	{
		fclose(bmp_file);
			return false;
	}

	BITMAPINFOHEADER bmp_infoheader;
	
	// Read the BITMAPINFOHEADER.
	if(!fread(&bmp_infoheader, sizeof(BITMAPINFOHEADER), 1, bmp_file))
	{
		fclose(bmp_file);
			return false;
	}

	// We only support 24-bit and 32-bit .bmps so make sure that's what we have
	if((bmp_infoheader.biBitCount != 24) && (bmp_infoheader.biBitCount != 32))
	{
		fclose(bmp_file);
			return false;
	}

	// Set the size of our DIB_BITMAP, once we do this we're ready to store the pixel
	// data in it
	if(set_size(bmp_infoheader.biWidth,bmp_infoheader.biHeight,bmp_infoheader.biBitCount / 8) == false)
	{
		fclose(bmp_file);
			return false;
	}

	// Jump to the location where the pixel data is stored
	if(fseek(bmp_file, bmp_fileheader.bfOffBits, SEEK_SET))
	{
		fclose(bmp_file);
			return false;
	}
	
	unsigned int bytesPerLine = bmp_width * bmp_channels; // Bytes per line (number of bytes
														 // in a scan line)
	
	// Calculate how many "padding" bytes there are -- WE DO NOT want to read in the
	// padding bytes (we will just skip over those)
	// **Remember** Windows adds padding bytes to ensure ALL .bmps are DWORD aligned
	// (divisible evenly by 4)
	unsigned int padding = bmp_stride - bytesPerLine;

		// Loop over all the scan lines (all the rows of pixels in the image)
		for(int y = bmp_height-1; y >= 0; y--)
		{
			// Get the "current" line pointer
			uchar *LinePtr = getLinePtr(y);

			// Read the precise number of bytes that the scan line requires into the bitmap
			if(!fread(LinePtr, bytesPerLine, 1, bmp_file))
			{
				fclose(bmp_file);
					return false;
			}

			// Skip over any padding bytes.
			if(fseek(bmp_file, padding, SEEK_CUR))
			{
				fclose(bmp_file);
					return false;
			}
	
		} // end of for (int y = 0; y < bmp_infoheader.biHeight; y++)


	fclose(bmp_file);
		return true; // If we get here .bmp was read in successfully

} // end of loadBMP(char *file_name, HDC hdc)