Not in FIle

Pages: 123
I am following along with a tutorial that is creating a bitmap file and writing fractal images to it. I am stuck on the part where I need to write to the file. I am not getting an image of a bitmap with a pixel in it. When I put a break point to test if I have opened the file, it shows that I am not in the file. I have double checked my code against the tutorial and I can not see what is wrong.

I have several files in the program, but didn't want to bombard the page with code. Please let me know if I should post anything else.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  bool Bitmap::write(string fileName)
	{
		BitmapFileHeader fileHeader;
		BitmapInfoHeader infoHeader;

		fileHeader.fileSize = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + (m_width * m_height * 3);

		// Distance from beginning of file to where data actually starts.
		fileHeader.dataOffSet = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader);

		infoHeader.width = m_width;
		infoHeader.height = m_height;

		ofstream file;
		file.open(fileName, ios::in | ios::binary);

		if (!file) {
			cout << "File not open." << endl;
			return false;
		}
		return true;

		// First arg always has to be a char star ptr to the address of the struct (or class).
		file.write((char*)&fileHeader, sizeof(fileHeader));
		file.write((char*)&infoHeader, sizeof(infoHeader));
		// Can't cast a unique ptr (m_pixels) to a char * so need the get() method to return it as a regular ptr.
		file.write((char*)m_pixels.get(), m_width*m_height*3);

		file.close();

		if (!file) {
			return false;
		}

		return true;
	}
Well, on line 21 you unequivocally return from the function (return true;), so nothing you write after that point will be done.
When I run the program it goes in the if statement on line 17, and then returns false. So, it exits the function at this point prematurely. I am trying to get it to create a bmp file at this point and write a pixel to it.

I see what you mean on line 21. I got rid of that. Thank you.
> infoHeader.width = m_width;
What about all the other members?
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapinfoheader
https://docs.microsoft.com/en-us/windows/win32/api/wingdi/ns-wingdi-bitmapfileheader
I'm guessing there's all kinds of garbage in the headers that make all the image viewers barf on the file.

> file.open(fileName, ios::in | ios::binary);
It seems strange to open an output file with ios::in

> file.write((char*)m_pixels.get(), m_width*m_height*3);
Bear in mind that each ROW of pixels is padded to 4-byte alignment.
https://en.wikipedia.org/wiki/BMP_file_format#Pixel_storage
I can post the other headers. The code I typed is from following the tutorial so I am not sure where I am off.

There is a pre processor directive that gets rid of extra padding.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#pragma once
#ifndef BITMAPFILEHEADER_H_
#define BITMAPFILEHEADER_H_
#include <cstdint> // Helps gaurantee 32 bit ints for portability.
using namespace std;

#pragma pack(2) // Pre-proccessor directive which gets rid of any padding that C++ will automatically 
// add to the struct. Struct needs to look exactly as is otherwise there will be extra padding bytes.


namespace caveofprogramming
{
	struct BitmapFileHeader
	{
		char header[2]{ 'b','m' }; // Tells the app that opens this file that it's a bitmap file.
		int32_t fileSize;
		int32_t reserved{ 0 };
		int32_t dataOffSet; // Says how long into the fiel that data begins.

	};

}


#endif 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#pragma once
#ifndef BITMAPINFOHEADER_H_
#define BITMAPINFOHEADER_H_
#include <cstdint>
#pragma pack(2) // Gets rid of extra padding.

// Could use int or short, but int32_t gaurantees to be the right size.
namespace caveofprogramming {

	struct BitmapInfoHeader
	{
		int32_t headerSize{ 40 }; // Size of this struct
		int32_t width;
		int32_t height;
		int16_t planes{ 1 };
		int16_t bitsPerPixel{ 24 }; // 8x3=24 so there are 8 bytes per pixel.
		int32_t compression{ 0 }; // Set compression to 0 otherwise it becomes more complicated.
		int32_t dataSize{ 0 }; // Going to set size later. Good to initialize to 0 for now.
		int32_t horizontalResolution{ 2400 };
		int32_t verticalResolution{ 2400 };
		int32_t colors{ 0 };
		int32_t importantColors{ 0 };

		// If you get this wrong you won't find out until you try to write your bitmap and can't open it.
	};

}
#endif; 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#pragma once
#ifndef BITMAP_h_
#define BITMAP_h_

#include <string>
#include <cstdint>
#include <memory> // Needed for unique ptr
#include <iostream>
using namespace std;

namespace caveofprogramming
{ 
	class Bitmap
	{
	private:
		int m_width{ 0 };
		int m_height{ 0 };
		unique_ptr< uint8_t[] > m_pixels{ nullptr }; // Using a unique ptr allows us to avoid using the 
		// destructor to deallocate memory (unlike a regular ptr). This is a ptr to an area of memory
		// big enogh to hold all the pixels.

