Click here to Skip to main content
15,881,172 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
Hello all. I am currently trying to copy the header and pixel data from an image file and write this into a new image file. I am using a 256 colour bitmap and for testing have chosen a fixed height and width pixel of 150 and 90. A new file image file is created but when trying to load this, the following message is given: "Paint cannot read this file. This is not a valid bitmap file or its format is currently not supported. Would appreciate the support after many attempts. Below is the code I'm using:

C++
02     const int height = 150;  

03     const int width = 90;  

05     unsigned char m_cHeaderData[1078];  

06     unsigned char** m_cImageData = new unsigned char* [height];  
  

08     for( int i = 0; i <height; i++)  

09     {  

10         m_cImageData[i] = new unsigned char [width];  

11     }  

12    

13     ifstream* m_pInFile;      

14     m_pInFile = new ifstream;  

15     m_pInFile->open("image.bmp", ios::in | ios::binary);  

16     m_pInFile->seekg(0, ios::beg);  

17     m_pInFile->read(reinterpret_cast<char*>(m_cHeaderData), 1078); 
       //bitmap bits start at offset 1078  

18    

19     for(int i = 0; i <height; i++)  

20     {  

21         m_pInFile->read(reinterpret_cast<char*>(m_cImageData[i]), width);   

22     }  

23    

24     m_pInFile->close();  

25    

26     ofstream* m_pOutFile;   

27     m_pOutFile = new ofstream;  

28     m_pOutFile->open("imageCopy.bmp", ios::out | ios::trunc | ios::binary);      

29     m_pOutFile->write(reinterpret_cast<char*>(m_cHeaderData), 1078);  

30     for(int i = 0; i <height; i++)      

31     {  

32         m_pOutFile->write(reinterpret_cast<char*>(m_cImageData[i]), width;  

33     }  

34     m_pOutFile->close();  


37     //deallocate memory:  

38     delete m_pInFile;  

39     delete m_pOutFile;  

40     for(int i = 0; i <height; i++)  

41     {  

42         delete[] m_cImageData[i];  

43     }  

44     delete[] m_cImageData;  
Posted
Updated 11-Jul-18 6:13am

You should have a look at the bitmap file format (Wikipedia[^]).

You should not use a fixed header size and fixed properties. Read the data from the file header instead.

Even if your bitmap file has the used properties (header size of 1078, 256 bit colours, matching width and height) you will not create a proper bitmap file. This is because the data are packed in rows where the size of each row must be a multiple of 4. With a width of 90 pixels each row is stored in 92 bytes rather than 90.

[UPDATE]
You may search the the web for example code on how to read a bitmap file into memory. Examples can be found here: http://www.cplusplus.com/articles/GwvU7k9E/[^] and here: https://graphics.stanford.edu/~mdfisher/Code/Engine/Bitmap.cpp.html[^].

What kind of data must be read depends on what you want to do. If you want to write the image back to a new file, you must read and parse the necessary header information and the image bits. Because there are different header versions (with different sizes) and optional colour tables, you must read all of them them, parse the information, and write all data to the new file in the same order.

Note also that it is more efficient to read the image bits into one large buffer rather than allocating buffers for each row. Then this buffer includes the stride (the padding bytes for the rows).
 
Share this answer
 
v2
Comments
CPallini 8-Oct-15 7:55am    
5.
binaryoffset 8-Oct-15 9:37am    
Thanks for the reply. Very useful. I've tested and noticed that the file opens when I manually set the width to a multiple of 4 in the program (even though I keep the original image the same size).

The original image has size 14.5k.
And the copied image (which now opens) has size 14.7k.

I've used BITMAPFILEHEADER and BITMAPINFOHEADER before to extract the width and height of an image before trying to read and write, but I was still confronted with the same problem (that the copied file wouldn't open). I even used the offset value from bfOffBits.

How would I correctly 'read' from the header file?

Should the width turn out not to be a multiple of 4, should I change the value in the program to the nearest multiple of 4?

Thanks for your feedback in advance.
Jochen Arndt 8-Oct-15 9:56am    
See my updated answer. But it would be good to know what you finally want to do. Depending on that some header parsing steps may be omitted.
binaryoffset 8-Oct-15 10:53am    
Thanks for your updated feedback. I eventually want to proceed to applying image processing algorithms (e.g RGB to greyscale). The initial idea being able to extract the pixel data into an array (1D or 2D) and then proceed to extracting the RGB colours from each pixel as well.
As pointed out by Jochen each horizontal line of the original file contains 2 spare bytes (since bitmap stride must be multiple of 4). Hence your 'copy file' is actually 300 bytes smaller than the original one.
 
Share this answer
 
Thanks for the reply. Very useful. I've tested and noticed that the file opens when I manually set the width to a multiple of 4 in the program (even though I keep the original image the same size).

The original image has size 14.5k.
And the copied image (which now opens) has size 14.7k.

I've used BITMAPFILEHEADER and BITMAPINFOHEADER before to extract the width and height of an image before trying to read and write, but I was still confronted with the same problem (that the copied file wouldn't open). I even used the offset value from bfOffBits.

How would I correctly 'read' from the header file?

Should the width turn out not to be a multiple of 4, should I change the value in the program to the nearest multiple of 4?

Thanks for your feedback in advance.
 
Share this answer
 
