DEPARTMENT OF COMPUTING

CS 3005: Programming in C++

void PPM::writeStream(std::ostream& os) const

The writeStream method is used to send the PPM data to the output stream os. Since PPM file is a mixture of ASCII data for the header information, and binary data for the pixel information, we need to use a mixture of stream operations.

The << Operator’s Functionality

The << (output operator or stream insertion operator) is used to send data to an output stream, possibly transforming the data for the stream. For example, to send a nice message to a stream you might write:

os << "Hello, friend.  How about a nice game of chess?";

The << operator has two operands. The left-hand side must be an object whose type is std::ostream (or a type that inherits from std::ostream). The right-hand side is a variable (such as x or y) or a literal value (such as 3 or "hello").

The operator’s responsibility is to send the data of the right-hand side into the ostream of the left-hand side, using methods or functions that work on the ostream.

C++ comes with many versions of << to send data of various types to an ostream. See the reference.

The type of the right-hand side value is important for the compiler to select the correct operator implementation. If the type is a built-in type (such as int) or a standard library type (such as std::string), then the compiler will correctly find one of the built-in << implementation that matches the type. This implementation will convert the data to ASCII characters and send the characters to the ostream.

The ostream::write() Method’s Functionality

If we want to send raw data (binary data as bytes) to the stream, instead of converting the data to ASCII first, we use the .write() method of the ostream class. In this case, we tell the write method where the data is located in memory, and how many bytes we want to have written to the stream. Here’s an example to write the contents of an int variable to an ostream, os:

int x = 314159;
os.write((char *) &x, sizeof(x));

The &x is an expression to calculate the location (memory address) of the variable x. The (char *) is necessary to tell the write() function to treat the address as the location of a collection of bytes (the built-in char type is a single byte). The sizeof() operator will calculate how many bytes are required by its argument.

Writing a PPM File

The format of a PPM file is

P6 WIDTH HEIGHT MAX_COLOR_VALUE\n
BINARY REPRESENTATION OF COLORS FOR EACH PIXEL IN THE SAME ORDER AS THE COLOR FILE

where the meta data P6 WIDTH HEIGHT MAX_COLOR_VALUE\n is (ASCII)[https://en.wikipedia.org/wiki/ASCII] encoded, and the rest of the data is binary data.

So, we will use the << operator to literally send "P6" to os. We’ll follow this with a literal space " " as well. Next, we need the PPM’s width and height sent to the stream as ASCII. So, again, the << will be used to send the values. Don’t forget to add spaces between them. The maximum color value needs the same treatment.

After the maximum color value send a literal newline character. This is a departure from using std::endl. Use "\n" here. Exactly one, with no trailing spaces sent to the stream.

Be sure that you put a space between each of the values in the meta data. Otherwise, the file will not be correctly formatted. Think of the difference between these two lines:

P6 200 150 255\n
P6200150255\n

This concludes the ASCII header of the file. The rest of the work will send the binary pixel data using .write().

We use 1 byte per channel (that’s one byte for red, one for green, and one for blue). We do not put any spaces or other data between these values. Spacing and separation are not an issue, because each pixel uses exactly these 3 bytes, so we know how to use the data.

To send binary data, we use the write method of the ostream.

Here is an example of writing a single byte:

unsigned char c = 17;
os.write((char *) &c, sizeof(c));

Of course, you don’t want to write the number 17. You want to write the value of a channel. In fact you want to write the value of all channels. Remember the getChannel() method of the PPM class. You probably want to use 3 nested for loops to work over all rows, columns and channels.

Last Updated 09/18/2020