	public:
		Bitmap(int width, int height);
		bool write(string fileName);
		virtual ~Bitmap();
		void setPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue); // Unsigned 8 bit char 0 - 255.
			
	};
}



#endif; 

Right, so your headers do seem to be initialised fully then.

Is m_width*3 a multiple of 4 ?
If it isn't, you need row padding.

When you've saved your file, make a hex dump or load it into a hex editor ( https://mh-nexus.de/en/hxd/ ).
1
2
3
4
5
6
7
8
9
10
11
12
13
$ identify Documents/tiger.bmp
test.bmp BMP3 630x354 630x354+0+0 8-bit sRGB 670KB 0.000u 0:00.000
$ hd test.bmp | head
00000000  42 4d 7e 38 0a 00 00 00  00 00 36 00 00 00 28 00  |BM~8......6...(.|
00000010  00 00 76 02 00 00 62 01  00 00 01 00 18 00 00 00  |..v...b.........|
00000020  00 00 48 38 0a 00 13 0b  00 00 13 0b 00 00 00 00  |..H8............|
00000030  00 00 00 00 00 00 11 3e  67 40 74 a3 66 a1 d6 2a  |.......>g@t.f..*|
00000040  65 9b 38 6f a4 39 64 87  17 3e 54 03 2a 2d 01 2d  |e.8o.9d..>T.*-.-|
00000050  24 09 41 35 03 3b 30 02  37 2e 08 42 38 0e 47 3d  |$.A5.;0.7..B8.G=|
00000060  08 3d 34 04 33 2b 08 31  27 04 2c 1e 07 31 23 07  |.=4.3+.1'.,..1#.|
00000070  37 2e 19 50 4c 1f 45 49  0c 2e 2f 00 24 22 0e 41  |7..PL.EI../.$".A|
00000080  42 1a 65 6a 18 5a 5e 07  49 46 01 3d 45 04 4d 6d  |B.ej.Z^.IF.=E.Mm|
00000090  1b 87 a2 02 6e 80 1c 62  73 12 3f 43 09 39 35 01  |....n..bs.?C.95.| 

Are the bytes what you expected?

Last edited on
Unfortunately, I have never heard of a hex editor before, and I wouldn't know how to interpret the values in it. I just know that at this point I should be getting a window to display a pixel, and that is not happening.
HxD, one of the better freeware hex and disk editors around.
https://mh-nexus.de/en/hxd/

(salem, your link was borked by the site's URL interpreter because you didn't separate it from the enclosing parentheses)

How to Use a Hex Editor (meta search):
https://duckduckgo.com/?q=how+to+use+a+hex+editor&t=ffsb&ia=web
learning to use the editor is excellent use of your time.
they are not that hard.
the left side is usually the raw hex bytes, 2 values per byte eg FF is 255
the right side is mostly useless, but its there because sometimes the data is text, and you can read it there. Its the ASCII text of the raw bytes. If it does not look like anything useful, you can ignore it, it may just be integer or doubles or the like and the ascii is random senseless garbage. frequently for multi-byte text it is there but split/spaced out as in T H I S I S U N I C O D E T E X T ... you get used to it.
then, beyond that, most tools have a mouse-over feature that breaks down, starting at the byte you are looking at, the values as 8, 16, 32, 64 signed and unsigned integers, float and double value (32 and 64 bit respectively). You have a flag in the settings for which byte order the file used (sometimes called endian).

that is 99% of it.
play with it for 10 min ... write out some integers and doubles and strings to a binary file, open it, and look at what you got.

displaying one pixel can be hard to see on a modern monitor where 1 pixel is very small indeed. That period after indeed is probably 9+ pixels at high resolutions.
Last edited on
If you're lucky, the C++ library uses the C library, which will tell you what went wrong. Try this:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <fstream>
#include <cerrno>
#include <cstring>

int main(int arc, char **argv)
{
    std::ofstream file;
    file.open(argv[1], std::ios::in | std::ios::binary);
    if (!file) {
	std::cout << "Can't open " << argv[1] << ": " << strerror(errno) << '\n';
    }
}

I have checked out the hex editor some, but my problem is that I am not seeing how to create a bmp file. When I do a Google search on how to do it I am seeing information on using Paint or Photoshop to save files as a bmp; I don't have those applications. Shouldn't one be automatically created in the write function? That is what happened in the tutorial. He was using Eclipse, and I am on Visual Studio, if that makes any difference.
to make a bmp file you need to write out the correct header data followed by the image data which may or may not have some light compression (I forget).

the open actually creates the file, if it did not exist already. write just populates it.

your IDE does not make any difference though VS has native bmp support in its libraries.

to make a bmp yourself, read up on the format and populate it as instructed:
https://en.wikipedia.org/wiki/BMP_file_format#Bitmap_file_header

or use a library.
you once were able to dump raw RGB or single byte greyscale to a raw binary file and open it in editors but it seems like 99% of free editors have removed this, its a shame.
Last edited on
you once were able to dump raw RGB or single byte greyscale to a raw binary file and open it in editors but it seems like 99% of free editors have removed this, its a shame.
You actually can do something very close to this. It's called portable pixel format (PPM).
https://en.wikipedia.org/wiki/Netpbm#File_formats

I don't have a list of all the applications that support it. The major ones like Photoshop and GIMP should. The default Windows 7 viewer does not support it, but the default Windows 10 viewer does. Adding support for it is incredibly easy since it's just a direct translation into a bitmap.
good to know. it used to be the .RAW extension but that seems to be hijacked by camera hardware now. Thx Ganado. I respect the GIMP but its a giant bloatware mess that takes longer to open than eclipse (this is an incredible accomplishment, I was not sure it could be done!).

Here OP: this opens for me in an older image viewer; its not the most up to date bmp format and some tools may complain. In spite of the older format it does work and gives you a bit of what you might want to do. I cooked this up from the wiki page above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <fstream>
#include <vector>
using namespace std;
#pragma pack(1) //if you do not do this your compiler may decide to inject padding
//which will break things badly.   you can also push and pop this to change it for a sec, 
//then revert to the default or current setting. 

struct bmphdr
{
  uint16_t bm{0x4d42};
  uint32_t thisfilesize{};
  uint32_t two16bitzeros{};
  uint32_t pixeladdr{};  
};

struct dibhdr //old school
{
  uint32_t forty{40};
  uint32_t widthpxls{};
  uint32_t heightpxls{};
  uint16_t one{1};
  uint16_t bpp{24}; //bits per pixel
  uint32_t comp{0};
  uint32_t rawsize{};
  uint32_t hres{2835};
  uint32_t vres{2835};
  uint32_t zero{};
  uint32_t zero2{};  
};

struct bmp
{
  bmphdr hdr;
  dibhdr dib;
  vector<uint8_t> pixels;
};

int main()
{
  bmp b;
  b.pixels.reserve(100*50*3);
  b.dib.widthpxls = 50;
  b.dib.heightpxls = 100;
  b.hdr.thisfilesize = sizeof(bmphdr)+sizeof(dibhdr)
                     + b.dib.widthpxls* b.dib.heightpxls*3;
  b.hdr.pixeladdr = sizeof(bmphdr)+sizeof(dibhdr);
  b.dib.rawsize = 100*50*3;
    
  for(int i = 0; i < 100*50*3; i++)
	b.pixels[i] = i*100+i>>3; //whatever image data here
  
  ofstream ofs("tst.bmp", ios::binary);
  ofs.write((char*)(&b.hdr), sizeof(bmphdr));
  ofs.write((char*)(&b.dib), sizeof(dibhdr));
  ofs.write((char*)&b.pixels[0], 3* b.dib.widthpxls* b.dib.heightpxls);
}
Last edited on
In the write function I changed the code ios::in to ios::out . So, now the test.bmp file is created, and in the project folder. However, if I try to open it with "Paint" I get a pop up window that says, "This is not a valid bitmap file, or its format is currently not supported".
Yes its an older format, paint may not like it. I can open the files from the code I made using 'lazpaint' which is a little freeware editor that does just what I want without all the annoying stuff. If you want to follow the wiki page and formatting to make the modern headers and all, go ahead. The example should be enough to get you started on how to do it ... same ideas, populate the headers, populate the image data, dump to a file...

https://portableapps.com/apps/graphics_pictures/lazpaint-portable
I have 6.41 version, looks like they have updated it a wee bit since then. (and broke it, from the site description lol)

anyway, the error is not YOU (if using my code), its that paint removed the ability to read older files.

I can't say it enough: this may be a simple example of HOW to understand and write a file format using documentation to cook up the correct bytes but when and where you can, you should use a library to do this stuff. Even this simple format, I overlooked a dozen details and hard-coded a lot of numbers to make it fit. Changing all the hard-coded settings in the headers to vary based off need would blow the code up to a fair little project... and its already been done by experts, so why dig into that (apart from to learn). If you want to learn it, by all means have a go, its simple enough to be doable in a few days and complex enough to give you a solid task. Just hacking it to produce a greyscale image that paint could read would be a good 1-2 day effort.
Last edited on
I am really just trying to learn it from the perspective of the tutorial that I am currently following.
If I can get this to work, and it gauges my interest, then I would be willing to dive into learning with all the extra recourses provided. Otherwise, it makes sense to learn how to do this with libraries like your saying. I definitely appreciate everyone's effort to put forth information though.

I tried to open the bmp file with lazpaint, and got a pop up window saying, "The image format is unknown."

I have been follwoing the tutorial code for code, so I can't see why he can an image editor window to pop up at this point but I can't.
TBH, you need to learn how to show us a hex dump of the header of your file.

Even if it's only loading the file into HxD and then taking a screenshot and uploading a .png file to a sharing site (make sure it's legible).

