src/Image.cpp

Go to the documentation of this file.
00001 
00008 /*
00009 This file is part of Teapot Colony Wars.
00010 
00011 Teapot Colony Wars is free software: you can redistribute it and/or modify
00012 it under the terms of the GNU General Public License as published by
00013 the Free Software Foundation, either version 2 of the License, or
00014 (at your option) any later version.
00015 
00016 Teapot Colony Wars is distributed in the hope that it will be useful,
00017 but WITHOUT ANY WARRANTY; without even the implied warranty of
00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00019 GNU General Public License for more details.
00020 
00021 You should have received a copy of the GNU General Public License
00022 along with Teapot Colony Wars.  If not, see <http://www.gnu.org/licenses/>.
00023 */
00024 
00025 #include "Image.h"
00026 
00027 #include <iostream>
00028 #include <fstream>
00029 #include <cstdlib>
00030 
00031 #include "Constants.h"
00032 
00033 int Image::readInt(std::ifstream &input) {
00034   char buffer[4];
00035   input.read(buffer, 4);
00036   return (int) (((unsigned char) buffer[3] << 24) |
00037                 ((unsigned char) buffer[2] << 16) |
00038                 ((unsigned char) buffer[1] <<  8) |
00039                  (unsigned char) buffer[0]);
00040 }
00041 
00042 short Image::readShort(std::ifstream &input) {
00043   char buffer[2];
00044   input.read(buffer, 2);
00045   return (short) (((unsigned char)buffer[1] << 8) |
00046                    (unsigned char)buffer[0]);
00047 }
00048 
00049 void Image::readBMPFileHeader(std::ifstream &input, BITMAPFILEHEADER *bfh) {
00050   bfh->bfType      = readShort(input);
00051   bfh->bfSize      = readInt(input);
00052   bfh->bfReserved1 = readShort(input);
00053   bfh->bfReserved2 = readShort(input);
00054   bfh->bfOffBits   = readInt(input);
00055 }
00056 
00057 void Image::readBMPInfoHeader(std::ifstream &input, BITMAPINFOHEADER *bih) {
00058   bih->biSize =          readInt(input);
00059   bih->biWidth =         readInt(input);
00060   bih->biHeight =        readInt(input);
00061   bih->biPlanes =        readShort(input);
00062   bih->biBitCount =      readShort(input);
00063   bih->biCompression =   readInt(input);
00064   bih->biSizeImage =     readInt(input);
00065   bih->biXPelsPerMeter = readInt(input);
00066   bih->biYPelsPerMeter = readInt(input);
00067   bih->biClrUsed =       readInt(input);
00068   bih->biClrImportant =  readInt(input);
00069 }
00070 
00071 void Image::readBMPColorTable(std::ifstream &input, int end, RGBQUAD *table) {
00072   int i, size = (end - input.tellg()) / 4;
00073 
00074   for(i = 0; i < size; i++) {
00075     input.read(&(table[i].rgbBlue), 1);
00076     input.read(&(table[i].rgbGreen), 1);
00077     input.read(&(table[i].rgbRed), 1);
00078     input.read(&(table[i].rgbReserved), 1);
00079   }
00080 }
00081 
00082 void Image::loadBMP(const char* filename) {
00083   std::ifstream input;
00084   BITMAPFILEHEADER *bfh;
00085   BITMAPINFOHEADER *bih;
00086   RGBQUAD *colorTable;
00087   int bytesPerRow, size, i, j, k, l;
00088   float factor;
00089   char *datas;
00090   char c;
00091 
00092   // ouverture du fichier
00093   input.open(filename, std::ios::in | std::ifstream::binary);
00094   if(input.fail()) {
00095     std::cerr << filename << " : could not find file" << std::endl;
00096     exit(EXIT_FAILURE);
00097   }
00098 
00099   // lecture de l'entête du fichier
00100   bfh = new BITMAPFILEHEADER;
00101   readBMPFileHeader(input, bfh);
00102 
00103   // les deux premiers octets d'un fichier bitmap doivent être "BM"
00104   if(bfh->bfType != 0x4D42) {
00105     std::cerr << filename << " : not a bitmap file" << std::endl;
00106     exit(EXIT_FAILURE);
00107   }
00108 
00109   // lecture des informations du bitmap
00110   bih = new BITMAPINFOHEADER;
00111   readBMPInfoHeader(input, bih);
00112 
00113   // on vérifie que le fichier n'est pas compressé
00114   if(bih->biCompression != 0) {
00115     std::cerr << filename << " : image is compressed" << std::endl;
00116     exit(EXIT_FAILURE);
00117   }
00118 
00119   // on récupère la largeur et la hauteur de l'image
00120   _width = bih->biWidth;
00121   _height = bih->biHeight;
00122 
00123   // chaque ligne de données du fichier doit comporter un nombre de bits
00124   // multiple de 4, les lignes sont éventuellement complètées par des 0.
00125   // on va donc calculer la taille des lignes à lire...
00126   switch(bih->biBitCount) {
00127     case 24:
00128       factor = 3.0;
00129       break;
00130     case 8:
00131       factor = 1.0;
00132       break;
00133     case 4:
00134       factor = 1.0 / 2.0;
00135       break;
00136     case 1:
00137       factor = 1.0 / 8.0;
00138       break;
00139     default:
00140       std::cerr << filename << " : invalid color depth" << std::endl;
00141       exit(EXIT_FAILURE);
00142   }
00143   float x = factor * (float) _width;
00144   int bytesToRead = ((int) x) + (((float) ((int) x)) != x);
00145   bytesPerRow = ((int) (((float) bytesToRead + 3.0) / 4.0)) * 4;
00146   int bytesToSkip = bytesPerRow - bytesToRead;
00147   size = bytesPerRow * _height;
00148 
00149   // création de la zone de mémoire temporaire pour stocker les données bruts.
00150   datas = new char[size];
00151   // ainsi que celle qui contiendra les données finales
00152   _pixels = new GLubyte[4 * _width * _height];
00153 
00154   // la lecture des données est différente selon que la composante couleur
00155   // par pixel est codée sur 24, 8, 4 ou 1 bits.
00156   switch(bih->biBitCount) {
00157     case 24:
00158       // on se positionne au début des données et on les lit en un seul passage
00159       input.seekg(bfh->bfOffBits, std::ios::beg);
00160       input.read(datas, size);
00161       // les données sont stockées sous forme BGR, il faut les retransformer
00162       // en RBG, en ajoutant une composante pour le blending alpha (on choisit
00163       // de rendre les pixels opaques, mais il serait envisageable de changer
00164       // cela par la suite)
00165       for(i = 0; i < _height; i++) {
00166         for(j = 0; j < _width; j++) {
00167           _pixels[4 * (i * _width + j)]     = datas[i * bytesPerRow + 3 * j + 2];
00168           _pixels[4 * (i * _width + j) + 1] = datas[i * bytesPerRow + 3 * j + 1];
00169           _pixels[4 * (i * _width + j) + 2] = datas[i * bytesPerRow + 3 * j];
00170           _pixels[4 * (i * _width + j) + 3] = COLOR_MAX;
00171         }
00172       }
00173       break;
00174     case 8:
00175       // création et lecture de la table des couleurs
00176       colorTable = new RGBQUAD[256];
00177       readBMPColorTable(input, bfh->bfOffBits, colorTable);
00178       // on se positionne au début des données
00179       input.seekg(bfh->bfOffBits, std::ios::beg);
00180       for(i = 0; i < _height; i++) {
00181         for(j = 0; j < _width; j++) {
00182           // on lit les octets un par un
00183           input.read(&c, 1);
00184           // chaque octet représente un pixel, la valeur stockée correspond
00185           // à l'index dans la table chromatique
00186           _pixels[4 * (i * _width + j)]     = colorTable[c].rgbRed;
00187           _pixels[4 * (i * _width + j) + 1] = colorTable[c].rgbGreen;
00188           _pixels[4 * (i * _width + j) + 2] = colorTable[c].rgbBlue;
00189           _pixels[4 * (i * _width + j) + 3] = COLOR_MAX;
00190         }
00191         input.seekg(bytesToSkip, std::ios::cur);
00192       }
00193       delete colorTable;
00194       break;
00195     case 4:
00196       // création et lecture de la table des couleurs
00197       colorTable = new RGBQUAD[16];
00198       readBMPColorTable(input, bfh->bfOffBits, colorTable);
00199       // on se positionne au début des données
00200       input.seekg(bfh->bfOffBits, std::ios::beg);
00201       for(i = 0; i < _height; i++) {
00202         for(j = 0; j < _width; j++) {
00203           // chaque octet représente deux pixels, la valeur stockée correspond
00204           // à l'index dans la table chromatique
00205           if((j % 2) == 0) {
00206             // on lit les octets un par un (pour un pixel sur deux)
00207             input.read(&c, 1);
00208             // on prend la valeur des quatres bits de poids fort
00209             k = c / 16;
00210           } else {
00211             // idem pour le deuxième pixel, mais pas besoin de relire l'octet
00212             // cette fois on veut la valeur des quatres bits de poids faible
00213             k = c % 16;
00214           }
00215           _pixels[4 * (i * _width + j)]     = colorTable[k].rgbRed;
00216           _pixels[4 * (i * _width + j) + 1] = colorTable[k].rgbGreen;
00217           _pixels[4 * (i * _width + j) + 2] = colorTable[k].rgbBlue;
00218           _pixels[4 * (i * _width + j) + 3] = COLOR_MAX;
00219         }
00220         input.seekg(bytesToSkip, std::ios::cur);
00221       }
00222       delete colorTable;
00223       break;
00224     case 1:
00225       // création et lecture de la table des couleurs
00226       colorTable = new RGBQUAD[2];
00227       readBMPColorTable(input, bfh->bfOffBits, colorTable);
00228       // on se positionne au début des données
00229       input.seekg(bfh->bfOffBits, std::ios::beg);
00230       for(i = 0; i < _height; i++) {
00231         for(j = 0; j < _width; j += 8) {
00232           // chaque octet représente huit pixels, la valeur stockée correspond
00233           // à l'index dans la table chromatique
00234           // on lit les octets un par un
00235           input.read(&c, 1);
00236           for(k = 0; k < 8 && 8 * j + k < _width; k++) {
00237             // on prend la valeur du bit correspondant au pixel
00238             l = (((1 << (7 - k)) & (((int) c) & 0xff)) > 0);
00239             _pixels[4 * (i * _width + j + k)]     = colorTable[l].rgbRed;
00240             _pixels[4 * (i * _width + j + k) + 1] = colorTable[l].rgbGreen;
00241             _pixels[4 * (i * _width + j + k) + 2] = colorTable[l].rgbBlue;
00242             _pixels[4 * (i * _width + j + k) + 3] = COLOR_MAX;
00243           }
00244         }
00245         input.seekg(bytesToSkip, std::ios::cur);
00246       }
00247       delete colorTable;
00248       break;
00249     default:
00250       std::cerr << filename << " : invalid color depth" << std::endl;
00251       exit(EXIT_FAILURE);
00252   }
00253 
00254   // on fait un peut de ménage...
00255   delete bfh;
00256   delete bih;
00257   delete datas;
00258   input.close();
00259 }
00260 
00261 Image::Image(const char* filename) {
00262   loadBMP(filename);
00263 }
00264 
00265 Image::Image(GLuint width, GLuint height) : _width(width), _height(height) {
00266   int i, size = _width * _height;
00267 
00268   _pixels = new GLubyte[4 * size];
00269   for(i = 0; i < size; i++) {
00270     _pixels[i]     = COLOR_MIN;
00271     _pixels[i + 1] = COLOR_MIN;
00272     _pixels[i + 2] = COLOR_MIN;
00273     _pixels[i + 3] = COLOR_MAX;
00274   }
00275 }
00276 
00277 Image::~Image() {
00278   delete _pixels;
00279 }
00280 
00281 GLuint Image::genTexture1D() {
00282   GLuint tex_name;
00283 
00284   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00285   // création d'une nouvelle texture
00286   glGenTextures(1, &tex_name);
00287   // cette texture est maintenant la texture courante
00288   glBindTexture(GL_TEXTURE_1D, tex_name);
00289   // association de la texture avec l'image
00290   glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, _width * _height, 0,
00291                GL_RGBA, GL_UNSIGNED_BYTE, _pixels);
00292 
00293   // paramètres de la texture
00294   glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP);
00295   glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00296   glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00297 
00298   return tex_name;
00299 }
00300 
00301 GLuint Image::genTexture2D() {
00302   GLuint tex_name;
00303 
00304   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
00305   // création d'une nouvelle texture
00306   glGenTextures(1, &tex_name);
00307   // cette texture est maintenant la texture courante
00308   glBindTexture(GL_TEXTURE_2D, tex_name);
00309   // association de la texture avec l'image
00310   glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0,
00311                GL_RGBA, GL_UNSIGNED_BYTE, _pixels);
00312 
00313   // paramètres de la texture
00314   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
00315   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
00316   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
00317   glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
00318 
00319   return tex_name;
00320 }

Generated on Sat Feb 2 22:22:54 2008 for Teapot Colony Wars by  doxygen 1.5.4