Search code examples
c++openglcolorsppm

Read PPM file and display with Opengl and c++. The colors are incorrect


I am working on a program that reads a PPM file(P6 formate )and display it with Opengl and c++.

The PPM image can be displayed with my code but the colors of the image is incorrect. The person's face should be brown and the background should be blue.

I read the files with ifstream and put the data into an array dataByte[] of char. Then I use glDrawPixels() to display the image. It works correctly with PGM file.

Here is my code. I need to do some operations to the image.The operations works fine but only when I display PPM file, the colors of the image are not as it display in photoshop.

The background should be blue but it turns to red and so on...I cannot uploads images right now so here is the link of the screenshot of the images

the image should be display but it display in this way

I think the problem is due to the functions I read the data.

 #include "stdafx.h"
    #include <stdlib.h>
    #include <GL/glut.h>
    #include <iostream>
    #include <fstream>
    #include <string>
    #include <stdio.h>

    using namespace std;
    GLint width;
    GLint height;
    GLint wholeLength;
    GLint  grayLevel;
    //GLubyte *dataByte;
    char* dataByte;
    GLint type;





//ignore the comment in PPM    
void ignoreComment(ifstream &f){
            char buf[1024];
            char t;
            t=f.peek();
            while (t=='\n' || t=='\r'){
                f.get();
                t=f.peek();
            }
            if(t=='#'){
                f.getline(buf,1023);
            }

        }

This is the function I read to image and put the data in dataByte[]. I think the problem is because the way I get the data from the PPM file. But I don't know how to fix it

    void readImage(string fname){


       ifstream f;
       f.open(fname.c_str(), ifstream::in | ifstream::binary);
       cout<<fname.c_str()<<endl;
       if(!f.is_open() ){
         cout<<"Cannot open the file"<<endl;
         return;
       }
       else
           cout<<"opened"<<endl;

        //get type
       ignoreComment(f);
       string temp;
       f >> temp;
       if( (!(temp[0]=='P' )  && !(temp[0]=='p' ) )|| 
           ( !(temp[1]=='6' )  && !(temp[1]=='5' ))
          ){
        cout<<temp<<endl;
        cout<<"cannot read this format"<<endl;
        f.close();
        return;
       }
       else{
        cout<<"file opened"<<endl;
        cout<<temp<<endl;
       }


     if(temp[1]=='5'){
        type=5;
        cout<<"type:"<<type<<endl;
       }
      if(temp[1]=='6'){
        type=6;
        cout<<"type:"<<type<<endl;
       }


        //get width,height
       ignoreComment(f);
       f >> width;
       ignoreComment(f);
       f >> height;
       ignoreComment(f);
       f >> grayLevel;

        if(width < 1 || height < 1 || grayLevel < 0 || grayLevel >255){
            cout<<"Cannot get the size or gray level"<<endl;
            f.close();
            return;
        }

        //allocate data size
        if( type==5){
            wholeLength=width*height;
            cout<<"width="<<width<<" height:"<<height<<endl;

        }
        if(type==6){
            wholeLength=3*width*height;
            cout<<"width="<<width<<" height:"<<height<<endl;
        }


        if(type==5 || type ==6){
            //dataByte=new GLubyte[wholeLength];
            dataByte=new unsigned char[wholeLength];
            //f.read((char*)&dataByte[0], wholeLength);
            //GLubyte j;
            int w;
            if(type==5)
                w=width;
            if(type==6)
                w=3*width;
            int count=0;int counth=0;
            for(int i=height;i>0;i--){
                for(int k=0;k<w;k++){
                    //j=(GLubyte)f.get();
                    //dataByte[(i-1)*w+k]=j;
                    f.read((char*)&dataByte[(i-1)*w+k],sizeof(unsigned char));

                    count++;
                }
                cout<<"count:"<<count<<endl;
                count=0;
                counth++;

            }
            cout<<"counth:"<<counth<<endl;
        }
        f.close();
    }





    int main(int argc, char *argv[] ) {

        //string n;
        //cout<<"Please enter the name of the image"<<endl;
        //cin>>n;
        readImage("small.ppm");
        glutInit(&argc,argv);
        glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB); 
        ori();
        getChoice();

        glutMainLoop();



        return 0;
    }

Here is my display functions. They can display PGM file and PPM file.

void drawAsType(char * d){

    if(type==5){
        glDrawPixels(width,height,GL_LUMINANCE,GL_UNSIGNED_BYTE,d);
        glFlush();
        glutSwapBuffers();
    }
    if(type==6 ){
        glDrawPixels(width,height,GL_RGB,GL_UNSIGNED_BYTE,d);
        glFlush();
        glutSwapBuffers();
    }   
}

void changeSize(int w,int h){
    glViewport(0,0,w,h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluOrtho2D(0.0,(GLfloat)w,0.0,(GLfloat)h);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}


void display(void){
    glClear(GL_COLOR_BUFFER_BIT);

    glRasterPos2i(0,0);


    glColor3f(1.0f,1.0f,1.0f);

    drawAsType(dataByte);

}

void ori(){

    glutInitWindowSize(width,height);
    glutCreateWindow("Origin");
    glutReshapeFunc(changeSize);
    glutDisplayFunc(&display);


}

Can anyone help me of this?


Solution

  • Thanks haraldK's.

    I found the reason is because in the ignoreComent() function I use peek() to check whether the next char is '\n' or '\t' or '#'.

    void ignoreComment(ifstream &f){
        char buf[1024];
        char t;
        while ( t=f.peek(),t=='\n'|| t=='\r'){
            f.get();
        }
        if(t=='#'){
            f.getline(buf,1023);
        }
    
    }
    

    peek() function returns the next character in the input sequence, without extracting it: The character is left as the next character to be extracted from the stream.--form cplusplus.com

    So after the last time peek() is called to check the line of garyLevel.

    ignoreComment(f);
    

    Since the the first char of the garyLevel is '2', because '2' should not be ignore, so the program executes the next line f >> grayLevel

    Then the read image data part is executed. But the first char in stream is '2' which was from peek(). So the first char of my array dataByte[] will be 2 instead of the real first char of the image block.

    The solution is to replace the '2'. So I should use f.get() again before I start to read the RGB data.

    So just add f.get() before the two for loops

    if(type==5 || type ==6){
    
            dataByte=new  char[wholeLength];
    
    
            f.get();    /*ADD THIS GUY!*/
    
            int w;
            if(type==5)
                w=width;
            if(type==6)
                w=3*width;
            int count=0;int counth=0;
            char t;
    
            for(int i=height;i>0;i--){
                for(int k=0;k<w;k++){
    
                 //read image RGB data
                }
             }