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
00034
00035
00036
00037
00038 #ifdef INVT_HAVE_LIBPNG
00039
00040 #include "Raster/PngParser.H"
00041
00042 #include "Util/Assert.H"
00043 #include "Image/Image.H"
00044 #include "Image/Pixels.H"
00045 #include "Raster/GenericFrame.H"
00046 #include "Util/log.H"
00047 #include "Util/sformat.H"
00048
00049 #include <cerrno>
00050 #include <cstdio>
00051 #include <cstdlib>
00052 #include <png.h>
00053
00054
00055 struct PngParser::Rep
00056 {
00057 Rep(const char* nm) :
00058 filename(nm), file(0), pngPtr(0), infoPtr(0), endPtr(0)
00059 {}
00060
00061 void close()
00062 {
00063 if (this->pngPtr != 0)
00064 {
00065 png_destroy_read_struct(this->pngPtr ? &this->pngPtr : 0,
00066 this->infoPtr ? &this->infoPtr : 0,
00067 this->endPtr ? &this->endPtr : 0);
00068 }
00069
00070 if (this->file != 0)
00071 {
00072 fclose(this->file);
00073 this->file = 0;
00074 }
00075 }
00076
00077 bool closed() const
00078 {
00079 return this->file == 0;
00080 }
00081
00082 void onError(const std::string& msg)
00083 {
00084 this->close();
00085 if (errno == 0)
00086 {
00087 LFATAL("with file %s: %s\n", this->filename, msg.c_str());
00088 }
00089 else
00090 {
00091 LFATAL("with file %s: %s\n [errno %d: %s]\n",
00092 this->filename, msg.c_str(), errno, strerror(errno));
00093 }
00094 }
00095
00096 bool isGray() const
00097 {
00098 return this->colorType == PNG_COLOR_TYPE_GRAY
00099 || this->colorType == PNG_COLOR_TYPE_GRAY_ALPHA;
00100 }
00101
00102 bool isColor() const
00103 {
00104 return this->colorType == PNG_COLOR_TYPE_RGB
00105 || this->colorType == PNG_COLOR_TYPE_RGB_ALPHA
00106 || this->colorType == PNG_COLOR_TYPE_PALETTE;
00107 }
00108
00109 const char* filename;
00110 FILE* file;
00111 png_structp pngPtr;
00112 png_infop infoPtr;
00113 png_infop endPtr;
00114 int width;
00115 int height;
00116 int bitDepth;
00117 png_byte colorType;
00118 int rowBytes;
00119 int numChannels;
00120
00121 };
00122
00123
00124 PngParser::PngParser(const char* filename)
00125 :
00126 rep(new Rep(filename))
00127 {
00128 errno = 0;
00129 rep->file = fopen(filename, "rb");
00130 if (rep->file == 0) rep->onError("couldn't open file for png reading");
00131
00132 const size_t nheader = 8;
00133 png_byte header[nheader];
00134
00135 if (fread(header, 1, nheader, rep->file) != nheader) rep->onError("short read on png file header");
00136
00137 int is_png = !png_sig_cmp(header, 0, nheader);
00138 if (!is_png) rep->onError("file was not a png image file");
00139
00140 rep->pngPtr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
00141 if (rep->pngPtr == 0) rep->onError("png_create_read_struct failed");
00142
00143 rep->infoPtr = png_create_info_struct(rep->pngPtr);
00144 if (rep->infoPtr == 0) rep->onError("png_create_info_struct failed");
00145
00146 rep->endPtr = png_create_info_struct(rep->pngPtr);
00147 if (rep->endPtr == 0) rep->onError("png_create_info_struct failed");
00148
00149 png_init_io(rep->pngPtr, rep->file);
00150
00151 png_set_sig_bytes(rep->pngPtr, nheader);
00152
00153 png_read_info(rep->pngPtr, rep->infoPtr);
00154
00155 rep->bitDepth = png_get_bit_depth(rep->pngPtr, rep->infoPtr);
00156
00157 rep->colorType = png_get_color_type(rep->pngPtr, rep->infoPtr);
00158
00159 if (rep->bitDepth == 16)
00160 png_set_strip_16(rep->pngPtr);
00161 else if (rep->bitDepth < 8 && (rep->colorType == PNG_COLOR_TYPE_GRAY))
00162 png_set_gray_1_2_4_to_8(rep->pngPtr);
00163 else if (rep->bitDepth != 8 && (rep->colorType != PNG_COLOR_TYPE_PALETTE))
00164 rep->onError(sformat("invalid bit-depth(%d)/color-mode(%d) "
00165 "combination", rep->bitDepth, rep->colorType));
00166
00167
00168 if (rep->colorType & PNG_COLOR_MASK_ALPHA)
00169 png_set_strip_alpha(rep->pngPtr);
00170
00171 if (rep->colorType & PNG_COLOR_MASK_PALETTE)
00172 png_set_palette_to_rgb(rep->pngPtr);
00173
00174
00175 png_read_update_info(rep->pngPtr, rep->infoPtr);
00176
00177
00178
00179 rep->width = png_get_image_width(rep->pngPtr, rep->infoPtr);
00180 rep->height = png_get_image_height(rep->pngPtr, rep->infoPtr);
00181
00182 rep->rowBytes = png_get_rowbytes(rep->pngPtr, rep->infoPtr);
00183
00184 rep->numChannels = png_get_channels(rep->pngPtr, rep->infoPtr);
00185
00186 ASSERT(rep->rowBytes = rep->width*rep->numChannels);
00187
00188
00189 #if defined(HAVE_PNG_ASM_FLAGS)
00190 const png_uint_32 flags = png_get_asm_flags(rep->pngPtr);
00191 const png_uint_32 mask =
00192 png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE);
00193 png_set_asm_flags(rep->pngPtr, flags | mask);
00194 #endif
00195 }
00196
00197
00198 PngParser::~PngParser()
00199 {
00200 rep->close();
00201
00202 delete rep;
00203 }
00204
00205
00206 GenericFrameSpec PngParser::getFrameSpec() const
00207 {
00208 GenericFrameSpec result;
00209
00210 if (rep->isGray()) result.nativeType = GenericFrame::GRAY_U8;
00211 else if (rep->isColor()) result.nativeType = GenericFrame::RGB_U8;
00212 else rep->onError("unsupported image type (neither grayscale nor RGB)");
00213
00214 result.videoFormat = VIDFMT_AUTO;
00215 result.videoByteSwap = false;
00216 result.dims = Dims(rep->width, rep->height);
00217 result.floatFlags = 0;
00218
00219 return result;
00220 }
00221
00222
00223 std::string PngParser::getComments() const
00224 { return "PNG comments not currently supported"; }
00225
00226
00227 uint PngParser::getTagCount() const
00228 { return 0; }
00229
00230
00231 bool PngParser::getTag(uint tag, std::string &name, std::string &value) const
00232 { return false; }
00233
00234
00235 GenericFrame PngParser::getFrame()
00236 {
00237 if (rep->isGray()) return GenericFrame(parseGray());
00238 else if (rep->isColor()) return GenericFrame(parseRGB());
00239
00240 rep->onError("unsupported image type (neither grayscale nor RGB)");
00241 return GenericFrame();
00242 }
00243
00244
00245 Image<byte> PngParser::parseGray()
00246 {
00247 ASSERT(!rep->closed());
00248 ASSERT(rep->isGray());
00249 ASSERT(rep->rowBytes == rep->width);
00250
00251 errno = 0;
00252
00253 Image<byte> result(rep->width, rep->height, NO_INIT);
00254
00255 png_bytep* row_pointers = new png_bytep[rep->height];
00256
00257 for (int i = 0; i < rep->height; ++i)
00258 {
00259 row_pointers[i] = (png_bytep) (result.getArrayPtr() + rep->width*i);
00260 }
00261
00262 png_read_image(rep->pngPtr, row_pointers);
00263
00264 png_read_end(rep->pngPtr, rep->endPtr);
00265
00266 delete [] row_pointers;
00267
00268 rep->close();
00269
00270 return result;
00271 }
00272
00273
00274 Image<PixRGB<byte> > PngParser::parseRGB()
00275 {
00276 ASSERT(!rep->closed());
00277 ASSERT(rep->isColor());
00278 ASSERT(rep->rowBytes == rep->width*3);
00279
00280 errno = 0;
00281
00282 Image<PixRGB<byte> > result(rep->width, rep->height, NO_INIT);
00283
00284 png_bytep* row_pointers = new png_bytep[rep->height];
00285
00286 for (int i = 0; i < rep->height; ++i)
00287 {
00288 row_pointers[i] = (png_bytep) (result.getArrayPtr() + rep->width*i);
00289 }
00290
00291 png_read_image(rep->pngPtr, row_pointers);
00292
00293 png_read_end(rep->pngPtr, rep->endPtr);
00294
00295 delete [] row_pointers;
00296
00297 rep->close();
00298
00299 return result;
00300 }
00301
00302 #endif // INVT_HAVE_LIBPNG
00303
00304
00305
00306
00307
00308