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 #include "ioPNG.hpp"
00033 #include <vlCore/LoadWriterManager.hpp>
00034 #include <vlCore/VisualizationLibrary.hpp>
00035 #include <vlCore/FileSystem.hpp>
00036 #include <vlCore/VirtualFile.hpp>
00037 #include <vlCore/Image.hpp>
00038 #include <png.h>
00039
00040 using namespace vl;
00041
00042 namespace
00043 {
00044 void png_read_vfile(png_structp png_ptr, png_bytep data, png_size_t byte_count)
00045 {
00046 VirtualFile* vfile = (VirtualFile*)png_get_io_ptr(png_ptr);
00047 vfile->read(data, byte_count);
00048 }
00049 void png_write_vfile(png_structp png_ptr, png_bytep data, png_size_t byte_count)
00050 {
00051 VirtualFile* vfile = (VirtualFile*)png_get_io_ptr(png_ptr);
00052 vfile->write(data,byte_count);
00053 }
00054 void png_flush_vfile(png_structp )
00055 {
00056
00057 }
00058 void vl_error_fn(png_structp , png_const_charp error_msg)
00059 {
00060 Log::error( Say("libPNG: %s\n") << error_msg);
00061 }
00062 void vl_warning_fn(png_structp , png_const_charp warning_msg)
00063 {
00064 Log::warning( Say("libPNG: %s\n") << warning_msg);
00065 }
00066 }
00067
00068
00069 ref<Image> vl::loadPNG(const String& path)
00070 {
00071 ref<VirtualFile> file = defFileSystem()->locateFile(path);
00072 if ( !file )
00073 {
00074 Log::error( Say("File '%s' not found.\n") << path );
00075 return NULL;
00076 }
00077 else
00078 return loadPNG(file.get());
00079 }
00080
00081 ref<Image> vl::loadPNG(VirtualFile* file)
00082 {
00083 if ( !file->open(OM_ReadOnly) )
00084 {
00085 Log::error( Say("loadPNG: cannot load PNG file '%s'\n") << file->path() );
00086 return NULL;
00087 }
00088
00089 png_structp png_ptr;
00090 png_infop info_ptr;
00091 png_infop endinfo;
00092
00093 png_uint_32 width, height;
00094 int bit_depth, color_type;
00095
00096
00097
00098
00099
00100
00101
00102 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00103 png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), vl_error_fn, vl_warning_fn);
00104
00105 if (png_ptr == NULL)
00106 {
00107 file->close();
00108 return NULL;
00109 }
00110
00111
00112 info_ptr = png_create_info_struct(png_ptr);
00113 endinfo = png_create_info_struct(png_ptr);
00114 VL_CHECK(info_ptr)
00115 VL_CHECK(endinfo)
00116
00117
00118
00119 #if 0
00120 if (setjmp(png_jmpbuf(png_ptr)))
00121 {
00122
00123 png_destroy_read_struct(&png_ptr, &info_ptr, &endinfo);
00124 file->close();
00125
00126 return NULL;
00127 }
00128 #endif
00129
00130 unsigned char header[8];
00131 int count = (int)file->read(header,8);
00132 if (count == 8 && png_check_sig(header, 8))
00133 {
00134 png_set_read_fn(png_ptr,file,png_read_vfile);
00135 png_set_sig_bytes(png_ptr, 8);
00136 }
00137 else
00138 {
00139 png_destroy_read_struct(&png_ptr, &info_ptr, &endinfo);
00140 file->close();
00141 return NULL;
00142 }
00143
00144
00145
00146
00147 png_read_info(png_ptr, info_ptr);
00148
00149 png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL);
00150
00151 ref<Image> img = new Image;
00152 img->setObjectName(file->path().toStdString().c_str());
00153
00154 if (bit_depth == 16)
00155 {
00156 switch(color_type)
00157 {
00158 case PNG_COLOR_TYPE_GRAY: img->allocate2D(width, height, 1, IF_LUMINANCE, IT_UNSIGNED_SHORT); break;
00159 case PNG_COLOR_TYPE_GRAY_ALPHA: img->allocate2D(width, height, 1, IF_LUMINANCE_ALPHA, IT_UNSIGNED_SHORT); break;
00160 case PNG_COLOR_TYPE_PALETTE: img->allocate2D(width, height, 1, IF_RGB, IT_UNSIGNED_SHORT); break;
00161 case PNG_COLOR_TYPE_RGB: img->allocate2D(width, height, 1, IF_RGB, IT_UNSIGNED_SHORT); break;
00162 case PNG_COLOR_TYPE_RGB_ALPHA: img->allocate2D(width, height, 1, IF_RGBA, IT_UNSIGNED_SHORT); break;
00163 default:
00164 VL_TRAP()
00165 break;
00166 }
00167 }
00168 else
00169 {
00170 switch(color_type)
00171 {
00172 case PNG_COLOR_TYPE_GRAY: img->allocate2D(width, height, 1, IF_LUMINANCE, IT_UNSIGNED_BYTE); break;
00173 case PNG_COLOR_TYPE_GRAY_ALPHA: img->allocate2D(width, height, 1, IF_LUMINANCE_ALPHA, IT_UNSIGNED_BYTE); break;
00174 case PNG_COLOR_TYPE_PALETTE: img->allocate2D(width, height, 1, IF_RGBA, IT_UNSIGNED_BYTE); break;
00175 case PNG_COLOR_TYPE_RGB: img->allocate2D(width, height, 1, IF_RGB, IT_UNSIGNED_BYTE); break;
00176 case PNG_COLOR_TYPE_RGB_ALPHA: img->allocate2D(width, height, 1, IF_RGBA, IT_UNSIGNED_BYTE); break;
00177 default:
00178 VL_TRAP()
00179 break;
00180 }
00181 }
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00196
00197
00198
00199
00200 png_set_packing(png_ptr);
00201
00202
00203
00204
00205
00206
00207 if (color_type == PNG_COLOR_TYPE_PALETTE)
00208 png_set_palette_to_rgb(png_ptr);
00209
00210
00211 if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8)
00212 png_set_expand_gray_1_2_4_to_8(png_ptr);
00213
00214 #if 1
00215
00216
00217
00218 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS))
00219 png_set_tRNS_to_alpha(png_ptr);
00220 #endif
00221
00222 #if 0
00223
00224
00225
00226
00227
00228
00229 png_color_16 my_background, *image_background;
00230 if (png_get_bKGD(png_ptr, info_ptr, &image_background))
00231 png_set_background(png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0);
00232 else
00233 png_set_background(png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1.0);
00234 #endif
00235
00236
00237 double screen_gamma = 2.2;
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249 double image_gamma;
00250 if (png_get_gAMA(png_ptr, info_ptr, &image_gamma))
00251 png_set_gamma(png_ptr, screen_gamma, image_gamma);
00252 else
00253 png_set_gamma(png_ptr, screen_gamma, 1.0/screen_gamma);
00254
00255 #if 0
00256
00257
00258
00259 if (color_type & PNG_COLOR_MASK_COLOR)
00260 {
00261 int num_palette;
00262 png_colorp palette;
00263
00264 * This reduces the image to the application supplied palette */
00265 if ()
00266 {
00267
00268 png_color std_color_cube[MAX_SCREEN_COLORS];
00269
00270 png_set_dither(png_ptr, std_color_cube, MAX_SCREEN_COLORS,
00271 MAX_SCREEN_COLORS, png_uint_16p_NULL, 0);
00272 }
00273 * This reduces the image to the palette supplied in the file */
00274 else
00275 if (png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette))
00276 {
00277 png_uint_16p histogram = NULL;
00278
00279 png_get_hIST(png_ptr, info_ptr, &histogram);
00280
00281 png_set_dither(png_ptr, palette, num_palette,
00282 max_screen_colors, histogram, 0);
00283 }
00284 }
00285 #endif
00286
00287 #if 0
00288
00289 png_set_invert_mono(png_ptr);
00290 #endif
00291
00292 #if 0
00293
00294
00295
00296
00297 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_sBIT))
00298 {
00299 png_color_8p sig_bit;
00300
00301 png_get_sBIT(png_ptr, info_ptr, &sig_bit);
00302 png_set_shift(png_ptr, sig_bit);
00303 }
00304 #endif
00305
00306 #if 0
00307
00308 if (color_type & PNG_COLOR_MASK_COLOR)
00309 png_set_bgr(png_ptr);
00310 #endif
00311
00312 #if 0
00313
00314 png_set_swap_alpha(png_ptr);
00315 #endif
00316
00317 #if 1
00318
00319 unsigned short bet = 0x00FF;
00320 bool little_endian_cpu = ((unsigned char*)&bet)[0] == 0xFF;
00321 if (little_endian_cpu && bit_depth > 8)
00322 png_set_swap(png_ptr);
00323 #endif
00324
00325 #if 0
00326
00327 png_set_filler(png_ptr, 0xff, PNG_FILLER_AFTER);
00328 #endif
00329
00331
00332
00333
00334
00335
00337
00338
00339
00340
00341
00343
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364 std::vector<png_bytep> row_p;
00365 row_p.resize(height);
00366 for(unsigned i=0; i<height; ++i)
00367 row_p[height - 1 - i] = (png_bytep)img->pixels()+img->pitch()*i;
00368
00369 png_read_image(png_ptr, &row_p[0]);
00370 png_read_end(png_ptr, endinfo);
00371
00372
00373 png_destroy_read_struct(&png_ptr, &info_ptr, &endinfo);
00374
00375 file->close();
00376 return img;
00377 }
00378
00379 bool vl::isPNG(VirtualFile* file)
00380 {
00381 if ( !file->open(OM_ReadOnly) )
00382 {
00383
00384 return false;
00385 }
00386
00387 unsigned char header[8];
00388 int count = (int)file->read(header,8);
00389 file->close();
00390 if (count == 8 && png_check_sig(header, 8))
00391 return true;
00392 else
00393 return false;
00394 }
00395
00396 bool vl::savePNG(const Image* src, const String& path, int compression)
00397 {
00398 ref<DiskFile> file = new DiskFile(path);
00399 return savePNG(src, file.get(), compression);
00400 }
00401
00402 bool vl::savePNG(const Image* src, VirtualFile* fout, int compression)
00403 {
00404
00405
00406
00407
00408
00409
00410 int w = src->width();
00411 int h = src->height();
00412 int d = src->depth();
00413 if (h == 0) h=1;
00414 if (d == 0) d=1;
00415 if (src->isCubemap()) d=6;
00416 h = h*d;
00417
00418 ref<Image> cimg;
00419
00420 if (src->type() == IT_FLOAT || src->type() == IT_SHORT)
00421 {
00422 cimg = src->convertType(IT_UNSIGNED_SHORT);
00423 src = cimg.get();
00424 if (!cimg)
00425 {
00426 Log::error( Say("savePNG('%s'): could not convert image to IT_UNSIGNED_SHORT.\n") << fout->path() );
00427 return false;
00428 }
00429 }
00430
00431 bool format_ok = src->format() == IF_RGB || src->format() == IF_RGBA || src->format() == IF_LUMINANCE || src->format() == IF_ALPHA || src->format() == IF_LUMINANCE_ALPHA;
00432 if (!format_ok)
00433 {
00434 cimg = src->convertFormat(IF_RGBA);
00435 src = cimg.get();
00436 if (!cimg)
00437 {
00438 Log::error( Say("savePNG('%s'): could not convert image to IF_RGBA.\n") << fout->path() );
00439 return false;
00440 }
00441 }
00442
00443 VL_CHECK(src->type() == IT_UNSIGNED_BYTE || src->type() == IT_UNSIGNED_SHORT)
00444 VL_CHECK(src->format() == IF_RGB || src->format() == IF_RGBA || src->format() == IF_LUMINANCE || src->format() == IF_ALPHA || src->format() == IF_LUMINANCE_ALPHA)
00445
00446 if(!fout->open(OM_WriteOnly))
00447 {
00448 Log::error( Say("PNG: could not write to '%s'.\n") << fout->path() );
00449 return false;
00450 }
00451
00452 png_structp png = NULL;
00453 png_infop info = NULL;
00454
00455 png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00456 if(!png)
00457 return false;
00458
00459 info = png_create_info_struct(png);
00460 if(!info)
00461 return false;
00462
00463 png_set_write_fn(png,fout,png_write_vfile,png_flush_vfile);
00464
00465 png_set_compression_level(png, compression);
00466
00467 std::vector< png_bytep > rows;
00468 rows.resize(h);
00469 for(int i=0, y = h-1; i<h; ++i,--y)
00470 rows[i] = (png_bytep)(src->pixels()+src->pitch()*y);
00471
00472 int color;
00473 switch(src->format())
00474 {
00475 case IF_RGB: color = PNG_COLOR_TYPE_RGB; break;
00476 case IF_RGBA: color = PNG_COLOR_TYPE_RGB_ALPHA; break;
00477 case IF_ALPHA:
00478 case IF_LUMINANCE: color = PNG_COLOR_TYPE_GRAY; break;
00479 case IF_LUMINANCE_ALPHA: color = PNG_COLOR_TYPE_GRAY_ALPHA; break;
00480 default:
00481 return false;
00482 }
00483
00484 int bit_depth = src->type() == IT_UNSIGNED_SHORT ? 16 : 8;
00485
00486 png_set_IHDR( png, info, w, h, bit_depth, color,
00487 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00488
00489 unsigned short bet = 0x00FF;
00490 bool little_endian_cpu = ((unsigned char*)&bet)[0] == 0xFF;
00491 if (little_endian_cpu && bit_depth == 16)
00492 {
00493 png_set_rows(png, info, &rows[0]);
00494 png_write_png(png, info, PNG_TRANSFORM_SWAP_ENDIAN, NULL);
00495 }
00496 else
00497 {
00498 png_write_info(png, info);
00499 png_write_image(png, &rows[0]);
00500 png_write_end(png, NULL);
00501 }
00502
00503 png_destroy_write_struct(&png,&info);
00504
00505 fout->close();
00506 return true;
00507 }
00508