wave-png.cpp

Go to the documentation of this file.
00001 /*
00002  * wave-png.cpp
00003  *
00004  * From: png.c -- png texture loader
00005  * http://tfcduke.developpez.com/tutoriel/format/png/
00006  *
00007  * last modification of that file: feb. 5, 2006
00008  * png.c Copyright (c) 2005-2006 David HENRY
00009  * wave-png.cpp Copyright(C) 2009  Thomas A. Vaughan
00010  *
00011  * Permission is hereby granted, free of charge, to any person
00012  * obtaining a copy of this software and associated documentation
00013  * files (the "Software"), to deal in the Software without
00014  * restriction, including without limitation the rights to use,
00015  * copy, modify, merge, publish, distribute, sublicense, and/or
00016  * sell copies of the Software, and to permit persons to whom the
00017  * Software is furnished to do so, subject to the following conditions:
00018  *
00019  * The above copyright notice and this permission notice shall be
00020  * included in all copies or substantial portions of the Software.
00021  *
00022  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
00023  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
00024  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
00025  * NONINFRINGEMENT.
00026  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
00027  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
00028  * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
00029  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
00030  */
00031 
00032 // includes --------------------------------------------------------------------
00033 #include "wave-png.h"           // always include our own header first!
00034 
00035 #include "png.h"                // libpng header
00036 
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include <string.h>
00040 
00041 #include <fstream>
00042 
00043 #include "nstream/nstream.h"
00044 #include "perf/perf.h"
00045 #include "util/file.h"
00046 
00047 
00048 
00049 ////////////////////////////////////////////////////////////////////////////////
00050 //
00051 //      This code from http://tfcduke.developpez.com/tutoriel/format/png/
00052 //      file there: png.c   Thanks again to David Henry!
00053 //
00054 //      Main changes in wave-png.cpp vs png.c:
00055 //       - Removed dependencies on OpenGL/glut
00056 //       - Added support for reading from nstream::Stream and std::istream
00057 //       - general style formatting (see wavepacket style, derived from FreeBSD)
00058 //       - save to media::image_t object
00059 //
00060 ////////////////////////////////////////////////////////////////////////////////
00061 
00062 
00063 
00064 namespace media {
00065 
00066 
00067 ////////////////////////////////////////////////////////////////////////////////
00068 //
00069 //      static helper methods
00070 //
00071 ////////////////////////////////////////////////////////////////////////////////
00072 
00073 static void
00074 pngReadFromStream
00075 (
00076 png_structp ctx,
00077 png_bytep data,
00078 png_size_t bytes
00079 )
00080 {
00081         std::istream * pstream = (std::istream *) png_get_io_ptr(ctx);
00082         ASSERT(pstream, "null context");
00083 
00084 //      DPRINTF("Asked to read %d bytes", (int) bytes);
00085 
00086         pstream->read((char *) data, bytes);
00087 }
00088 
00089 
00090 
00091 ////////////////////////////////////////////////////////////////////////////////
00092 //
00093 //      public API
00094 //
00095 ////////////////////////////////////////////////////////////////////////////////
00096 
00097 static int
00098 getBytesPerPixel
00099 (
00100 IN int ct
00101 )
00102 throw()
00103 {
00104         switch(ct) {
00105         case PNG_COLOR_TYPE_GRAY:       return 1;
00106         case PNG_COLOR_TYPE_GRAY_ALPHA: return 2;
00107         case PNG_COLOR_TYPE_RGB:        return 3;
00108         case PNG_COLOR_TYPE_RGB_ALPHA:  return 4;
00109         default:
00110                 ASSERT_THROW(false, "unknown color type: " << ct);
00111         }
00112         return 0;
00113 }
00114 
00115 
00116 
00117 static eColorFormat
00118 getColorFormat
00119 (
00120 IN int pngColorType
00121 )
00122 throw()
00123 {
00124         switch (pngColorType) {
00125         case PNG_COLOR_TYPE_GRAY:       return eColorFormat_Grayscale;
00126         case PNG_COLOR_TYPE_GRAY_ALPHA: return eColorFormat_GrayAlpha;
00127         case PNG_COLOR_TYPE_RGB:        return eColorFormat_RGB;
00128         case PNG_COLOR_TYPE_RGB_ALPHA:  return eColorFormat_RGBA;
00129         default:
00130                 ASSERT_THROW(false, "Unknown color type: " << pngColorType);
00131         }
00132         return eColorFormat_Invalid;
00133 }
00134 
00135 
00136 
00137 void
00138 readPngFromStream
00139 (
00140 IO std::istream& stream,
00141 OUT image_t& png
00142 )
00143 {
00144         perf::Timer timer("readPngFromStream");
00145         ASSERT_THROW(stream.good(), "bad stream?");
00146         png.clear();
00147 
00148         // read magic number
00149         const int magicBytes = 8;
00150         png_byte magic[magicBytes];
00151         stream.read((char *) magic, magicBytes);
00152         if (!png_check_sig(magic, magicBytes)) {
00153                 WAVE_EX(wex);
00154                 wex << "Stream does not contain valid PNG header";
00155         }
00156 
00157         // create a png read struct
00158         png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
00159             NULL, NULL, NULL);
00160         ASSERT(png_ptr, "failed to create png read struct");
00161 
00162         // create a png info struct
00163         png_infop info_ptr = png_create_info_struct(png_ptr);
00164         ASSERT(info_ptr, "failed to create png info struct");
00165 
00166         // libpng uses setjmp/longjmp for errors
00167         // A longjmp will return here.  Clean up and throw.
00168         if (setjmp(png_jmpbuf(png_ptr))) {
00169                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00170                 png.clear();
00171                 WAVE_EX(wex);
00172                 wex << "Encountered an error reading PNG from stream.";
00173                 wex << "  Corrupted file?";
00174         }
00175 
00176         // we want to read from std::istream, so override the default read fn
00177         png_set_read_fn(png_ptr, &stream, pngReadFromStream);
00178 
00179         // we've got the magic number
00180         png_set_sig_bytes(png_ptr, magicBytes);
00181 
00182         // read png info
00183         png_read_info(png_ptr, info_ptr);
00184         // DPRINTF("width:  %d pixels", (int) info_ptr->width);
00185         // DPRINTF("height: %d pixels", (int) info_ptr->height);
00186         // DPRINTF("rowbytes: %d", (int) info_ptr->rowbytes);
00187         // DPRINTF("bit_depth: %d", info_ptr->bit_depth);
00188         // DPRINTF("color_type: %d", info_ptr->color_type);
00189         // DPRINTF("pixel_depth: %d", info_ptr->pixel_depth);
00190 
00191         // get info
00192         png.bit_depth = png_get_bit_depth(png_ptr, info_ptr);
00193         int pngColorType = png_get_color_type(png_ptr, info_ptr);
00194         png.color_format = getColorFormat(pngColorType);
00195         // DPRINTF("color_format: %d", png.color_format);
00196 
00197         // convert index color images to RGB
00198         if (PNG_COLOR_TYPE_PALETTE == pngColorType) {
00199                 DPRINTF("Transforming from palette to rgb");
00200                 png_set_palette_to_rgb(png_ptr);
00201         } else if (eColorFormat_Grayscale == png.color_format &&
00202                    png.bit_depth < 8) {
00203                 DPRINTF("Transforming to 8-bit grayscale");
00204                 png_set_gray_1_2_4_to_8(png_ptr);
00205         }
00206 
00207         if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
00208                 DPRINTF("Doing weird tRNS thing");
00209                 png_set_tRNS_to_alpha(png_ptr);
00210         }
00211 
00212         if (16 == png.bit_depth) {
00213                 png_set_strip_16(png_ptr);
00214         } else if (png.bit_depth < 8) {
00215                 png_set_packing(png_ptr);
00216         }
00217 
00218         // update for transformations (anything we did above)
00219         png_read_update_info(png_ptr, info_ptr);
00220 
00221         // bytes per pixel
00222         int bpp = getBytesPerPixel(info_ptr->color_type);
00223         ASSERT_THROW(bpp > 0 && bpp < 5,
00224             "Bad bytes per pixel: " << bpp);
00225 
00226         // update png object
00227         png.width = info_ptr->width;
00228         png.height = info_ptr->height;
00229         png.bytes_per_pixel = bpp;
00230 
00231         // ask for memory
00232         png.allocate();
00233         ASSERT_THROW(png.data, "failed");
00234 
00235         // read a row at a time
00236         // NOTE: read rows in reverse order!  PNG format is bottom-to-top
00237         long bytes_per_row = png.width * bpp;
00238         for (int i = 0; i < png.height; ++i) {
00239                 long pixel_offset = png.height - i - 1;
00240                 long byte_offset = pixel_offset * bytes_per_row;
00241                 png_read_row(png_ptr, png.data + byte_offset, NULL);
00242         }
00243 
00244 #ifdef DEBUG
00245         // DEBUG: write out the first NxN pixels
00246         {
00247                 int nCols = 120;        // this many characters in a row
00248                 int nCount = 0;
00249                 if (1 == bpp) {
00250                         DPRINTF("Dumping image (grayscale values)");
00251                         nCount = nCols / 5;
00252                 } else if (3 == bpp) {
00253                         DPRINTF("Dumping RGB image (r,g,b)");
00254                         nCount = nCols / 13;
00255                 } else if (4 == bpp) {
00256                         DPRINTF("Dumping RGBA image (r,g,b,a)");
00257                         nCount = nCols / 17;
00258                 }
00259                 DPRINTF("  (dumping top left %d x %d pixels)", nCount, nCount);
00260                 for (int y = 0; y < nCount; ++y) {
00261                         const byte_t * data = png.data + y * bytes_per_row;
00262                         for (int x = 0; x < nCount; ++x, data += bpp) {
00263                                 if (1 == bpp) {
00264                                         fprintf(stderr, "  %03d", *data);
00265                                 } else if (3 == bpp) {
00266                                         fprintf(stderr, "  %03d,%03d,%03d",
00267                                             data[0], data[1], data[2]);
00268                                 } else if (4 == bpp) {
00269                                         fprintf(stderr, "  %03d,%03d,%03d,%03d",
00270                                             data[0], data[1], data[2], data[3]);
00271                                 }
00272                         }
00273                         fprintf(stderr, "\n");
00274                 }
00275         }
00276 #endif  // DEBUG
00277 
00278         // all done
00279         png_read_end(png_ptr, NULL);
00280         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00281         png.setImageType("PNG");
00282 }
00283 
00284 
00285 
00286 class PngLoader : public media::ImageLoader {
00287 public:
00288         ~PngLoader(void) throw() { }
00289 
00290         // media::ImageLoader class interface methods --------------------------
00291         const char * getLoaderName(void) const throw() { return "PNG"; }
00292         bool canLoadImage(IN nstream::Manager * mgr,
00293                                 IN const char * name) const {
00294                         mgr = mgr;      // unused
00295                         ASSERT(name, "null");
00296 
00297                         const char * ext = GetExtension(name);
00298                         DPRINTF("Checking extension: '%s'", ext);
00299                         return !strcasecmp("png", ext);
00300                 }
00301 
00302         void load(IN nstream::Stream * stream,
00303                                 OUT image_t& image) const {
00304                         std::istream& input = stream->getStream();
00305                         readPngFromStream(input, image);
00306                 }
00307 };
00308 
00309 
00310 
00311 smart_ptr<ImageLoader>
00312 createPngImageLoader
00313 (
00314 void
00315 )
00316 {
00317         smart_ptr<ImageLoader> loader = new PngLoader;
00318         ASSERT(loader, "out of memory");
00319 
00320         return loader;
00321 }
00322 
00323 
00324 
00325 };      // media namespace
00326