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 (stream.eof() || stream.fail()) {
00153                 DPRINTF("Stream had issues!");
00154         }
00155         if (!png_check_sig(magic, magicBytes)) {
00156                 WAVE_EX(wex);
00157                 wex << "Stream does not contain valid PNG header";
00158         }
00159 
00160         // create a png read struct
00161         png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
00162             NULL, NULL, NULL);
00163         ASSERT(png_ptr, "failed to create png read struct");
00164 
00165         // create a png info struct
00166         png_infop info_ptr = png_create_info_struct(png_ptr);
00167         ASSERT(info_ptr, "failed to create png info struct");
00168 
00169         // libpng uses setjmp/longjmp for errors
00170         // A longjmp will return here.  Clean up and throw.
00171         if (setjmp(png_jmpbuf(png_ptr))) {
00172                 png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00173                 png.clear();
00174                 WAVE_EX(wex);
00175                 wex << "Encountered an error reading PNG from stream.";
00176                 wex << "  Corrupted file?";
00177         }
00178 
00179         // we want to read from std::istream, so override the default read fn
00180         png_set_read_fn(png_ptr, &stream, pngReadFromStream);
00181 
00182         // we've got the magic number
00183         png_set_sig_bytes(png_ptr, magicBytes);
00184 
00185         // read png info
00186         png_read_info(png_ptr, info_ptr);
00187         // DPRINTF("width:  %d pixels", (int) info_ptr->width);
00188         // DPRINTF("height: %d pixels", (int) info_ptr->height);
00189         // DPRINTF("rowbytes: %d", (int) info_ptr->rowbytes);
00190         // DPRINTF("bit_depth: %d", info_ptr->bit_depth);
00191         // DPRINTF("color_type: %d", info_ptr->color_type);
00192         // DPRINTF("pixel_depth: %d", info_ptr->pixel_depth);
00193 
00194         // get info
00195         png.bit_depth = png_get_bit_depth(png_ptr, info_ptr);
00196         int pngColorType = png_get_color_type(png_ptr, info_ptr);
00197         png.color_format = getColorFormat(pngColorType);
00198         // DPRINTF("color_format: %d", png.color_format);
00199 
00200         // convert index color images to RGB
00201         if (PNG_COLOR_TYPE_PALETTE == pngColorType) {
00202                 DPRINTF("Transforming from palette to rgb");
00203                 png_set_palette_to_rgb(png_ptr);
00204         } else if (eColorFormat_Grayscale == png.color_format &&
00205                    png.bit_depth < 8) {
00206                 DPRINTF("Transforming to 8-bit grayscale");
00207                 png_set_gray_1_2_4_to_8(png_ptr);
00208         }
00209 
00210         if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
00211                 DPRINTF("Doing weird tRNS thing");
00212                 png_set_tRNS_to_alpha(png_ptr);
00213         }
00214 
00215         if (16 == png.bit_depth) {
00216                 png_set_strip_16(png_ptr);
00217         } else if (png.bit_depth < 8) {
00218                 png_set_packing(png_ptr);
00219         }
00220 
00221         // update for transformations (anything we did above)
00222         png_read_update_info(png_ptr, info_ptr);
00223 
00224         // bytes per pixel
00225         int bpp = getBytesPerPixel(info_ptr->color_type);
00226         ASSERT_THROW(bpp > 0 && bpp < 5,
00227             "Bad bytes per pixel: " << bpp);
00228 
00229         // update png object
00230         png.width = info_ptr->width;
00231         png.height = info_ptr->height;
00232         png.bytes_per_pixel = bpp;
00233 
00234         // ask for memory
00235         png.allocate();
00236         ASSERT_THROW(png.data, "failed");
00237 
00238         // read a row at a time
00239         // NOTE: read rows in reverse order!  PNG format is bottom-to-top
00240         long bytes_per_row = png.width * bpp;
00241         for (int i = 0; i < png.height; ++i) {
00242                 long pixel_offset = png.height - i - 1;
00243                 long byte_offset = pixel_offset * bytes_per_row;
00244                 png_read_row(png_ptr, png.data + byte_offset, NULL);
00245         }
00246 
00247 #ifdef DEBUG
00248         // DEBUG: write out the first NxN pixels
00249         {
00250                 int nCols = 120;        // this many characters in a row
00251                 int nCount = 0;
00252                 if (1 == bpp) {
00253                         DPRINTF("Dumping image (grayscale values)");
00254                         nCount = nCols / 5;
00255                 } else if (3 == bpp) {
00256                         DPRINTF("Dumping RGB image (r,g,b)");
00257                         nCount = nCols / 13;
00258                 } else if (4 == bpp) {
00259                         DPRINTF("Dumping RGBA image (r,g,b,a)");
00260                         nCount = nCols / 17;
00261                 }
00262                 DPRINTF("  (dumping top left %d x %d pixels)", nCount, nCount);
00263                 for (int y = 0; y < nCount; ++y) {
00264                         const byte_t * data = png.data + y * bytes_per_row;
00265                         for (int x = 0; x < nCount; ++x, data += bpp) {
00266                                 if (1 == bpp) {
00267                                         fprintf(stderr, "  %03d", *data);
00268                                 } else if (3 == bpp) {
00269                                         fprintf(stderr, "  %03d,%03d,%03d",
00270                                             data[0], data[1], data[2]);
00271                                 } else if (4 == bpp) {
00272                                         fprintf(stderr, "  %03d,%03d,%03d,%03d",
00273                                             data[0], data[1], data[2], data[3]);
00274                                 }
00275                         }
00276                         fprintf(stderr, "\n");
00277                 }
00278         }
00279 #endif  // DEBUG
00280 
00281         // all done
00282         png_read_end(png_ptr, NULL);
00283         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
00284         png.setImageType("PNG");
00285 }
00286 
00287 
00288 
00289 class PngLoader : public media::ImageLoader {
00290 public:
00291         ~PngLoader(void) throw() { }
00292 
00293         // media::ImageLoader class interface methods --------------------------
00294         const char * getLoaderName(void) const throw() { return "PNG"; }
00295         bool canLoadImage(IN nstream::Manager * mgr,
00296                                 IN const char * name) const {
00297                         mgr = mgr;      // unused
00298                         ASSERT(name, "null");
00299 
00300                         const char * ext = GetExtension(name);
00301                         DPRINTF("Checking extension: '%s'", ext);
00302                         return !strcasecmp("png", ext);
00303                 }
00304 
00305         void load(IN nstream::Stream * stream,
00306                                 OUT image_t& image) const {
00307                         std::istream& input = stream->getStream();
00308                         readPngFromStream(input, image);
00309                 }
00310 };
00311 
00312 
00313 
00314 smart_ptr<ImageLoader>
00315 createPngImageLoader
00316 (
00317 void
00318 )
00319 {
00320         smart_ptr<ImageLoader> loader = new PngLoader;
00321         ASSERT(loader, "out of memory");
00322 
00323         return loader;
00324 }
00325 
00326 
00327 
00328 };      // media namespace
00329