With such information, it's relatively straight forward to check that the relevant bytes describing your image are correct for the file format.

yea post your currentmost code maybe, or the hex of the image file.

did you do the pack() command? Often with this kind of work, that is the first thing to overlook and facepalm fix.
Last edited on
I'll post the rest of my code.

I have the pack() command at the top of the BitmapInfoHeader file.

I opened the bmp file with HxD, and took a screenshot. However, I do not see an option for uploading images on this site.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <fstream>
#include "bitmap.h"
#include "bitmapFileHeader.h"
#include "bitmapInfoHeader.h"

using namespace std;
using namespace caveofprogramming;

int main()
{
	int const WIDTH = 800;
	int const HEIGHT = 600;
	Bitmap bitmap(WIDTH, HEIGHT); // 800, 600 about standard screen resolution

	bitmap.setPixel(WIDTH / 2, HEIGHT / 2, 255, 255, 255);
	bitmap.write("test.bmp");

	cout << "Finished!" << endl;

}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
#include <fstream>
#include "bitmap.h"
#include "bitmapInfoHeader.H"
#include "bitmapFileHeader.H"

#include <iostream>
#include <cstdint> // For unsigned 
using namespace std;

namespace caveofprogramming
{// Curly brackets at the end of m_pixels initiallizes it to all zero
	Bitmap::Bitmap(int width, int height) : m_width(width), m_height(height), m_pixels(new uint8_t[height * width * 3]{}) // Every pixel has 3 bytes
	{

	}

