/* orxonox - the future of 3D-vertical-scrollers Copyright (C) 2004 orx This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. ### File Specific: main-programmer: Benjamin Grauer co-programmer: ... TGA-code: borrowed from nehe-Tutorials */ #define DEBUG_SPECIAL_MODULE DEBUG_MODULE_IMPORTER #include "texture.h" // headers only for PathList #include #include #include #include #include /** \brief creates a ned PathList. It is a good idea to use this as an initial List, because if you give on a name the Path will not be checked for its existence. */ PathList::PathList() { this->pathName = NULL; this->next = NULL; } /** \brief Creates a new PathList with a Name. \param pName the Name of The Path. This function just adds the Path without checking if it exists. */ PathList::PathList(char* pName) { this->pathName = new char [strlen(pName)+1]; strcpy (this->pathName, pName); this->next = NULL; } /** \brief destroys a PathList It does this by deleting the Name and then delete its preceding PathList. */ PathList::~PathList() { if (this->pathName) delete []this->pathName; if (this->next) delete this->next; } PathList* PathList::firstPath = NULL; /** \returns A Pointer to the first Path of the Pathlist */ PathList* PathList::getInstance(void) { if (firstPath) return firstPath; firstPath = new PathList(); } /** \brief Adds a new Pathlist Element. \param pName Adding a Path automatically checks if the Path exists, and if it does not it will not add it to the List. */ void PathList::addPath (char* pName) { if (pName[0] == '\0') { PRINTF(2)("not Adding empty Path to the List.\n"); return; } char* tmpPName = new char[strlen(pName)]; strncpy(tmpPName, pName, strlen(pName)-1); tmpPName[strlen(pName)-1] = '\0'; if (access (tmpPName, F_OK) == 0) { struct stat status; stat(tmpPName, &status); if (status.st_mode & S_IFDIR) { PRINTF(4)("Adding Path %s to the PathList.\n", pName); PathList* tmpPathList = this; while (tmpPathList->next) tmpPathList = tmpPathList->next; tmpPathList->next = new PathList(pName); } else PRINTF(2)("You tried to add non-folder %s to a PathList.\n", tmpPName); } else PRINTF(2)("You tried to add non-existing folder %s to a PathList.\n", tmpPName); delete []tmpPName; } /** \brief Constructor for a Texture */ Texture::Texture(void) { this->pImage = new Image; this->pImage->data = NULL; this->map = NULL; this->texture = 0; } /** \brief Destructor of a Texture Frees Data, and deletes the textures from GL */ Texture::~Texture(void) { if (this->pImage->data) delete []this->pImage->data; delete pImage; if (this->texture) glDeleteTextures(1, &this->texture); } /** \brief Searches for a Texture inside one of the defined Paths \param texName The name of the texture o search for. \returns pathName+texName if texName was found in the pathList. NULL if the Texture is not found. */ char* Texture::searchTextureInPaths(char* texName) const { char* tmpName = NULL; PathList* pList = PathList::getInstance(); while (pList) { if (pList->pathName) { tmpName = new char [strlen(pList->pathName)+strlen(texName)+1]; strcpy(tmpName, pList->pathName); } else { tmpName = new char [strlen(texName)+1]; tmpName[0]='\0'; } strcat(tmpName, texName); if (access (tmpName, F_OK) == 0) return tmpName; if (tmpName) delete []tmpName; tmpName = NULL; pList = pList->next; } return NULL; } /** \brief a Simple function that switches two char values \param a The first value \param b The second value */ inline void Texture::swap (unsigned char &a, unsigned char &b) { unsigned char temp; temp = a; a = b; b = temp; } /** \brief Loads a Texture to the openGL-environment. \param pImage The Image to load to openGL */ bool Texture::loadTexToGL (Image* pImage) { PRINTF(4)("Loading texture to OpenGL-Environment.\n"); glGenTextures(1, &this->texture); glBindTexture(GL_TEXTURE_2D, this->texture); /* not Working, and not needed. glTexImage2D( GL_TEXTURE_2D, 0, 3, width, height, 0, GL_BGR, GL_UNSIGNED_BYTE, map->pixels ); */ gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pImage->width, pImage->height, pImage->format, GL_UNSIGNED_BYTE, pImage->data); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR); } #ifdef HAVE_SDL_SDL_IMAGE_H bool Texture::loadImage(char* imageName) { char* imgNameWithPath = searchTextureInPaths(imageName); if (imgNameWithPath) { this->map=IMG_Load(imgNameWithPath); if(!map) { PRINTF(1)("IMG_Load: %s\n", IMG_GetError()); return false; } pImage->height = map->h; pImage->width = map->w; pImage->data = (GLubyte*)map->pixels; pImage->bpp = map->format->BytesPerPixel; if (pImage->bpp == 3) pImage->format = GL_RGB; else if (pImage->bpp == 4) pImage->format = GL_RGBA; if( !IMG_isPNG(SDL_RWFromFile(imgNameWithPath, "rb")) && !IMG_isJPG(SDL_RWFromFile(imgNameWithPath, "rb"))) for (int i=0;ih * map->w *3;i+=3) { GLuint temp = pImage->data[i]; pImage->data[i] = pImage->data[i+2]; pImage->data[i+2] = temp; } /* this is the real swapping algorithm */ for( int i = 0 ; i < (pImage->height / 2) ; ++i ) for( int j = 0 ; j < pImage->width * pImage->bpp; j += pImage->bpp ) for(int k = 0; k < pImage->bpp; ++k) swap( pImage->data[ (i * pImage->width * pImage->bpp) + j + k], pImage->data[ ( (pImage->height - i - 1) * pImage->width * pImage->bpp ) + j + k]); this->loadTexToGL (this->pImage); SDL_FreeSurface(map); this->pImage->data = NULL; } else { PRINTF(2)("Image not Found: %s\n", imgNameWithPath); return false; } } #else /* HAVE_SDL_SDL_IMAGE_H */ /** \brief Makes the Programm ready to Read-in a texture-File 1. Checks what type of Image should be imported \todo Checks where to find the Image */ bool Texture::loadImage(char* imageName) { char* imgNameWithPath = searchTextureInPaths(imageName); if (imgNameWithPath) { if (!strncmp(imgNameWithPath+strlen(imgNameWithPath)-4, ".bmp", 4)) { PRINTF(4)("Requested bmp-image. Trying to Import.\n"); return this->loadBMP(imgNameWithPath); } else if (!strncmp(imgNameWithPath+strlen(imgNameWithPath)-4, ".jpg", 4) || !strncmp(imgNameWithPath+strlen(imgNameWithPath)-5, ".jpg", 5)) { PRINTF(4)("Requested jpeg-image. Trying to Import\n"); return this->loadJPG(imgNameWithPath); } else if (!strncmp(imgNameWithPath+strlen(imgNameWithPath)-4, ".tga", 4)) { PRINTF(4)("Requested tga-image. Trying to Import\n"); return this->loadTGA(imgNameWithPath); } else if (!strncmp(imgNameWithPath+strlen(imgNameWithPath)-4, ".png", 4)) { PRINTF(4)("Requested png-image. Trying to Import\n"); return this->loadPNG(imgNameWithPath); } else { PRINTF(2)("Requested Image was not recognized in its type. (Maybe a type-Cast-error.)\n FileName: %s", imgNameWithPath); return false; } } else { PRINTF(2)("Image not Found: %s\n", imgNameWithPath); return false; } } /** \brief reads in a Windows BMP-file, and imports it to openGL. \param bmpName The name of the Image to load. */ bool Texture::loadBMP (char* bmpName) { 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) GLuint temp; // temporary color storage for bgr-rgb conversion. // make sure the file is there. if ((file = fopen(bmpName, "rb"))==NULL) { PRINTF(2)("File Not Found : %s\n",bmpName); return false; } // seek through the bmp header, up to the width/height: fseek(file, 18, SEEK_CUR); // read the width if ((i = fread(&pImage->width, 4, 1, file)) != 1) { PRINTF(2)("Error reading width from %s.\n", bmpName); return false; } // read the height if ((i = fread(&pImage->height, 4, 1, file)) != 1) { PRINTF(2)("Error reading height from %s.\n", bmpName); return false; } // calculate the size (assuming 24 bits or 3 bytes per pixel). size = pImage->width * pImage->height * 3; // read the planes if ((fread(&planes, 2, 1, file)) != 1) { PRINTF(2)("Error reading planes from %s.\n", bmpName); return false; } if (planes != 1) { PRINTF(1)("Planes from %s is not 1: %u\n", bmpName, planes); return false; } // read the bpp if ((i = fread(&bpp, 2, 1, file)) != 1) { PRINTF(2)("Error reading bpp from %s.\n", bmpName); return false; } if (bpp != 24) { PRINTF(2)("Bpp from %s is not 24: %u\n", bmpName, bpp); return false; } // seek past the rest of the bitmap header. fseek(file, 24, SEEK_CUR); // read the data. pImage->data = (GLubyte *) malloc(size); if (pImage->data == NULL) { PRINTF(2)("Error allocating memory for color-corrected image data"); return false; } if ((i = fread(pImage->data, size, 1, file)) != 1) { PRINTF(2)("Error reading image data from %s.\n", bmpName); return false; } fclose(file); // reverse all of the colors. (bgr -> rgb) for (i=0;idata[i]; pImage->data[i] = pImage->data[i+2]; pImage->data[i+2] = temp; } this->loadTexToGL (pImage); if (pImage) { if (pImage->data) { free(pImage->data); } free(pImage); } return true; } /** \brief reads in a jpg-file \param jpgName the Name of the Image to load */ bool Texture::loadJPG (char* jpgName) { #ifdef HAVE_JPEGLIB_H struct jpeg_decompress_struct cinfo; Image *pImage = NULL; FILE *pFile; // Open a file pointer to the jpeg file and check if it was found and opened if((pFile = fopen(jpgName, "rb")) == NULL) { // Display an error message saying the file was not found, then return NULL PRINTF(2)("Unable to load JPG File %s.\n", jpgName); return false; } // Create an error handler jpeg_error_mgr jerr; // Have our compression info object point to the error handler address cinfo.err = jpeg_std_error(&jerr); // Initialize the decompression object jpeg_create_decompress(&cinfo); // Specify the data source (Our file pointer) jpeg_stdio_src(&cinfo, pFile); // Allocate the structure that will hold our eventual jpeg data (must free it!) pImage = (Image*)malloc(sizeof(Image)); // DECOFING // Read in the header of the jpeg file jpeg_read_header(&cinfo, TRUE); // Start to decompress the jpeg file with our compression info jpeg_start_decompress(&cinfo); // Get the image dimensions and row span to read in the pixel data pImage->rowSpan = cinfo.image_width * cinfo.num_components; pImage->width = cinfo.image_width; pImage->height = cinfo.image_height; // Allocate memory for the pixel buffer pImage->data = new unsigned char[pImage->rowSpan * pImage->height]; // Here we use the library's state variable cinfo.output_scanline as the // loop counter, so that we don't have to keep track ourselves. // Create an array of row pointers unsigned char** rowPtr = new unsigned char*[pImage->height]; for (int i = 0; i < pImage->height; i++) rowPtr[i] = &(pImage->data[i*pImage->rowSpan]); // Now comes the juice of our work, here we extract all the pixel data int rowsRead = 0; while (cinfo.output_scanline < cinfo.output_height) { // Read in the current row of pixels and increase the rowsRead count rowsRead += jpeg_read_scanlines(&cinfo, &rowPtr[rowsRead], cinfo.output_height - rowsRead); } // Delete the temporary row pointers delete [] rowPtr; // Finish decompressing the data jpeg_finish_decompress(&cinfo);// decodeJPG(&cinfo, pImage); // This releases all the stored memory for reading and decoding the jpeg jpeg_destroy_decompress(&cinfo); // Close the file pointer that opened the file fclose(pFile); if(pImage == NULL) exit(0); this->loadTexToGL (pImage); if (pImage) { if (pImage->data) { free(pImage->data); } free(pImage); } return true; #else /* HAVE_JPEGLIB_H */ PRINTF(1)("sorry, but you did not compile with jpeg-support.\nEither install SDL_image or jpeglib, and recompile to see the image\n"); return false; #endif /* HAVE_JPEGLIB_H */ } /** \brief reads in a tga-file \param tgaName the Name of the Image to load */ bool Texture::loadTGA(const char * tgaName) { typedef struct { GLubyte Header[12]; } TGAHeader; TGAHeader tgaHeader; GLubyte uTGAcompare[12] = {0,0,2, 0,0,0,0,0,0,0,0,0}; // Uncompressed TGA Header GLubyte cTGAcompare[12] = {0,0,10,0,0,0,0,0,0,0,0,0}; // Compressed TGA Header FILE * fTGA; fTGA = fopen(tgaName, "rb"); if(fTGA == NULL) { PRINTF(2)("Error could not open texture file: %s\n", tgaName); return false; } if(fread(&tgaHeader, sizeof(TGAHeader), 1, fTGA) == 0) { PRINTF(2)("Error could not read file header of %s\n", tgaName); if(fTGA != NULL) { fclose(fTGA); } return false; } if(memcmp(uTGAcompare, &tgaHeader, sizeof(TGAHeader)) == 0) { loadUncompressedTGA(tgaName, fTGA); if (fTGA) fclose (fTGA); } else if(memcmp(cTGAcompare, &tgaHeader, sizeof(TGAHeader)) == 0) { loadCompressedTGA(tgaName, fTGA); if (fTGA) fclose (fTGA); } else { PRINTF(2)("Error TGA file be type 2 or type 10\n"); if (fTGA) fclose(fTGA); return false; } return true; } /** \brief reads in an uncompressed tga-file \param filename the Name of the Image to load \param fTGA a Pointer to a File, that should be read */ bool Texture::loadUncompressedTGA(const char * filename, FILE * fTGA) { GLubyte header[6]; // First 6 Useful Bytes From The Header GLuint bytesPerPixel; // Holds Number Of Bytes Per Pixel Used In The TGA File GLuint imageSize; // Used To Store The Image Size When Setting Aside Ram GLuint temp; // Temporary Variable GLuint type; GLuint Height; // Height of Image GLuint Width; // Width of Image GLuint Bpp; // Bits Per Pixel GLuint cswap; if(fread(header, sizeof(header), 1, fTGA) == 0) { PRINTF(2)("Error could not read info header\n"); return false; } Width = pImage->width = header[1] * 256 + header[0]; Height = pImage->height = header[3] * 256 + header[2]; Bpp = pImage->bpp = header[4]; // Make sure all information is valid if((pImage->width <= 0) || (pImage->height <= 0) || ((pImage->bpp != 24) && (pImage->bpp !=32))) { PRINTF(2)("Error invalid texture information\n"); return false; } if(pImage->bpp == 24) { pImage->type = GL_RGB; } else { pImage->type = GL_RGBA; } bytesPerPixel = (Bpp / 8); imageSize = (bytesPerPixel * Width * Height); pImage->data = (GLubyte*) malloc(imageSize); if(pImage->data == NULL) { PRINTF(2)("Error could not allocate memory for image\n"); return false; } if(fread(pImage->data, 1, imageSize, fTGA) != imageSize) { PRINTF(2)("Error could not read image data\n"); if(pImage->data != NULL) { free(pImage->data); } return false; } for(cswap = 0; cswap < (int)imageSize; cswap += bytesPerPixel) { pImage->data[cswap] ^= pImage->data[cswap+2] ^= pImage->data[cswap] ^= pImage->data[cswap+2]; } this->loadTexToGL (pImage); return true; } /** \brief reads in a compressed tga-file \param filename the Name of the Image to load \param fTGA a Pointer to a File, that should be read */ bool Texture::loadCompressedTGA(const char * filename, FILE * fTGA) { GLubyte header[6]; // First 6 Useful Bytes From The Header GLuint bytesPerPixel; // Holds Number Of Bytes Per Pixel Used In The TGA File GLuint imageSize; // Used To Store The Image Size When Setting Aside Ram GLuint temp; // Temporary Variable GLuint type; GLuint Height; // Height of Image GLuint Width; // Width of Image GLuint Bpp; // Bits Per Pixel if(fread(header, sizeof(header), 1, fTGA) == 0) { PRINTF(2)("Error could not read info header\n"); return false; } Width = pImage->width = header[1] * 256 + header[0]; Height = pImage->height = header[3] * 256 + header[2]; Bpp = pImage->bpp = header[4]; GLuint pixelcount = Height * Width; GLuint currentpixel = 0; GLuint currentbyte = 0; GLubyte * colorbuffer = (GLubyte *)malloc(bytesPerPixel); //Make sure all pImage info is ok if((pImage->width <= 0) || (pImage->height <= 0) || ((pImage->bpp != 24) && (pImage->bpp !=32))) { PRINTF(2)("Error Invalid pImage information\n"); return false; } bytesPerPixel = (Bpp / 8); imageSize = (bytesPerPixel * Width * Height); pImage->data = (GLubyte*) malloc(imageSize); if(pImage->data == NULL) { PRINTF(2)("Error could not allocate memory for image\n"); return false; } do { GLubyte chunkheader = 0; if(fread(&chunkheader, sizeof(GLubyte), 1, fTGA) == 0) { PRINTF(2)("Error could not read RLE header\n"); if(pImage->data != NULL) { free(pImage->data); } return false; } // If the ehader is < 128, it means the that is the number of RAW color packets minus 1 if(chunkheader < 128) { short counter; chunkheader++; // Read RAW color values for(counter = 0; counter < chunkheader; counter++) { // Try to read 1 pixel if(fread(colorbuffer, 1, bytesPerPixel, fTGA) != bytesPerPixel) { PRINTF(2)("Error could not read image data\n"); if(colorbuffer != NULL) { free(colorbuffer); } if(pImage->data != NULL) { free(pImage->data); } return false; } // write to memory // Flip R and B vcolor values around in the process pImage->data[currentbyte ] = colorbuffer[2]; pImage->data[currentbyte + 1] = colorbuffer[1]; pImage->data[currentbyte + 2] = colorbuffer[0]; if(bytesPerPixel == 4) // if its a 32 bpp image { pImage->data[currentbyte + 3] = colorbuffer[3];// copy the 4th byte } currentbyte += bytesPerPixel; currentpixel++; // Make sure we haven't read too many pixels if(currentpixel > pixelcount) { PRINTF(2)("Error too many pixels read\n"); if(colorbuffer != NULL) { free(colorbuffer); } if(pImage->data != NULL) { free(pImage->data); } return false; } } } // chunkheader > 128 RLE data, next color reapeated chunkheader - 127 times else { short counter; chunkheader -= 127; // Subteact 127 to get rid of the ID bit if(fread(colorbuffer, 1, bytesPerPixel, fTGA) != bytesPerPixel) // Attempt to read following color values { PRINTF(2)("Error could not read from file"); if(colorbuffer != NULL) { free(colorbuffer); } if(pImage->data != NULL) { free(pImage->data); } return false; } for(counter = 0; counter < chunkheader; counter++) //copy the color into the image data as many times as dictated { // switch R and B bytes areound while copying pImage->data[currentbyte ] = colorbuffer[2]; pImage->data[currentbyte + 1] = colorbuffer[1]; pImage->data[currentbyte + 2] = colorbuffer[0]; if(bytesPerPixel == 4) { pImage->data[currentbyte + 3] = colorbuffer[3]; } currentbyte += bytesPerPixel; currentpixel++; if(currentpixel > pixelcount) { PRINTF(2)("Error too many pixels read\n"); if(colorbuffer != NULL) { free(colorbuffer); } if(pImage->data != NULL) { free(pImage->data); } return false; } } } } while(currentpixel < pixelcount); // Loop while there are still pixels left this->loadTexToGL (pImage); return true; } /** \brief reads in a png-file \param pngName the Name of the Image to load */ bool Texture::loadPNG(const char* pngName) { #ifdef HAVE_PNG_H FILE *PNG_file = fopen(pngName, "rb"); if (PNG_file == NULL) { return 0; } GLubyte PNG_header[8]; fread(PNG_header, 1, 8, PNG_file); if (png_sig_cmp(PNG_header, 0, 8) != 0) { PRINTF(2)("Not Recognized as a pngFile\n"); fclose (PNG_file); return 0; } png_structp PNG_reader = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (PNG_reader == NULL) { fclose(PNG_file); return 0; } png_infop PNG_info = png_create_info_struct(PNG_reader); if (PNG_info == NULL) { png_destroy_read_struct(&PNG_reader, NULL, NULL); fclose(PNG_file); return 0; } png_infop PNG_end_info = png_create_info_struct(PNG_reader); if (PNG_end_info == NULL) { png_destroy_read_struct(&PNG_reader, &PNG_info, NULL); fclose(PNG_file); return 0; } if (setjmp(png_jmpbuf(PNG_reader))) { png_destroy_read_struct(&PNG_reader, &PNG_info, &PNG_end_info); fclose(PNG_file); return (0); } png_init_io(PNG_reader, PNG_file); png_set_sig_bytes(PNG_reader, 8); png_read_info(PNG_reader, PNG_info); pImage->width = png_get_image_width(PNG_reader, PNG_info); pImage->height = png_get_image_height(PNG_reader, PNG_info); png_uint_32 bit_depth, color_type; bit_depth = png_get_bit_depth(PNG_reader, PNG_info); color_type = png_get_color_type(PNG_reader, PNG_info); if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(PNG_reader); } if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_gray_1_2_4_to_8(PNG_reader); } if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(PNG_reader); } if (png_get_valid(PNG_reader, PNG_info, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(PNG_reader); } else { png_set_filler(PNG_reader, 0xff, PNG_FILLER_AFTER); } if (bit_depth == 16) { png_set_strip_16(PNG_reader); } png_read_update_info(PNG_reader, PNG_info); pImage->data = (png_byte*)malloc(4 * pImage->width * pImage->height); png_byte** PNG_rows = (png_byte**)malloc(pImage->height * sizeof(png_byte*)); unsigned int row; for (row = 0; row < pImage->height; ++row) { PNG_rows[pImage->height - 1 - row] = pImage->data + (row * 4 * pImage->width); } png_read_image(PNG_reader, PNG_rows); free(PNG_rows); png_destroy_read_struct(&PNG_reader, &PNG_info, &PNG_end_info); fclose(PNG_file); /* if (!ST_is_power_of_two(pImage->width) || !ST_is_power_of_two(pImage->height)) { free(pImage->data); return 0; } */ this->loadTexToGL (pImage); free(pImage->data); return true; #else /* HAVE_PNG_H */ PRINTF(1)("sorry, but you did not compile with png-support.\nEither install SDL_image or libpng, and recompile to see the image\n"); return false; #endif /* HAVE_PNG_H */ } #endif /* HAVE_SDL_SDL_IMAGE_H */