read a ppm file in c++

Started by
1 comment, last by Merowingian 16 years, 8 months ago
hi, i just wann read a ppm file in c++ and place the rgb-values in a character array. i have written the following code to do this:

#include <ppmreader.h>
#include <iostream>
#include <fstream>
#include <cstring>

using namespace std;

PPMReader::PPMReader(char * filename) {
	loadPPMFile(filename);
}

void PPMReader::loadPPMFile(char * filename) {

	const int readBufferSize = 20;
	ifstream ifs;
	char buffer[readBufferSize];
	ifs.open(filename, ifstream::in);

	ifs.getline(buffer, readBufferSize);
	ifs.getline(buffer, readBufferSize, ' ');
	width = atoi(buffer);
	ifs.getline(buffer, readBufferSize);
	height = atoi(buffer);
	ifs.getline(buffer, readBufferSize);
	unsigned int bitDepth = atoi(buffer);
	bytesPerPix = (bitDepth == 255) ? 3 : 6;

	unsigned int imagePixs = width * height;
	unsigned int imageBytes = imagePixs * bytesPerPix;
	image = new char[imageBytes];
	unsigned int bytesPerLn = width * bytesPerPix;
	for (int i = 0; i < int(height); i++) {	
		char* tmp = new char[bytesPerLn];
		ifs.getline(tmp, bytesPerLn);
		strcat(image, tmp);
	}
	
	ifs.close();

}

unsigned int PPMReader::getBytesPerPix() {
	return bytesPerPix;
}


prior to this, i have tried to use the read()-function of ifstream first, but it failed to write the contents into the character array: the failbit was set to 1, the eof bit, too. i have calculated the number of bytes to be read after reading the ppm header information. you can follow the calculation in my code. it should be correct. this is about a 256x256 pixel ppm file with 3 bytes per pixel for the rgb-values. i really don't understand why ifstreams arrives at eof too quickly. here's the ppmreader.h, just in case you need it:

#ifndef PPMREADER_H
#define PPMREADER_H

#include <fstream>

using namespace std;

class PPMReader {

private:

	char * image;
	unsigned int width;
	unsigned int height;
	unsigned int bytesPerPix;

public:

	PPMReader(char * filename);

	inline void getImage(char * &dest) { dest = image; };

	inline unsigned int getWidth() { return width; };

	inline unsigned int getHeight() { return height; };

	void loadPPMFile(char * filename);

	unsigned int getBytesPerPix();

};

#endif

[Edited by - Promit on July 30, 2007 7:51:11 PM]
Advertisement
The appearance of atoi, strcat, fixed size buffers, manual memory allocation and char*s is making my spidey sense tingle. There's a lot of potentially dangerous code in there.

An istream will typically do buffering for you and the usual extraction operators will perform just fine, here. Let's use them.

Really, the first thing you'll need to make is a function that skips over 'line comments', denoted by '#' in PPM files. Or better yet a type of object for which we can overload the stream extraction operator in order to skip comments.

After that you need to:
1. Read the magic number in to an std::string using the stream extraction operator
2. Read the image dimensions and pixel-component maximum in to integral objects, again using appropriate stream extraction operators
3. Read either a binary or 'ascii' raster depending on whether the magic number was 'P6' or 'P3'. For this you'd use the read() member function of an istream for binary, or stream extraction repeatedly for ascii.

You'll need error checks along the way of course, and you'll also need to look for line comments at appropriate points in the reading process.

Really, you could make the function look something like this:

bool read_ppm(std::istream &in, image &raster){    comment_skip comment; // implement appropriately    std::string magic;    long width = 0;    long height = 0;    long pixel_max = 0;    // read the header    in >> magic >> comment >> width >> comment >> height >> comment >> pixel_max;    // check the header    if (in &&         (magic == "P6" || magic == "P3") &&        width > 0 && height > 0 &&         pixel_max > 0 && pixel_max < 256)    {        // PPM "header" is valid        if (magic == "P6")            return read_ppm_raster_binary(in, raster); // implement appropriately        else            return read_ppm_rater_ascii(in, raster); // implement appropriately    }    return false;}// convenience wrapperbool read_ppm(const std::string &filename, image &raster){    std::ifstream in(filename.c_str(), std::ios::binary);    return read_ppm(in, raster);}


IMHO, this is much more readable, not to mention free of memory leaks. There's also zero chance of buffer overrun occurring, by construction.

Edd
Hey, I solved the problem.

The main problem was the usage of binary mode, which you need to explicitly specify at the open() call of ifstream.

Otherwise the program will tell Windows to open the file in text mode, occupying more space than neccessary due to binary-to-text conversion.

Cheers
Marvin

This topic is closed to new replies.

Advertisement