Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

image.cpp

Go to the documentation of this file.
00001 // image.cpp
00002 //
00003 // Copyright (C) 2001, Chris Laurel
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #include <fstream>
00011 
00012 #ifndef MACOSX
00013 #define JPEG_SUPPORT
00014 #define PNG_SUPPORT
00015 #endif
00016 
00017 #ifdef MACOSX
00018 #include <unistd.h>
00019 #include "CGBuffer.h"
00020 #ifndef PNG_SUPPORT
00021 #include <Quicktime/ImageCompression.h>
00022 #include <QuickTime/QuickTimeComponents.h>
00023 #endif
00024 #endif
00025 
00026 #ifndef _WIN32
00027 #ifndef MACOSX
00028 #include <config.h>
00029 #endif /* ! MACOSX */
00030 #endif /* ! _WIN32 */
00031 
00032 #include "image.h"
00033 
00034 #ifdef JPEG_SUPPORT
00035 
00036 #ifndef PNG_SUPPORT
00037 #include "setjmp.h"
00038 #endif // PNG_SUPPORT
00039 
00040 extern "C" {
00041 #ifdef _WIN32
00042 #include "jpeglib.h"
00043 #else
00044 #include <cstdio>
00045 #include <jpeglib.h>
00046 #endif
00047 }
00048 
00049 #endif // JPEG_SUPPORT
00050 
00051 #ifdef PNG_SUPPORT // PNG_SUPPORT
00052 #ifdef MACOSX
00053 #include "../../macosx/png.h"
00054 #else
00055 #include "png.h"
00056 #endif // MACOSX
00057 
00058 #include <celutil/debug.h>
00059 #include <celutil/util.h>
00060 #include <celutil/filetype.h>
00061 #include "gl.h"
00062 #include "glext.h"
00063 #include "celestia.h"
00064 
00065 #include <cassert>
00066 #include <iostream>
00067 #include <algorithm>
00068 #include <cmath>
00069 
00070 using namespace std;
00071 
00072 
00073 // Define png_jmpbuf() in case we are using a pre-1.0.6 version of libpng
00074 #ifndef png_jmpbuf
00075 #define png_jmpbuf(png_ptr) png_ptr->jmpbuf
00076 #endif // PNG_SUPPORT
00077 
00078 // Define various expansion transformations for old versions of libpng
00079 #if PNG_LIBPNG_VER < 10004
00080 #define png_set_palette_to_rgb(p)  png_set_expand(p)
00081 #define png_set_gray_1_2_4_to_8(p) png_set_expand(p)
00082 #define png_set_tRNS_to_alpha(p)   png_set_expand(p)
00083 #endif // PNG_LIBPNG_VER < 10004
00084 
00085 #endif // PNG_SUPPORT
00086 
00087 
00088 static int formatComponents(int fmt)
00089 {
00090     switch (fmt)
00091     {
00092     case GL_RGBA:
00093     case GL_BGRA_EXT:
00094         return 4;
00095     case GL_RGB:
00096     case GL_BGR_EXT:
00097         return 3;
00098     case GL_LUMINANCE_ALPHA:
00099     case GL_DSDT_NV:
00100         return 2;
00101     case GL_ALPHA:
00102     case GL_LUMINANCE:
00103         return 1;
00104 
00105     // Compressed formats
00106     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
00107         return 3;
00108     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
00109     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
00110         return 4;
00111 
00112     // Unknown format
00113     default:
00114         return 0;
00115     }
00116 }
00117 
00118 
00119 static int calcMipLevelSize(int fmt, int w, int h, int mip)
00120 {
00121     w = max(w >> mip, 1);
00122     h = max(h >> mip, 1);
00123 
00124     switch (fmt)
00125     {
00126     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
00127         // 4x4 blocks, 8 bytes per block
00128         return ((w + 3) / 4) * ((h + 3) / 4) * 8;
00129     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
00130     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
00131         // 4x4 blocks, 16 bytes per block
00132         return ((w + 3) / 4) * ((h + 3) / 4) * 16;
00133     default:
00134         return h * w * formatComponents(fmt);
00135     }
00136 }
00137 
00138 
00139 Image::Image(int fmt, int w, int h, int mips) :
00140     width(w),
00141     height(h),
00142     mipLevels(mips), 
00143     format(fmt),
00144     pixels(NULL)
00145 {
00146     components = formatComponents(fmt);
00147     assert(components != 0);
00148 
00149     size = 0;
00150     for (int i = 0; i < mipLevels; i++)
00151         size += calcMipLevelSize(fmt, w, h, i);
00152     pixels = new unsigned char[size];
00153 }
00154 
00155 
00156 Image::~Image()
00157 {
00158     if (pixels != NULL)
00159         delete[] pixels;
00160 }
00161 
00162 
00163 int Image::getWidth() const
00164 {
00165     return width;
00166 }
00167 
00168 
00169 int Image::getHeight() const
00170 {
00171     return height;
00172 }
00173 
00174 
00175 int Image::getMipLevelCount() const
00176 {
00177     return mipLevels;
00178 }
00179 
00180 
00181 int Image::getSize() const
00182 {
00183     return size;
00184 }
00185 
00186 
00187 int Image::getFormat() const
00188 {
00189     return format;
00190 }
00191 
00192 
00193 int Image::getComponents() const
00194 {
00195     return components;
00196 }
00197 
00198 
00199 unsigned char* Image::getPixels()
00200 {
00201     return pixels;
00202 }
00203 
00204 
00205 unsigned char* Image::getPixelRow(int mip, int row)
00206 {
00207     int w = max(width >> mip, 1);
00208     int h = max(height >> mip, 1);
00209     if (mip >= mipLevels || row >= h)
00210         return NULL;
00211 
00212     // Row addressing of compressed textures is not allowed
00213     if (isCompressed())
00214         return NULL;
00215 
00216     return getMipLevel(mip) + row * w * components;
00217 }
00218 
00219 
00220 unsigned char* Image::getPixelRow(int row)
00221 {
00222     return getPixelRow(0, row);
00223 }
00224 
00225 
00226 unsigned char* Image::getMipLevel(int mip)
00227 {
00228     if (mip >= mipLevels)
00229         return NULL;
00230 
00231     int offset = 0;
00232     for (int i = 0; i < mip; i++)
00233         offset += calcMipLevelSize(format, width, height, i);
00234 
00235     return pixels + offset;
00236 }
00237 
00238 
00239 int Image::getMipLevelSize(int mip) const
00240 {
00241     if (mip >= mipLevels)
00242         return 0;
00243     else
00244         return calcMipLevelSize(format, width, height, mip);
00245 }
00246 
00247 
00248 bool Image::isCompressed() const
00249 {
00250     switch (format)
00251     {
00252     case GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
00253     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
00254     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
00255         return true;
00256     default:
00257         return false;
00258     }
00259 }
00260 
00261 
00262 bool Image::hasAlpha() const
00263 {
00264     switch (format)
00265     {
00266     case GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
00267     case GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
00268     case GL_RGBA:
00269     case GL_BGRA_EXT:
00270     case GL_LUMINANCE_ALPHA:
00271     case GL_ALPHA:
00272         return true;
00273     default:
00274         return false;
00275     }
00276 }
00277 
00278 
00279 // Convert an input height map to a normal map.  Ideally, a single channel
00280 // input should be used.  If not, the first color channel of the input image
00281 // is the one only one used when generating normals.  This produces the
00282 // expected results for grayscale values in RGB images.
00283 Image* Image::computeNormalMap(float scale, bool wrap) const
00284 {
00285     // Can't do anything with compressed input; there are probably some other
00286     // formats that should be rejected as well . . .
00287     if (isCompressed())
00288         return NULL;
00289 
00290     Image* normalMap = new Image(GL_RGBA, width, height);
00291     if (normalMap == NULL)
00292         return NULL;
00293 
00294     unsigned char* nmPixels = normalMap->getPixels();
00295 
00296     // Compute normals using differences between adjacent texels.
00297     for (int i = 0; i < height; i++)
00298     {
00299         for (int j = 0; j < width; j++)
00300         {
00301             int i0 = i;
00302             int j0 = j;
00303             int i1 = i - 1;
00304             int j1 = j - 1;
00305             if (i1 < 0)
00306             {
00307                 if (wrap)
00308                 {
00309                     i1 = height - 1;
00310                 }
00311                 else
00312                 {
00313                     i0++;
00314                     i1++;
00315                 }   
00316             }
00317             if (j1 < 0)
00318             {
00319                 if (wrap)
00320                 {
00321                     j1 = width - 1;
00322                 }
00323                 else
00324                 {
00325                     j0++;
00326                     j1++;
00327                 }
00328             }
00329 
00330             int h00 = (int) pixels[(i0 * width + j0) * components];
00331             int h10 = (int) pixels[(i0 * width + j1) * components];
00332             int h01 = (int) pixels[(i1 * width + j0) * components];
00333             
00334             float dx = (float) (h10 - h00) * (1.0f / 255.0f) * scale;
00335             float dy = (float) (h01 - h00) * (1.0f / 255.0f) * scale;
00336 
00337             float mag = (float) sqrt(dx * dx + dy * dy + 1.0f);
00338             float rmag = 1.0f / mag;
00339 
00340             int n = (i * width + j) * 4;
00341             nmPixels[n]     = (unsigned char) (128 + 127 * dx * rmag);
00342             nmPixels[n + 1] = (unsigned char) (128 + 127 * dy * rmag);
00343             nmPixels[n + 2] = (unsigned char) (128 + 127 * rmag);
00344             nmPixels[n + 3] = 255;
00345         }
00346     }
00347 
00348     return normalMap;
00349 }
00350 
00351 
00352 Image* LoadImageFromFile(const string& filename)
00353 {
00354     ContentType type = DetermineFileType(filename);
00355     Image* img = NULL;
00356 
00357     clog << _("Loading image from file ") << filename << '\n';
00358 
00359     switch (type)
00360     {
00361     case Content_JPEG:
00362         img = LoadJPEGImage(filename);
00363         break;
00364     case Content_BMP:
00365         img = LoadBMPImage(filename);
00366         break;
00367     case Content_PNG:
00368         img = LoadPNGImage(filename);
00369         break;
00370     case Content_DDS:
00371         img = LoadDDSImage(filename);
00372         break;
00373     default:
00374         clog << filename << _(": unrecognized or unsupported image file type.\n");
00375         break;
00376     }
00377 
00378     return img;
00379 }
00380 
00381 
00382 
00383 
00384 
00385 #ifdef JPEG_SUPPORT
00386 
00387 struct my_error_mgr
00388 {
00389     struct jpeg_error_mgr pub;  // "public" fields
00390     jmp_buf setjmp_buffer;      // for return to caller
00391 };
00392 
00393 typedef struct my_error_mgr *my_error_ptr;
00394 
00395 METHODDEF(void) my_error_exit(j_common_ptr cinfo)
00396 {
00397     // cinfo->err really points to a my_error_mgr struct, so coerce pointer
00398     my_error_ptr myerr = (my_error_ptr) cinfo->err;
00399 
00400     // Always display the message.
00401     // We could postpone this until after returning, if we chose.
00402     (*cinfo->err->output_message) (cinfo);
00403 
00404     // Return control to the setjmp point
00405     longjmp(myerr->setjmp_buffer, 1);
00406 }
00407 #endif // JPEG_SUPPORT
00408 
00409 
00410 Image* LoadJPEGImage(const string& filename, int channels)
00411 {
00412 #ifdef JPEG_SUPPORT
00413     Image* img = NULL;
00414 
00415     // This struct contains the JPEG decompression parameters and pointers to
00416     // working space (which is allocated as needed by the JPEG library).
00417     struct jpeg_decompress_struct cinfo;
00418 
00419     // We use our private extension JPEG error handler.
00420     // Note that this struct must live as long as the main JPEG parameter
00421     // struct, to avoid dangling-pointer problems.
00422     struct my_error_mgr jerr;
00423     // More stuff
00424     JSAMPARRAY buffer;          // Output row buffer
00425     int row_stride;             // physical row width in output buffer
00426     long cont;
00427 
00428     // In this example we want to open the input file before doing anything else,
00429     // so that the setjmp() error recovery below can assume the file is open.
00430     // VERY IMPORTANT: use "b" option to fopen() if you are on a machine that
00431     // requires it in order to read binary files.
00432 
00433     FILE *in;
00434     in = fopen(filename.c_str(), "rb");
00435     if (in == NULL)
00436         return NULL;
00437 
00438     // Step 1: allocate and initialize JPEG decompression object
00439     // We set up the normal JPEG error routines, then override error_exit.
00440     cinfo.err = jpeg_std_error(&jerr.pub);
00441     jerr.pub.error_exit = my_error_exit;
00442     // Establish the setjmp return context for my_error_exit to use.
00443     if (setjmp(jerr.setjmp_buffer))
00444     {
00445         // If we get here, the JPEG code has signaled an error.
00446         // We need to clean up the JPEG object, close the input file, and return.
00447         jpeg_destroy_decompress(&cinfo);
00448         fclose(in);
00449         if (img != NULL)
00450             delete img;
00451 
00452         return NULL;
00453     }
00454 
00455     // Now we can initialize the JPEG decompression object.
00456     jpeg_create_decompress(&cinfo);
00457 
00458     // Step 2: specify data source (eg, a file)
00459     jpeg_stdio_src(&cinfo, in);
00460 
00461     // Step 3: read file parameters with jpeg_read_header()
00462     (void) jpeg_read_header(&cinfo, TRUE);
00463 
00464     // We can ignore the return value from jpeg_read_header since
00465     //  (a) suspension is not possible with the stdio data source, and
00466     //  (b) we passed TRUE to reject a tables-only JPEG file as an error.
00467 
00468     // Step 4: set parameters for decompression
00469 
00470     // In this example, we don't need to change any of the defaults set by
00471     // jpeg_read_header(), so we do nothing here.
00472 
00473     // Step 5: Start decompressor
00474 
00475     (void) jpeg_start_decompress(&cinfo);
00476     // We can ignore the return value since suspension is not possible
00477     // with the stdio data source.
00478 
00479     // We may need to do some setup of our own at this point before reading
00480     // the data.  After jpeg_start_decompress() we have the correct scaled
00481     // output image dimensions available, as well as the output colormap
00482     // if we asked for color quantization.
00483     // In this example, we need to make an output work buffer of the right size.
00484     // JSAMPLEs per row in output buffer
00485     row_stride = cinfo.output_width * cinfo.output_components;
00486     // Make a one-row-high sample array that will go away when done with image
00487     buffer = (*cinfo.mem->alloc_sarray)
00488         ((j_common_ptr) & cinfo, JPOOL_IMAGE, row_stride, 1);
00489 
00490     // Step 6: while (scan lines remain to be read)
00491     //             jpeg_read_scanlines(...);
00492 
00493     // Here we use the library's state variable cinfo.output_scanline as the
00494     // loop counter, so that we don't have to keep track ourselves.
00495 
00496     int format = GL_RGB;
00497     if (cinfo.output_components == 1)
00498         format = GL_LUMINANCE;
00499 
00500     img = new Image(format, cinfo.image_width, cinfo.image_height);
00501 
00502     // cont = cinfo.output_height - 1;
00503     cont = 0;
00504     while (cinfo.output_scanline < cinfo.output_height)
00505     {
00506         // jpeg_read_scanlines expects an array of pointers to scanlines.
00507         // Here the array is only one element long, but you could ask for
00508         // more than one scanline at a time if that's more convenient.
00509         (void) jpeg_read_scanlines(&cinfo, buffer, 1);
00510 
00511         // Assume put_scanline_someplace wants a pointer and sample count.
00512         // put_scanline_someplace(buffer[0], row_stride);
00513         memcpy(img->getPixelRow(cont), buffer[0], row_stride);
00514         cont++;
00515     }
00516 
00517     // Step 7: Finish decompression
00518 
00519     (void) jpeg_finish_decompress(&cinfo);
00520     // We can ignore the return value since suspension is not possible
00521     // with the stdio data source.
00522 
00523     // Step 8: Release JPEG decompression object
00524 
00525     // This is an important step since it will release a good deal of memory.
00526     jpeg_destroy_decompress(&cinfo);
00527 
00528     // After finish_decompress, we can close the input file.
00529     // Here we postpone it until after no more JPEG errors are possible,
00530     // so as to simplify the setjmp error logic above.  (Actually, I don't
00531     // think that jpeg_destroy can do an error exit, but why assume anything...
00532 
00533     fclose(in);
00534 
00535     // At this point you may want to check to see whether any corrupt-data
00536     // warnings occurred (test whether jerr.pub.num_warnings is nonzero).
00537 
00538     return img;
00539     
00540 #elif MACOSX
00541 
00542     Image* img = NULL;
00543     CGBuffer* cgJpegImage;
00544     size_t img_w, img_h, img_d;
00545 
00546     cgJpegImage = new CGBuffer(filename.c_str());
00547     if (cgJpegImage == NULL) {
00548         char tempcwd[2048];
00549         getcwd(tempcwd, sizeof(tempcwd));
00550         DPRINTF(0, "CGBuffer :: Error opening JPEG image file %s/%s\n", tempcwd, filename.c_str());
00551         delete cgJpegImage;
00552         return NULL;
00553     }
00554 
00555     if (!cgJpegImage->LoadJPEG()) {
00556         char tempcwd[2048];
00557         getcwd(tempcwd, sizeof(tempcwd));
00558         DPRINTF(0, "CGBuffer :: Error loading JPEG image file %s/%s\n", tempcwd, filename.c_str());
00559         delete cgJpegImage;
00560         return NULL;
00561     }
00562     
00563     cgJpegImage->Render();
00564 
00565     img_w = (size_t) cgJpegImage->image_size.width;
00566     img_h = (size_t) cgJpegImage->image_size.height;
00567     img_d = (size_t) ((cgJpegImage->image_depth == 8) ? 1 : 4);
00568 
00569     // DPRINTF(0,"cgJpegImage :: %d x %d x %d [%d] bpp\n", img_w, img_h, (size_t)cgJpegImage->image_depth, img_d);
00570 
00571 #ifdef MACOSX_ALPHA_JPEGS
00572     int format = (img_d == 1) ? GL_LUMINANCE : GL_RGBA;
00573 #else
00574     int format = (img_d == 1) ? GL_LUMINANCE : GL_RGB;
00575 #endif
00576     img = new Image(format, img_w, img_h);
00577     if (img == NULL || img->getPixels() == NULL) {
00578         DPRINTF(0, "Could not create image\n");
00579         delete cgJpegImage;
00580         return NULL;
00581     }
00582     // following code flips image and skips alpha byte if no alpha support
00583     unsigned char* bout = (unsigned char*) img->getPixels();
00584     unsigned char* bin  = (unsigned char*) cgJpegImage->buffer->data;
00585     unsigned int bcount = img_w * img_h * img_d;
00586     unsigned int i = 0;
00587     bin += bcount+(img_w*img_d); // start one row past end
00588     for (i=0; i<bcount; ++i)
00589     {
00590          // at end of row, move back two rows
00591         if ( (i % (img_w * img_d)) == 0 ) bin -= 2*(img_w * img_d);
00592 
00593 #ifndef MACOSX_ALPHA_JPEGS
00594         if (( (img_d != 1) && !((i&3)^3) )) // skip extra byte 
00595         {
00596             ++bin;
00597         } else 
00598 #endif // !MACOSX_ALPHA_JPEGS         
00599 
00600         *bout++ = *bin++;
00601     }    
00602     delete cgJpegImage;
00603     return img;
00604     
00605 #else
00606     return NULL;
00607 #endif // JPEG_SUPPORT
00608 }
00609 
00610 
00611 #ifdef PNG_SUPPORT
00612 void PNGReadData(png_structp png_ptr, png_bytep data, png_size_t length)
00613 {
00614     FILE* fp = (FILE*) png_get_io_ptr(png_ptr);
00615     fread((void*) data, 1, length, fp);
00616 }
00617 #endif
00618 
00619 
00620 Image* LoadPNGImage(const string& filename)
00621 {
00622 #ifndef PNG_SUPPORT
00623     return NULL;
00624 #else
00625     char header[8];
00626     png_structp png_ptr;
00627     png_infop info_ptr;
00628     png_uint_32 width, height;
00629     int bit_depth, color_type, interlace_type;
00630     FILE* fp = NULL;
00631     Image* img = NULL;
00632     png_bytep* row_pointers = NULL;
00633 
00634     fp = fopen(filename.c_str(), "rb");
00635     if (fp == NULL)
00636     {
00637         clog << _("Error opening image file ") << filename << '\n';
00638         return NULL;
00639     }
00640    
00641     fread(header, 1, sizeof(header), fp);
00642     if (png_sig_cmp((unsigned char*) header, 0, sizeof(header)))
00643     {
00644         clog << _("Error: ") << filename << _(" is not a PNG file.\n");
00645         fclose(fp);
00646         return NULL;
00647     }
00648 
00649     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
00650                                      NULL, NULL, NULL);
00651     if (png_ptr == NULL)
00652     {
00653         fclose(fp);
00654         return NULL;
00655     }
00656 
00657     info_ptr = png_create_info_struct(png_ptr);
00658     if (info_ptr == NULL)
00659     {
00660         fclose(fp);
00661         png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
00662         return NULL;
00663     }
00664    
00665     if (setjmp(png_jmpbuf(png_ptr)))
00666     {
00667         fclose(fp);
00668         if (img != NULL)
00669             delete img;
00670         png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
00671         clog << _("Error reading PNG image file ") << filename << '\n';
00672         return NULL;
00673     }
00674 
00675     // png_init_io(png_ptr, fp);
00676     png_set_read_fn(png_ptr, (void*) fp, PNGReadData);
00677     png_set_sig_bytes(png_ptr, sizeof(header));
00678 
00679     png_read_info(png_ptr, info_ptr);
00680 
00681     png_get_IHDR(png_ptr, info_ptr,
00682                  &width, &height, &bit_depth,
00683                  &color_type, &interlace_type,
00684                  NULL, NULL);
00685 
00686     GLenum glformat = GL_RGB;
00687     switch (color_type)
00688     {
00689     case PNG_COLOR_TYPE_GRAY:
00690         glformat = GL_LUMINANCE;
00691         break;
00692     case PNG_COLOR_TYPE_GRAY_ALPHA:
00693         glformat = GL_LUMINANCE_ALPHA;
00694         break;
00695     case PNG_COLOR_TYPE_RGB:
00696         glformat = GL_RGB;
00697         break;
00698     case PNG_COLOR_TYPE_PALETTE:
00699     case PNG_COLOR_TYPE_RGB_ALPHA:
00700         glformat = GL_RGBA;
00701         break;
00702     default:
00703         // badness
00704         break;
00705     }
00706 
00707     img = new Image(glformat, width, height);
00708     if (img == NULL)
00709     {
00710         fclose(fp);
00711         png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL);
00712         return NULL;
00713     }
00714 
00715     // TODO: consider using paletted textures if they're available
00716     if (color_type == PNG_COLOR_TYPE_PALETTE)
00717     {
00718         png_set_palette_to_rgb(png_ptr);
00719     }
00720 
00721     if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
00722     {
00723         png_set_gray_1_2_4_to_8(png_ptr);
00724     }
00725 
00726     if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
00727     {
00728         png_set_tRNS_to_alpha(png_ptr);
00729     }
00730 
00731     // TODO: consider passing images with < 8 bits/component to
00732     // GL without expanding
00733     if (bit_depth == 16)
00734         png_set_strip_16(png_ptr);
00735     else if (bit_depth < 8)
00736         png_set_packing(png_ptr);
00737 
00738     row_pointers = new png_bytep[height];
00739     for (unsigned int i = 0; i < height; i++)
00740         row_pointers[i] = (png_bytep) img->getPixelRow(i);
00741 
00742     png_read_image(png_ptr, row_pointers);
00743 
00744     delete[] row_pointers;
00745 
00746     png_read_end(png_ptr, NULL);
00747     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00748 
00749     fclose(fp);
00750 
00751     return img;
00752 #endif
00753 }
00754 
00755 
00756 // BMP file definitions--can't use windows.h because we might not be
00757 // built on Windows!
00758 typedef struct
00759 {
00760     unsigned char b;
00761     unsigned char m;
00762     unsigned int size;
00763     unsigned int reserved;
00764     unsigned int offset;
00765 } BMPFileHeader;
00766 
00767 typedef struct
00768 {
00769     unsigned int size;
00770     int width;
00771     int height;
00772     unsigned short planes;
00773     unsigned short bpp;
00774     unsigned int compression;
00775     unsigned int imageSize;
00776     int widthPPM;
00777     int heightPPM;
00778     unsigned int colorsUsed;
00779     unsigned int colorsImportant;
00780 } BMPImageHeader;
00781 
00782 
00783 static int readInt(ifstream& in)
00784 {
00785     unsigned char b[4];
00786     in.read(reinterpret_cast<char*>(b), 4);
00787     return ((int) b[3] << 24) + ((int) b[2] << 16)
00788         + ((int) b[1] << 8) + (int) b[0];
00789 }
00790 
00791 static short readShort(ifstream& in)
00792 {
00793     unsigned char b[2];
00794     in.read(reinterpret_cast<char*>(b), 2);
00795     return ((short) b[1] << 8) + (short) b[0];
00796 }
00797 
00798 
00799 static Image* LoadBMPImage(ifstream& in)
00800 {
00801     BMPFileHeader fileHeader;
00802     BMPImageHeader imageHeader;
00803     unsigned char* pixels;
00804 
00805     in >> fileHeader.b;
00806     in >> fileHeader.m;
00807     fileHeader.size = readInt(in);
00808     fileHeader.reserved = readInt(in);
00809     fileHeader.offset = readInt(in);
00810 
00811     if (fileHeader.b != 'B' || fileHeader.m != 'M')
00812         return NULL;
00813 
00814     imageHeader.size = readInt(in);
00815     imageHeader.width = readInt(in);
00816     imageHeader.height = readInt(in);
00817     imageHeader.planes = readShort(in);
00818     imageHeader.bpp = readShort(in);
00819     imageHeader.compression = readInt(in);
00820     imageHeader.imageSize = readInt(in);
00821     imageHeader.widthPPM = readInt(in);
00822     imageHeader.heightPPM = readInt(in);
00823     imageHeader.colorsUsed = readInt(in);
00824     imageHeader.colorsImportant = readInt(in);
00825 
00826     if (imageHeader.width <= 0 || imageHeader.height <= 0)
00827         return NULL;
00828 
00829     // We currently don't support compressed BMPs
00830     if (imageHeader.compression != 0)
00831         return NULL;
00832     // We don't handle 1-, 2-, or 4-bpp images
00833     if (imageHeader.bpp != 8 && imageHeader.bpp != 24 && imageHeader.bpp != 32)
00834         return NULL;
00835 
00836     unsigned char* palette = NULL;
00837     if (imageHeader.bpp == 8)
00838     {
00839         printf("Reading %d color palette\n", imageHeader.colorsUsed);
00840         palette = new unsigned char[imageHeader.colorsUsed * 4];
00841         in.read(reinterpret_cast<char*>(palette), imageHeader.colorsUsed * 4);
00842     }
00843 
00844     in.seekg(fileHeader.offset, ios::beg);
00845 
00846     unsigned int bytesPerRow =
00847         (imageHeader.width * imageHeader.bpp / 8 + 1) & ~1;
00848     unsigned int imageBytes = bytesPerRow * imageHeader.height;
00849 
00850     // slurp the image data
00851     pixels = new unsigned char[imageBytes];
00852     in.read(reinterpret_cast<char*>(pixels), imageBytes);
00853 
00854     // check for truncated file
00855 
00856     Image* img = new Image(GL_RGB, imageHeader.width, imageHeader.height);
00857     if (img == NULL)
00858     {
00859         delete[] pixels;
00860         return NULL;
00861     }
00862 
00863     // Copy the image and perform any necessary conversions
00864     for (int y = 0; y < imageHeader.height; y++)
00865     {
00866         unsigned char* src = &pixels[y * bytesPerRow];
00867         unsigned char* dst = img->getPixelRow(y);
00868 
00869         switch (imageHeader.bpp)
00870         {
00871         case 8:
00872             {
00873                 for (int x = 0; x < imageHeader.width; x++)
00874                 {
00875                     unsigned char* color = palette + (*src << 2);
00876                     dst[0] = color[2];
00877                     dst[1] = color[1];
00878                     dst[2] = color[0];
00879                     src++;
00880                     dst += 3;
00881                 }
00882             }
00883             break;
00884             
00885         case 24:
00886             {
00887                 for (int x = 0; x < imageHeader.width; x++)
00888                 {
00889                     dst[0] = src[2];
00890                     dst[1] = src[1];
00891                     dst[2] = src[0];
00892                     src += 3;
00893                     dst += 3;
00894                 }
00895             }
00896             break;
00897 
00898         case 32:
00899             {
00900                 for (int x = 0; x < imageHeader.width; x++)
00901                 {
00902                     dst[0] = src[2];
00903                     dst[1] = src[1];
00904                     dst[2] = src[0];
00905                     src += 4;
00906                     dst += 3;
00907                 }
00908             }
00909             break;
00910         }
00911     }
00912 
00913     delete[] pixels;
00914 
00915     return img;
00916 }
00917 
00918 
00919 Image* LoadBMPImage(const string& filename)
00920 {
00921     ifstream bmpFile(filename.c_str(), ios::in | ios::binary);
00922 
00923     if (bmpFile.good())
00924     {
00925         Image* img = LoadBMPImage(bmpFile);
00926         bmpFile.close();
00927         return img;
00928     }
00929     else
00930     {
00931         return NULL;
00932     }
00933 }

Generated on Sat Jan 14 22:30:27 2006 for Celestia by  doxygen 1.4.1