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/PngWriter.H"
00041
00042 #include "Image/Image.H"
00043 #include "Image/Normalize.H"
00044 #include "Image/Pixels.H"
00045 #include "Raster/GenericFrame.H"
00046 #include "rutz/fstring.h"
00047 #include "rutz/sfmt.h"
00048 #include "Util/FileUtil.H"
00049
00050 #include <cerrno>
00051 #include <cstdio>
00052 #include <cstdlib>
00053 #include <png.h>
00054 #include <sys/file.h>
00055 #include <unistd.h>
00056 #include <fcntl.h>
00057
00058
00059 namespace
00060 {
00061 rutz::fstring errnoMessage()
00062 {
00063 if (errno != 0)
00064 return rutz::sfmt("\n [errno %d: %s]", errno, strerror(errno));
00065
00066 return rutz::fstring();
00067 }
00068
00069
00070
00071
00072 class PngWriterHelper
00073 {
00074 public:
00075 PngWriterHelper(const char* filename);
00076
00077 ~PngWriterHelper();
00078
00079
00080
00081
00082
00083
00084
00085 int close();
00086
00087
00088 void writeGray(const Image<byte>& img);
00089
00090
00091 void writeRGB(const Image<PixRGB<byte> >& img);
00092
00093 private:
00094 PngWriterHelper(const PngWriterHelper&);
00095 PngWriterHelper& operator=(const PngWriterHelper&);
00096
00097 void onError(const rutz::fstring& msg);
00098
00099 const rutz::fstring itsFilename;
00100 FILE* itsFile;
00101 png_structp itsPngPtr;
00102 png_infop itsInfoPtr;
00103 };
00104
00105 PngWriterHelper::PngWriterHelper(const char* filename)
00106 :
00107 itsFilename(filename),
00108 itsFile(0),
00109 itsPngPtr(0),
00110 itsInfoPtr(0)
00111 {
00112 errno = 0;
00113
00114 itsFile = fopen(filename, "wb");
00115
00116 if (itsFile == 0)
00117 {
00118 onError(rutz::sfmt("couldn't open png file '%s' for writing%s",
00119 filename, errnoMessage().c_str()));
00120 }
00121
00122 const int lockstat = flock(fileno(itsFile),LOCK_EX);
00123
00124 if (lockstat == EBADF)
00125 {
00126 onError(rutz::sfmt("couldn't open png file '%s' for writing%s",
00127 filename, errnoMessage().c_str()));
00128 }
00129 if (lockstat == EINTR)
00130 {
00131 onError(rutz::sfmt("Lock on png file '%s' interupted %s",
00132 filename, errnoMessage().c_str()));
00133 }
00134
00135 itsPngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
00136 if (itsPngPtr == 0)
00137 {
00138 onError(rutz::sfmt("png_create_write_struct() failed for file '%s'%s",
00139 filename, errnoMessage().c_str()));
00140 }
00141
00142 itsInfoPtr = png_create_info_struct(itsPngPtr);
00143 if (itsInfoPtr == 0)
00144 {
00145 onError(rutz::sfmt("png_create_info_struct() failed for file '%s'%s",
00146 filename, errnoMessage().c_str()));
00147 }
00148
00149 png_init_io(itsPngPtr, itsFile);
00150
00151 png_set_compression_level(itsPngPtr, 9);
00152 }
00153
00154 PngWriterHelper::~PngWriterHelper()
00155 {
00156 const int status = this->close();
00157 if (status != 0)
00158
00159
00160 PLERROR("Error during fclose() while writing %s",
00161 itsFilename.c_str());
00162 }
00163
00164 int PngWriterHelper::close()
00165 {
00166 if (itsPngPtr != 0)
00167 {
00168 png_destroy_write_struct(itsPngPtr ? &itsPngPtr : 0,
00169 itsInfoPtr ? &itsInfoPtr : 0);
00170
00171 itsPngPtr = 0;
00172 itsInfoPtr = 0;
00173 }
00174
00175 if (itsFile != 0)
00176 {
00177
00178
00179
00180
00181 fsync(fileno(itsFile));
00182
00183 const int file = fileno(itsFile);
00184 const int status = fclose(itsFile);
00185 flock(file,LOCK_UN);
00186 itsFile = 0;
00187 return status;
00188 }
00189
00190 return 0;
00191 }
00192
00193 void PngWriterHelper::writeGray(const Image<byte>& img)
00194 {
00195
00196 #if defined(HAVE_PNG_ASM_FLAGS)
00197 const png_uint_32 flags = png_get_asm_flags(itsPngPtr);
00198 const png_uint_32 mask =
00199 png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE);
00200 png_set_asm_flags(itsPngPtr, flags | mask);
00201 #endif
00202
00203 png_set_IHDR(itsPngPtr, itsInfoPtr,
00204 img.getWidth(), img.getHeight(),
00205 8,
00206 PNG_COLOR_TYPE_GRAY,
00207 PNG_INTERLACE_NONE,
00208 PNG_COMPRESSION_TYPE_DEFAULT,
00209 PNG_FILTER_TYPE_DEFAULT);
00210
00211 png_write_info(itsPngPtr, itsInfoPtr);
00212
00213 const int height = img.getHeight();
00214
00215 png_bytep row_pointers[height];
00216
00217 const byte* p = img.getArrayPtr();
00218
00219 for (int i = 0; i < height; ++i)
00220 {
00221 row_pointers[i] =
00222 static_cast<png_bytep>
00223 (static_cast<void*>
00224 (const_cast<byte*>(p + i*img.getWidth())));
00225 }
00226
00227 png_write_image(itsPngPtr, &row_pointers[0]);
00228 png_write_end(itsPngPtr, itsInfoPtr);
00229
00230 const int status = this->close();
00231 if (status != 0)
00232 PLFATAL("Error during fclose() while writing %s",
00233 itsFilename.c_str());
00234 }
00235
00236 void PngWriterHelper::writeRGB(const Image<PixRGB<byte> >& img)
00237 {
00238
00239
00240 #if defined(HAVE_PNG_ASM_FLAGS)
00241 const png_uint_32 flags = png_get_asm_flags(itsPngPtr);
00242 const png_uint_32 mask =
00243 png_get_asm_flagmask(PNG_SELECT_READ | PNG_SELECT_WRITE);
00244 png_set_asm_flags(itsPngPtr, flags | mask);
00245 #endif
00246
00247 png_set_IHDR(itsPngPtr, itsInfoPtr,
00248 img.getWidth(), img.getHeight(),
00249 8,
00250 PNG_COLOR_TYPE_RGB,
00251 PNG_INTERLACE_NONE,
00252 PNG_COMPRESSION_TYPE_DEFAULT,
00253 PNG_FILTER_TYPE_DEFAULT);
00254
00255 png_write_info(itsPngPtr, itsInfoPtr);
00256
00257 const int height = img.getHeight();
00258
00259 png_bytep row_pointers[height];
00260
00261 const PixRGB<byte>* p = img.getArrayPtr();
00262
00263 for (int i = 0; i < height; ++i)
00264 {
00265 row_pointers[i] =
00266 static_cast<png_bytep>
00267 (static_cast<void*>
00268 (const_cast<PixRGB<byte>*>(p + i*img.getWidth())));
00269 }
00270
00271 png_write_image(itsPngPtr, &row_pointers[0]);
00272 png_write_end(itsPngPtr, itsInfoPtr);
00273
00274 const int status = this->close();
00275 if (status != 0)
00276 PLFATAL("Error during fclose() while writing %s",
00277 itsFilename.c_str());
00278 }
00279
00280 void PngWriterHelper::onError(const rutz::fstring& msg)
00281 {
00282 const int status = this->close();
00283 if (status != 0)
00284
00285
00286 PLERROR("Error during fclose() while writing %s",
00287 itsFilename.c_str());
00288
00289 LFATAL("%s", msg.c_str());
00290 }
00291
00292 }
00293
00294 PngWriter::PngWriter() {}
00295
00296 PngWriter::~PngWriter() {}
00297
00298 std::string PngWriter::writeFrame(const GenericFrame& image,
00299 const std::string& fname)
00300 {
00301 switch (image.nativeType())
00302 {
00303 case GenericFrame::NONE:
00304 LFATAL("cannot write an empty image to '%s'", fname.c_str());
00305 break;
00306
00307 case GenericFrame::RGB_U8:
00308 case GenericFrame::RGBD:
00309 PngWriter::writeRGB(image.asRgbU8(), fname);
00310 break;
00311
00312 case GenericFrame::RGB_U16:
00313 PngWriter::writeRGB(image.asRgbU16(), fname);
00314 break;
00315
00316 case GenericFrame::RGB_F32:
00317 PngWriter::writeRGB(image.asRgbU8(), fname);
00318 break;
00319
00320 case GenericFrame::GRAY_U8:
00321 PngWriter::writeGray(image.asGrayU8(), fname);
00322 break;
00323
00324 case GenericFrame::GRAY_U16:
00325 PngWriter::writeGray(image.asGrayU16(), fname);
00326 break;
00327
00328 case GenericFrame::GRAY_F32:
00329 if ((image.floatFlags() & FLOAT_NORM_PRESERVE)
00330 && !(image.floatFlags() & FLOAT_NORM_0_255))
00331 {
00332
00333
00334
00335
00336
00337 const GenericFrame image2(image.asGrayF32(),
00338 image.floatFlags() | FLOAT_NORM_0_255);
00339 PngWriter::writeGray(image2.asGrayU8(), fname);
00340 }
00341 else
00342 PngWriter::writeGray(image.asGrayU8(), fname);
00343 break;
00344
00345 case GenericFrame::VIDEO:
00346 PngWriter::writeRGB(image.asRgbU8(), fname);
00347 break;
00348 }
00349
00350 return fname;
00351 }
00352
00353 void PngWriter::writeRGB(const Image<PixRGB<byte> >& image,
00354 const std::string& fname)
00355 {
00356 PngWriterHelper writer(fname.c_str());
00357 writer.writeRGB(image);
00358 }
00359
00360 void PngWriter::writeGray(const Image<byte>& image,
00361 const std::string& fname)
00362 {
00363 PngWriterHelper writer(fname.c_str());
00364 writer.writeGray(image);
00365 }
00366
00367 #endif // INVT_HAVE_LIBPNG
00368
00369
00370
00371
00372
00373
00374