	// We're creating a bitmap, setting pixels in it and writing to it with this function.
	// Writing to files is errror prone, so we'll make it bool to return false if it does'nt work.
	bool Bitmap::write(string fileName)
	{
		BitmapFileHeader fileHeader;
		BitmapInfoHeader infoHeader;

		fileHeader.fileSize = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader) + m_width * m_height * 3;

		// Distance from beginning of file to where data actually starts.
		fileHeader.dataOffSet = sizeof(BitmapFileHeader) + sizeof(BitmapInfoHeader);

		infoHeader.width = m_width;
		infoHeader.height = m_height;

		ofstream file;
		file.open(fileName, ios::out | ios::binary);

		if (!file) {
			cout << "File not open." << endl;
			return false;
		}
		

		// First arg always has to be a char star ptr to the address of the struct (or class).
		file.write((char*)&fileHeader, sizeof(fileHeader));
		file.write((char*)&infoHeader, sizeof(infoHeader));
		// Can't cast a unique ptr (m_pixels) to a char * so need the get() method to return it as a regular ptr.
		file.write((char*)m_pixels.get(), m_width*m_height*3);

		file.close();

		if (!file) {
			return false;
		}

		return true;
	}


	Bitmap::~Bitmap()
	{

	}

	// Allows us to set pixels in a bitmap to specify colors. There is one byte of info for each pixel.
	// x and y are used to specify a particular pixel.
	void Bitmap::setPixel(int x, int y, uint8_t red, uint8_t green, uint8_t blue) // Unsigned 8 bit char 0 - 255.
	{
		// Pointer to a particular pixel specified by x and y.
		uint8_t* pixel = m_pixels.get(); //Can't assign a unique pointer to a regualr pointer, but we can with the get() method.

		// y * m_width is th number of pixels in each byte (if each pixel is one byte) in all the rows before the 
		// one we are intersted in as specified by y. Once we get it in the right row, we then move it forward
		// to the correct column with x. Since each pixel is taking up 3 bytes we have to mult x and y by 3.
		// If we add the value one to this ptr that would move it forward by one byte.
		pixel += (y * 3) * m_width + (x * 3);

		// We can now use the array subscipts to reference the red, blue, and green bytes following the one 
		// that ptr "pixel" points at.
		pixel[0] = blue;
		pixel[1] = green;
		pixel[2] = red;

		// Hexadecimal number: FF for red, 88 for blue, 33 for green. Takes exactly 3 bytes; 3 digits for each number.
		// The last digits in a hexadecimal number represent the first area in memory.
		0xFF8833;



	}

}
Pages: 123