00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include "wave-png.h"
00034
00035 #include "png.h"
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
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064 namespace media {
00065
00066
00067
00068
00069
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
00085
00086 pstream->read((char *) data, bytes);
00087 }
00088
00089
00090
00091
00092
00093
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
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
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
00166 png_infop info_ptr = png_create_info_struct(png_ptr);
00167 ASSERT(info_ptr, "failed to create png info struct");
00168
00169
00170
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
00180 png_set_read_fn(png_ptr, &stream, pngReadFromStream);
00181
00182
00183 png_set_sig_bytes(png_ptr, magicBytes);
00184
00185
00186 png_read_info(png_ptr, info_ptr);
00187
00188
00189
00190
00191
00192
00193
00194
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
00199
00200
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
00222 png_read_update_info(png_ptr, info_ptr);
00223
00224
00225 int bpp = getBytesPerPixel(info_ptr->color_type);
00226 ASSERT_THROW(bpp > 0 && bpp < 5,
00227 "Bad bytes per pixel: " << bpp);
00228
00229
00230 png.width = info_ptr->width;
00231 png.height = info_ptr->height;
00232 png.bytes_per_pixel = bpp;
00233
00234
00235 png.allocate();
00236 ASSERT_THROW(png.data, "failed");
00237
00238
00239
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
00249 {
00250 int nCols = 120;
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
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
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;
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 };
00329