void CMRSMATHDlg::Loadit(TCHAR *destination)
{
	CImage img;
	bool reverse=false;
	BITMAPINFOHEADER Info;
	BITMAPFILEHEADER bFileHeader;	
	CFile file2;
	file2.Open(destination, CFile::modeRead | CFile::typeBinary);
	file2.Read(&bFileHeader, sizeof(BITMAPFILEHEADER));
	file2.Read(&Info, sizeof(BITMAPINFOHEADER));
	BYTE ch;
	int width = Info.biWidth;
	int height = Info.biHeight;
	if (load1.m_hObject!=NULL)
	 {
	 load1.Detach();
	 //load1.DeleteObject();
	 //load1.CreateCompatibleBitmap(GetDC(),width,height);
	 }
	if (height > 0)
	{
		reverse = true;
	}
	if (height<0)
		height = -height;
	int size1 = width*height * 3;
	int size2 = ((width * 24 + 31) / 32) * 4 * height;
	int gap = (size2 - size1) / height;			
	BYTE * buffer1 = (BYTE *)GlobalAlloc(GPTR, size2);		
	//////////////////////////
	HGDIOBJ old;
	unsigned char alpha = 0;	
	int z = 0;	
	z = 0;			
	CRect rc;
	GetClientRect(&rc);					
	memset(buffer1,255,size2);	
	file2.Read(buffer1,size2);		
     int dpi=GetDC()->GetDeviceCaps(LOGPIXELSX);
	 int dpmm=dpi/25; //dots per mm<br/>
	 BITMAPINFO *info1;
	 info1=(BITMAPINFO *)new  BYTE[sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD)*256]; 	 
	 info1->bmiHeader=Info;
	 info1->bmiHeader.biXPelsPerMeter=dpmm*1000;
	 info1->bmiHeader.biYPelsPerMeter=dpmm*1000;
	 info1->bmiHeader.biBitCount=24;	 
	 RGBQUAD *rgbPalette=(RGBQUAD *)malloc(sizeof(RGBQUAD)*256); 
   for(int i=0; i<256; i++){
     rgbPalette[i].rgbRed = (byte)i;
     rgbPalette[i].rgbGreen = (byte)i;
     rgbPalette[i].rgbBlue = (byte)
     rgbPalette[i].rgbReserved = 0;}
     memcpy(info1->bmiColors, rgbPalette, sizeof(RGBQUAD) * 256);
	 //info1.bmiColors=	
	 CDC mem;
	 mem.CreateCompatibleDC(0);
	 HDC hDC = mem.m_hDC;
	 HDC m_hDC = CreateCompatibleDC(hDC);	 	 
	 load1.Attach(CreateDIBSection(NULL,info1,DIB_RGB_COLORS, (LPVOID*)0,0,0));	 	 	 
	 load1.SetBitmapBits(size2,buffer1);
	 load1width=width;
	 load1height=height;
	 CImage s1;	 
	 GlobalFree(buffer1);	 
	 file2.Close();
 }

void CMRSMATHDlg::saveit(CBitmap &bit1, CDC &memdc, TCHAR *destination,bool isreverse)
{
	 BITMAP bm;
	 BITMAPINFOHEADER Info;
	 BITMAPFILEHEADER bFileHeader;
	 CFile file1;
     CSize size = bit1.GetBitmap(&bm);
	 int z = 0;
	 BYTE ch = 0;
	 size.cx = bm.bmWidth;
	 size.cy = bm.bmHeight;
	 int size1 = (size.cx)*(size.cy);
	 int size2 = size1 * 3;
	 size1 = (((size.cx) * 24 + 31) / 32) *4* (size.cy);
	 int gap = (size1 - size2) / (size.cy);
	 BYTE * buffer = (BYTE *)GlobalAlloc(GPTR, size2);
	 BYTE * buffer1 = (BYTE *)GlobalAlloc(GPTR, size.cx*3);
	 BYTE * gbuffer = (BYTE *)GlobalAlloc(GPTR, gap);
	 memset(gbuffer, 0, gap);
	 bFileHeader.bfType = 'B' + ('M' << 8);
	 bFileHeader.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
	 bFileHeader.bfSize = bFileHeader.bfOffBits + size1;
	 bFileHeader.bfReserved1 = 0;
	 bFileHeader.bfReserved2 = 0;
	 Info.biSize = sizeof(BITMAPINFOHEADER);
	 Info.biPlanes = 1;
	 Info.biBitCount = 24;//bm.bmBitsPixel;//bitsperpixel///////////////////32
	 Info.biCompression = BI_RGB;
	 Info.biWidth =size.cx;
	 Info.biHeight =size.cy;///reverse pic if negative height
	 Info.biSizeImage =size1;
	 Info.biClrImportant = 0;	 
	 Info.biClrUsed = 0;
	 Info.biXPelsPerMeter = 0;
	 Info.biYPelsPerMeter = 0;
	 bit1.GetBitmapBits(size2, buffer);	 	 
	 file1.Open(destination, CFile::modeCreate | CFile::modeWrite |CFile::typeBinary, 0);
	 file1.Write(&bFileHeader, sizeof(BITMAPFILEHEADER));
	 file1.Write(&Info, sizeof(BITMAPINFOHEADER));
	 unsigned char alpha = 0;
	 int i;
	 for (int y = 0;y<size.cy;y++)
	 {
		 i=0;
		 for (int x = 0;x<size.cx;x++)
		 {
		  //for reverse picture below
	      //z = (((size.cy - 1 - y)*size.cx) + (x)) * 3;
			if (isreverse)
		  z = (((size.cy-1-y)*(size.cx)) + (x)) * 3;
			else
			z=(y*size.cx+x)*3;
		  buffer1[i]=buffer[z];
		  buffer1[i+1]=buffer[z+1];
		  buffer1[i+2]=buffer[z+2];
		  i+=3;
		 }
		  file1.Write(buffer1,size.cx*3);
		  file1.Write(&gbuffer, gap);
	 }
	 GlobalFree(buffer);
	 GlobalFree(gbuffer);
	 GlobalFree(buffer1);
	 file1.Close();
	 file1.m_hFile = NULL;
 }
 
Share this answer
 
v4

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900