/************************************************************************* Stereo pair image viewer by Aidan Crees Usage: viewer leftimage.bmp rightimage.bmp [options] Accepts 24bit bitmaps as arguments Written by Aidan Crees, completed 6/2/04 -Draw text function from opengllib.c (by Paul Bourke) -Bitmap loading function from http://nehe.gamedev.net, lesson 7 -TGA loading function from http://nehe.gamedev.net, lesson 33 Everything else written by Aidan Crees Originally written under Linux, ported to Windows by changing TGA function types from 'bool' to 'int', and returning 'true' to '1' and 'false' to '0'. Holds up to 20 images in memory. *************************************************************************/ #include #include #include #include #include #include "Tga.h" #define NOSTEREO 0 #define DUALSTEREO 1 #define ESC 27 #define SPACE 32 #define BITMAP 0 #define TARGA 1 #define LIST 3 GLenum fullscreen=0, info=0, cursor=0, screen=0, DisplayListContents=0,DisplayHelp=0; char *file1[20]; //array to hold file names (for printing to screen) char fileArray[20][100];//array to read in file names char FileType[20]; //array to hold file type (BITMAP or TARGA) int imagetype,next=0,count=0; typedef struct { int stereo; int screenwidth; int screenheight; } CAMERA; struct Image { unsigned long sizeX; unsigned long sizeY; char *data; }; typedef struct Image Image; Image image1[20]; //holds bitmaps CAMERA camera; Texture texture[20]; //holds targa /******************************************************** Load a 24bit bitmap does not have to be sqaure anymore! 24bit bitmaps only 1 plane only /*******************************************************/ // quick and dirty bitmap loader...for 24 bit bitmaps with 1 plane only. // See http://www.dcs.ed.ac.uk/~mxr/gfx/2d/BMP.txt for more info. // if mesa ever gets glaux, let me know. int ImageLoad(char *filename, Image *image) { FILE *file; unsigned long size; // size of the image in bytes. unsigned long i; // standard counter. unsigned short int planes; // number of planes in image (must be 1) unsigned short int bpp; // number of bits per pixel (must be 24) char temp; // used to convert bgr to rgb color. // ensure the file is there. if ((file = fopen(filename, "rb"))==NULL) { printf("File Not Found : %s\n",filename); return 0; } // seek through the bmp header, up to the width/height: fseek(file, 18, SEEK_CUR); // read the width if ((i = fread(&image->sizeX, 4, 1, file)) != 1) { printf("Error reading width from %s.\n", filename); return 0; } printf("Width of %s: %lu\n", filename, image->sizeX); // read the height if ((i = fread(&image->sizeY, 4, 1, file)) != 1) { printf("Error reading height from %s.\n", filename); return 0; } printf("Height of %s: %lu\n", filename, image->sizeY); // calculate the size (assuming 24 bits or 3 bytes per pixel). size = image->sizeX * image->sizeY * 3; // read the planes if ((fread(&planes, 2, 1, file)) != 1) { printf("Error reading planes from %s.\n", filename); return 0; } if (planes != 1) { printf("Planes from %s is not 1: %u\n", filename, planes); return 0; } // read the bpp if ((i = fread(&bpp, 2, 1, file)) != 1) { printf("Error reading bpp from %s.\n", filename); return 0; } if (bpp != 24) { printf("Bpp from %s is not 24: %u\n", filename, bpp); return 0; } // seek past the rest of the bitmap header. fseek(file, 24, SEEK_CUR); // read the data. image->data = (char *) malloc(size); if (image->data == NULL) { printf("Error allocating memory for color-corrected image data"); return 0; } if ((i = fread(image->data, size, 1, file)) != 1) { printf("Error reading image data from %s.\n", filename); return 0; } for (i=0;i rgb) temp = image->data[i]; image->data[i] = image->data[i+2]; image->data[i+2] = temp; } // we're done. return 1; } /******************************************************** Load a TGA file /*******************************************************/ BOOL LoadTGA(Texture * texture,const char * filename) /* Load a TGA file */ { FILE * fTGA; /* File pointer to texture file */ fTGA = fopen(filename, "rb"); /* Open file for reading */ if(fTGA == NULL) /* If it didn't open.... */ { printf("Error could not open %s", filename); /* Display an error message */ return False; /* Exit function */ } if(fread(&tgaheader, sizeof(TGAHeader), 1, fTGA) == 0) /* Attempt to read 12 byte header from file */ { printf("Error could not read file header"); /* If it fails, display an error message */ if(fTGA != NULL) /* Check to seeiffile is still open */ { fclose(fTGA); /* If it is, close it */ } return False; /* Exit function */ } if(memcmp(uTGAcompare, &tgaheader, sizeof(tgaheader)) == 0) /* See if header matches the predefined header of */ { /* an Uncompressed TGA image */ LoadUncompressedTGA(texture, filename, fTGA); /* If so, jump to Uncompressed TGA loading code */ } else if(memcmp(cTGAcompare, &tgaheader, sizeof(tgaheader)) == 0) /* See if header matches the predefined header of */ { /* an RLE compressed TGA image */ LoadCompressedTGA(texture, filename, fTGA); /* If so, jump to Compressed TGA loading code */ } else /* If header matches neither type */ { printf("Error TGA file be type 2 or type 10 "); /* Display an error */ fclose(fTGA); return False; /* Exit function */ } return True; /* All went well, continue on */ } BOOL LoadUncompressedTGA(Texture * texture, const char * filename, FILE * fTGA) /* Load an uncompressed TGA (note, much of this code is based on NeHe's */ { GLuint cswap; /* TGA Loading code nehe.gamedev.net) */ if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0) /* Read TGA header */ { printf("Error could not read info header"); /* Display error */ if(fTGA != NULL) /* if file is still open */ { fclose(fTGA); /* Close it */ } return False; /* Return failular */ } texture->width = tga.header[1] * 256 + tga.header[0]; /* Determine The TGA Width (highbyte*256+lowbyte) */ texture->height = tga.header[3] * 256 + tga.header[2]; /* Determine The TGA Height (highbyte*256+lowbyte) */ texture->bpp = tga.header[4]; /* Determine the bits per pixel */ tga.Width = texture->width; /* Copy width into local structure */ tga.Height = texture->height; /* Copy height into local structure */ tga.Bpp = texture->bpp; /* Copy BPP into local structure */ if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32))) /* Make sure all information is valid */ { printf("Error invalid texture information"); /* Display Error */ if(fTGA != NULL) /* Check if file is still open */ { fclose(fTGA); /* If so, close it */ } return False; /* Return failed */ } if(texture->bpp == 24) /* If the BPP of the image is 24... */ { texture->type = GL_RGB; /* Set Image type to GL_RGB */ } else /* Else if its 32 BPP */ { texture->type = GL_RGBA; /* Set image type to GL_RGBA */ } tga.bytesPerPixel = (tga.Bpp / 8); /* Compute the number of BYTES per pixel */ tga.imageSize = (tga.bytesPerPixel * tga.Width * tga.Height); /* Compute the total amout ofmemory needed to store data */ texture->imageData = malloc(tga.imageSize); /* Allocate that much memory */ if(texture->imageData == NULL) /* If no space was allocated */ { printf("Error could not allocate memory for image"); /* Display Error */ fclose(fTGA); /* Close the file */ return False; /* Return failed */ } if(fread(texture->imageData, 1, tga.imageSize, fTGA) != tga.imageSize) /* Attempt to read image data */ { printf("Error could not read image data"); /* Display Error */ if(texture->imageData != NULL) /* If imagedata has data in it */ { free(texture->imageData); /* Delete data from memory */ } fclose(fTGA); /* Close file */ return False; /* Return failed */ } /* Byte Swapping Optimized By Steve Thomas */ for(cswap = 0; cswap < (int)tga.imageSize; cswap += tga.bytesPerPixel) { texture->imageData[cswap] ^= texture->imageData[cswap+2] ^= texture->imageData[cswap] ^= texture->imageData[cswap+2]; } fclose(fTGA); /* Close file */ return True; /* Return success */ } BOOL LoadCompressedTGA(Texture * texture,const char * filename, FILE * fTGA) /* Load COMPRESSED TGAs */ { GLuint pixelcount = tga.Height * tga.Width; /* Nuber of pixels in the image */ GLuint currentpixel = 0; /* Current pixel being read */ GLuint currentbyte = 0; /* Current byte */ GLubyte * colorbuffer = (GLubyte *)malloc(tga.bytesPerPixel); /* Storage for 1 pixel */ if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0) /* Attempt to read header */ { printf("Error could not read info header"); /* Display Error */ if(fTGA != NULL) /* If file is open */ { fclose(fTGA); /* Close it */ } return False; /* Return failed */ } texture->width = tga.header[1] * 256 + tga.header[0]; /* Determine The TGA Width (highbyte*256+lowbyte) */ texture->height = tga.header[3] * 256 + tga.header[2]; /* Determine The TGA Height (highbyte*256+lowbyte) */ texture->bpp = tga.header[4]; /* Determine Bits Per Pixel */ tga.Width = texture->width; /* Copy width to local structure */ tga.Height = texture->height; /* Copy width to local structure */ tga.Bpp = texture->bpp; /* Copy width to local structure */ if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32))) /*Make sure all texture info is ok */ { printf("Error Invalid texture information"); /* If it isnt...Display error */ if(fTGA != NULL) /* Check if file is open */ { fclose(fTGA); /* Ifit is, close it */ } return False; /* Return failed */ } tga.bytesPerPixel = (tga.Bpp / 8); /* Compute BYTES per pixel */ tga.imageSize = (tga.bytesPerPixel * tga.Width * tga.Height); /* Compute amout of memory needed to store image */ texture->imageData = (GLubyte *)malloc(tga.imageSize); /* Allocate that much memory */ if(texture->imageData == NULL) /* If it wasnt allocated correctly.. */ { printf("Error could not allocate memory for image"); /* Display Error */ fclose(fTGA); /* Close file */ return False; /* Return failed */ } do { GLubyte chunkheader = 0; /* Storage for "chunk" header */ if(fread(&chunkheader, sizeof(GLubyte), 1, fTGA) == 0) /* Read in the 1 byte header */ { printf("Error could not read RLE header"); /*Display Error */ if(fTGA != NULL) /* If file is open */ { fclose(fTGA); /* Close file */ } if(texture->imageData != NULL) /* If there is stored image data */ { free(texture->imageData); /* Delete image data */ } return False; /* Return failed */ } if(chunkheader < 128) /* If the ehader is < 128, it means the that is the number of RAW color packets minus 1 */ { short counter; /* that follow the header */ chunkheader++; /* add 1 to get number of following color values */ for(counter = 0; counter < chunkheader; counter++) /* Read RAW color values */ { if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel) /* Try to read 1 pixel */ { printf("Error could not read image data"); /* IF we cant, display an error */ if(fTGA != NULL) /* See if file is open */ { fclose(fTGA); /* If so, close file */ } if(colorbuffer != NULL) /* See if colorbuffer has data in it */ { free(colorbuffer); /* If so, delete it */ } if(texture->imageData != NULL) /* See if there is stored Image data */ { free(texture->imageData); /* If so, delete it too */ } return False; /* Return failed */ } /* write to memory */ texture->imageData[currentbyte ] = colorbuffer[2]; /* Flip R and B vcolor values around in the process */ texture->imageData[currentbyte + 1 ] = colorbuffer[1]; texture->imageData[currentbyte + 2 ] = colorbuffer[0]; if(tga.bytesPerPixel == 4) /* if its a 32 bpp image */ { texture->imageData[currentbyte + 3] = colorbuffer[3]; /* copy the 4th byte */ } currentbyte += tga.bytesPerPixel; /* Increase thecurrent byte by the number of bytes per pixel */ currentpixel++; /* Increase current pixel by 1 */ if(currentpixel > pixelcount) /* Make sure we havent read too many pixels */ { printf("Error too many pixels read"); /* if there is too many... Display an error! */ if(fTGA != NULL) /* If there is a file open */ { fclose(fTGA); /* Close file */ } if(colorbuffer != NULL) /* If there is data in colorbuffer */ { free(colorbuffer); /* Delete it */ } if(texture->imageData != NULL) /* If there is Image data */ { free(texture->imageData); /* delete it */ } return False; /* Return failed */ } } } else /* chunkheader > 128 RLE data, next color reapeated chunkheader - 127 times */ { short counter; chunkheader -= 127; /* Subteact 127 to get rid of the ID bit */ if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel) /* Attempt to read following color values */ { printf("Error could not read from file"); /* If attempt fails.. Display error (again) */ if(fTGA != NULL) /* If thereis a file open */ { fclose(fTGA); /* Close it */ } if(colorbuffer != NULL) /* If there is data in the colorbuffer */ { free(colorbuffer); /* delete it */ } if(texture->imageData != NULL) /* If thereis image data */ { free(texture->imageData); /* delete it */ } return False; /* return failed */ } for(counter = 0; counter < chunkheader; counter++) /* copy the color into the image data as many times as dictated */ { /* by the header */ texture->imageData[currentbyte ] = colorbuffer[2]; /* switch R and B bytes areound while copying */ texture->imageData[currentbyte + 1 ] = colorbuffer[1]; texture->imageData[currentbyte + 2 ] = colorbuffer[0]; if(tga.bytesPerPixel == 4) /* If TGA images is 32 bpp */ { texture->imageData[currentbyte + 3] = colorbuffer[3]; /* Copy 4th byte */ } currentbyte += tga.bytesPerPixel; /* Increase current byte by the number of bytes per pixel */ currentpixel++; /* Increase pixel count by 1 */ if(currentpixel > pixelcount) /* Make sure we havent written too many pixels */ { printf("Error too many pixels read"); /* if there is too many... Display an error! */ if(fTGA != NULL) /* If there is a file open */ { fclose(fTGA); /* Close file */ } if(colorbuffer != NULL) /* If there is data in colorbuffer */ { free(colorbuffer); /* Delete it */ } if(texture->imageData != NULL) /* If there is Image data */ { free(texture->imageData); /* delete it */ } return False; /* Return failed */ } } } } while(currentpixel < pixelcount); /* Loop while there are still pixels left */ fclose(fTGA); /* Close the file */ return True; /* return success */ } /******************************************************** Handle a window reshape/resize ********************************************************/ void HandleReshape(int w,int h) { camera.screenwidth = w; //set new window width camera.screenheight = h; //set new window height glViewport(0,0,(GLsizei)w,(GLsizei)h); //resize the viewport } /******************************************************** Function to draw text ********************************************************/ void DrawGLText(int x,int y,char *s) { int lines; char *p; glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glOrtho(0,glutGet(GLUT_WINDOW_WIDTH),0,glutGet(GLUT_WINDOW_HEIGHT),-1,1); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glRasterPos2i(x,y); for (p=s,lines=0;*p;p++) { if (*p == '\n' || *p == '\r') { lines++; glRasterPos2i(x,y-(lines*13)); } else { glutBitmapCharacter(GLUT_BITMAP_8_BY_13,*p); } } glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } /******************************************************** Sets up viewports for normal/stereo viewing and draws the pixels of the image data ********************************************************/ void HandleDisplay(void) { int x,y; int i,j; FILE *pFile; char next_1[10],next_2[10],count_d[10],sizeX[10],sizeY[10]; //write number of files and current file displayed to file //this is so that they can be read back into an array //very bad way of doing things...another way?? i = next; pFile = fopen("data", "w+"); if (FileType[i]==BITMAP) fprintf(pFile, "%d %d %d %d %d", next+1, next+2, count, image1[i].sizeX, image1[i].sizeY); else if (FileType[i]==TARGA) fprintf(pFile, "%d %d %d %d %d", next+1, next+2, count, texture[i].width, texture[i].height); rewind(pFile); fscanf(pFile, "%s",next_1); fscanf(pFile, "%s",next_2); fscanf(pFile, "%s",count_d); fscanf(pFile, "%s",sizeX); fscanf(pFile, "%s",sizeY); fclose(pFile); //prevent from cycling into unused memory if(i>(count-1)) { i=0; next=0; } if(i<0) { i=count-2; next=count-2; } //clear buffers glDrawBuffer(GL_BACK); glClear(GL_COLOR_BUFFER_BIT); //set cursor visibility if(cursor==0) glutSetCursor(GLUT_CURSOR_NONE); else glutSetCursor(GLUT_CURSOR_CROSSHAIR); //left image projection glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (camera.stereo == DUALSTEREO) { gluOrtho2D(0,camera.screenwidth/2,0,camera.screenheight); glViewport(0,0,camera.screenwidth/2,camera.screenheight); } else { gluOrtho2D(0,camera.screenwidth,0,camera.screenheight); glViewport(0,0,camera.screenwidth,camera.screenheight); } glMatrixMode(GL_MODELVIEW); glDrawBuffer(GL_BACK_LEFT); glLoadIdentity(); glPushMatrix(); if(FileType[i]==TARGA) { //centre left TGA image if(DUALSTEREO) x = (camera.screenwidth/2 - texture[i].width)/2; else x = (camera.screenwidth - texture[i].width)/2; y = (camera.screenheight - texture[i].height)/2; glRasterPos2i(x,y); glDrawPixels(texture[i].width, texture[i].height, texture[i].type, GL_UNSIGNED_BYTE, texture[i].imageData); } else { //centre left BMP image if(DUALSTEREO) x = (camera.screenwidth/2 - image1[i].sizeX)/2; else x = (camera.screenwidth - image1[i].sizeX)/2; y = (camera.screenheight - image1[i].sizeY)/2; glRasterPos2i(x,y); glDrawPixels(image1[i].sizeX, image1[i].sizeY, GL_RGB, GL_UNSIGNED_BYTE, image1[i].data); } if(info) { glColor3f(1.0,0.0,0.0); DrawGLText(10,camera.screenheight-10, "Stereo Image Viewer by Aidan Crees"); DrawGLText(10,camera.screenheight-25, "Left image:"); DrawGLText(210,camera.screenheight-25, file1[i]); DrawGLText(10,camera.screenheight-40, "Width:"); DrawGLText(110,camera.screenheight-40, sizeX); DrawGLText(10,camera.screenheight-55, "Height:"); DrawGLText(125,camera.screenheight-55, sizeY); DrawGLText(10,10, "Image of "); DrawGLText(110,10, next_1); DrawGLText(195,10, count_d); } if(DisplayListContents) { glColor3f(1.0,0.0,0.0); DrawGLText(10,camera.screenheight-85, "Files loaded..."); for(j=0;j [OPTION]\n\n", argv[0]); fprintf (stderr, "Images must be either in BMP or TGA format!\n"); fprintf (stderr, "If the -l flag is set, enter instead of and \n\n"); fprintf (stderr, "Command line options\n"); fprintf (stderr, " -f fullscreen\n"); fprintf (stderr, " -ss dual screen stereo\n"); fprintf (stderr, " -l specify file list mode\n"); fprintf (stderr, "Program options\n"); fprintf (stderr, " left arrow previous image\n"); fprintf (stderr, " right arrow next image\n"); fprintf (stderr, " i toggle image information\n"); fprintf (stderr, " c toggle cursor visibility\n"); fprintf (stderr, " f toggle fullscreen\n"); fprintf (stderr, " l toggle image list display\n"); fprintf (stderr, " h display this screen\n"); fprintf (stderr, " q exit program\n"); exit(0); } for (i=1;i