00001
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005
00006
00007
00008
00009 #include "vil_png.h"
00010
00011 #include <vcl_cassert.h>
00012 #include <vcl_cstring.h>
00013 #include <vcl_iostream.h>
00014 #include <vcl_algorithm.h>
00015
00016 #include <vil/vil_stream.h>
00017 #include <vil/vil_image_view.h>
00018 #include <vil/vil_property.h>
00019 #include <vil/vil_exception.h>
00020
00021 #include <png.h>
00022 #if (PNG_LIBPNG_VER_MAJOR == 0)
00023 extern "You need a later libpng. You should rerun CMake, after setting VXL_FORCE_V3P_PNG to ON."
00024 #endif
00025 #include <vcl_cstdlib.h>
00026
00027 #include <vxl_config.h>
00028
00029
00030 #define SIG_CHECK_SIZE 4
00031
00032 char const* vil_png_format_tag = "png";
00033
00034
00035 static bool problem(char const* msg)
00036 {
00037 vcl_cerr << "[vil_png: PROBLEM " <<msg << ']';
00038 return false;
00039 }
00040
00041 vil_image_resource_sptr vil_png_file_format::make_input_image(vil_stream* is)
00042 {
00043
00044 png_byte sig_buf [SIG_CHECK_SIZE];
00045 if (is->read(sig_buf, SIG_CHECK_SIZE) != SIG_CHECK_SIZE) {
00046 problem("Initial header fread");
00047 return 0;
00048 }
00049
00050 if (png_sig_cmp (sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE) != 0)
00051 return 0;
00052
00053 return new vil_png_image(is);
00054 }
00055
00056 vil_image_resource_sptr vil_png_file_format::make_output_image(vil_stream* vs,
00057 unsigned nx,
00058 unsigned ny,
00059 unsigned nplanes,
00060 enum vil_pixel_format format)
00061 {
00062 if (format != VIL_PIXEL_FORMAT_BYTE &&
00063 format != VIL_PIXEL_FORMAT_UINT_16)
00064
00065
00066 {
00067 vcl_cout<<"ERROR! vil_png_file_format::make_output_image()\n"
00068 <<"Pixel format should be byte, but is "<<format<<" instead.\n";
00069 return 0;
00070 }
00071
00072 return new vil_png_image(vs, nx, ny, nplanes, format);
00073 }
00074
00075 char const* vil_png_file_format::tag() const
00076 {
00077 return vil_png_format_tag;
00078 }
00079
00080
00081
00082 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
00083 {
00084 vil_stream* f = static_cast<vil_stream*>(png_get_io_ptr(png_ptr));
00085 f->read(data, length);
00086 }
00087
00088 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
00089 {
00090 vil_stream* f = static_cast<vil_stream*>(png_get_io_ptr(png_ptr));
00091 f->write(data, length);
00092 }
00093
00094 static void user_flush_data(png_structp )
00095 {
00096
00097
00098 }
00099
00100 struct vil_jmpbuf_wrapper
00101 {
00102 jmp_buf jmpbuf;
00103 };
00104
00105 static vil_jmpbuf_wrapper pngtopnm_jmpbuf_struct;
00106 static bool jmpbuf_ok = false;
00107
00108
00109 #define png_setjmp_on(ACTION) \
00110 do {\
00111 jmpbuf_ok = true;\
00112 if (setjmp (pngtopnm_jmpbuf_struct.jmpbuf) != 0) {\
00113 problem("png_setjmp_on");\
00114 ACTION;\
00115 }\
00116 } while (false);
00117 #define png_setjmp_off() (jmpbuf_ok = false)
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128 static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
00129 {
00130 vcl_cerr << "vil_png: fatal libpng error: " << msg << '\n';
00131
00132 if (!jmpbuf_ok) {
00133
00134 vcl_cerr << "vil_png: jmpbuf is pretty far from ok. returning\n";
00135
00136 return;
00137 }
00138
00139 vil_jmpbuf_wrapper *jmpbuf_ptr = static_cast<vil_jmpbuf_wrapper*>(png_get_error_ptr(png_ptr));
00140 if (jmpbuf_ptr == NULL) {
00141 vcl_cerr << "pnmtopng: EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n";
00142 vcl_exit(99);
00143 }
00144
00145 longjmp(jmpbuf_ptr->jmpbuf, 1);
00146 }
00147
00148 struct vil_png_structures
00149 {
00150 bool reading_;
00151 png_struct *png_ptr;
00152 png_info *info_ptr;
00153 png_byte **rows;
00154 int channels;
00155 bool ok;
00156
00157 vil_png_structures(bool reading)
00158 {
00159 reading_ = reading;
00160 png_ptr = 0;
00161 info_ptr = 0;
00162 rows = 0;
00163 channels = 0;
00164 ok = false;
00165
00166 png_setjmp_on(return);
00167
00168 if (reading)
00169 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
00170 else
00171 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
00172
00173 if (!png_ptr) {
00174 problem("cannot allocate LIBPNG structure");
00175 return;
00176 }
00177
00178 info_ptr = png_create_info_struct (png_ptr);
00179 if (!info_ptr) {
00180 png_destroy_read_struct(&png_ptr, NULL, NULL);
00181 problem("cannot allocate LIBPNG structures");
00182 return;
00183 }
00184
00185 ok = true;
00186
00187
00188 png_setjmp_off();
00189 }
00190
00191 bool alloc_image()
00192 {
00193 rows = new png_byte* [png_get_image_height(png_ptr, info_ptr)];
00194 if (rows == 0)
00195 return ok = problem("couldn't allocate space for image");
00196
00197 unsigned long linesize;
00198 if (png_get_bit_depth(png_ptr, info_ptr) == 16)
00199 linesize = 2 * png_get_image_width(png_ptr, info_ptr);
00200 else
00201 linesize = png_get_image_width(png_ptr, info_ptr);
00202
00203 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA)
00204 linesize *= 2;
00205 else
00206 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB)
00207 linesize *= 3;
00208 else
00209 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB_ALPHA)
00210 linesize *= 4;
00211
00212 unsigned int height = png_get_image_height(png_ptr,info_ptr);
00213
00214 rows[0] = new png_byte[linesize * height];
00215 if (!rows[0])
00216 return ok = problem("couldn't allocate space for image");
00217
00218
00219 for (unsigned int y = 1; y < height; ++y)
00220 rows[y] = rows[0] + y * linesize;
00221
00222 return true;
00223 }
00224
00225 png_byte** get_rows()
00226 {
00227 if (reading_) {
00228 if (!rows) {
00229 if (alloc_image()) {
00230 png_setjmp_on(return 0);
00231 png_read_image (png_ptr, rows);
00232 png_read_end (png_ptr, info_ptr);
00233 png_setjmp_off();
00234 }
00235 }
00236 }
00237 else {
00238 assert(rows != 0);
00239 }
00240
00241 return rows;
00242 }
00243
00244 ~vil_png_structures()
00245 {
00246 png_setjmp_on(goto del);
00247 if (reading_) {
00248
00249 png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
00250 }
00251 else {
00252
00253 png_write_image(png_ptr, rows);
00254 png_write_end(png_ptr, info_ptr);
00255
00256 png_destroy_write_struct (&png_ptr, &info_ptr);
00257 }
00258 png_setjmp_off();
00259
00260 del:
00261 if (rows) {
00262 delete [] rows[0];
00263 delete [] rows;
00264 }
00265 }
00266 };
00267
00268
00269
00270
00271 vil_png_image::vil_png_image(vil_stream* is)
00272 : vs_(is),
00273 p_(new vil_png_structures(true))
00274 {
00275 vs_->ref();
00276 read_header();
00277 }
00278
00279 bool vil_png_image::get_property(char const *key, void * value) const
00280 {
00281 if (vcl_strcmp(vil_property_quantisation_depth, key)==0)
00282 {
00283 if (value)
00284 *static_cast<unsigned int*>(value) = bits_per_component_;
00285 return true;
00286 }
00287
00288 return false;
00289 }
00290
00291 vil_png_image::vil_png_image(vil_stream *s,
00292 unsigned nx,
00293 unsigned ny,
00294 unsigned nplanes,
00295 enum vil_pixel_format format)
00296 : vs_(s),
00297 width_(nx),
00298 height_(ny),
00299 components_(nplanes),
00300 format_(format),
00301 p_(new vil_png_structures(false))
00302 {
00303 if (format == VIL_PIXEL_FORMAT_BOOL) bits_per_component_ = 1;
00304 else bits_per_component_ = vil_pixel_format_sizeof_components(format) * 8;
00305
00306 vs_->ref();
00307 write_header();
00308 assert(format == VIL_PIXEL_FORMAT_BYTE ||
00309 format == VIL_PIXEL_FORMAT_UINT_16);
00310
00311 }
00312
00313 vil_png_image::~vil_png_image()
00314 {
00315 delete p_;
00316 vs_->unref();
00317 }
00318
00319
00320 char const* vil_png_image::file_format() const
00321 {
00322 return vil_png_format_tag;
00323 }
00324
00325 bool vil_png_image::read_header()
00326 {
00327 if (!p_->ok)
00328 return false;
00329
00330 png_setjmp_on(return false);
00331
00332 vs_->seek(0L);
00333 png_byte sig_buf [SIG_CHECK_SIZE];
00334 if (vs_->read(sig_buf, SIG_CHECK_SIZE) != SIG_CHECK_SIZE) {
00335 png_setjmp_off();
00336 return problem("Initial header fread");
00337 }
00338
00339 if (png_sig_cmp (sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE) != 0) {
00340 png_setjmp_off();
00341 return problem("png_sig_cmp");
00342 }
00343
00344
00345
00346 png_set_read_fn(p_->png_ptr, vs_, user_read_data);
00347 png_set_sig_bytes (p_->png_ptr, SIG_CHECK_SIZE);
00348 png_read_info (p_->png_ptr, p_->info_ptr);
00349
00350
00351 png_byte const color_type = png_get_color_type(p_->png_ptr, p_->info_ptr);
00352 png_byte const bit_depth = png_get_bit_depth(p_->png_ptr, p_->info_ptr);
00353 bool is_bool_image = false;
00354
00355 #if 1
00356 if (color_type == PNG_COLOR_TYPE_PALETTE) {
00357 assert( bit_depth <= 8 );
00358 png_set_palette_to_rgb(p_->png_ptr);
00359 }
00360 if (color_type == PNG_COLOR_TYPE_GRAY) {
00361 if (bit_depth==1) {
00362 is_bool_image = true;
00363 png_set_packing(p_->png_ptr);
00364 }
00365 else if (bit_depth < 8)
00366 png_set_expand_gray_1_2_4_to_8(p_->png_ptr);
00367 }
00368 if (png_get_valid(p_->png_ptr, p_->info_ptr, PNG_INFO_tRNS)) {
00369 int channels = png_get_channels(p_->png_ptr, p_->info_ptr);
00370 assert( channels == 1 || channels == 3 );
00371 png_set_tRNS_to_alpha(p_->png_ptr);
00372 }
00373 #else
00374
00375
00376
00377 if (png_get_bit_depth(p_->png_ptr, p_->info_ptr) < 8)
00378 png_set_packing (p_->png_ptr);
00379 #endif
00380
00381 #if VXL_LITTLE_ENDIAN
00382
00383 if ( png_get_bit_depth(p_->png_ptr, p_->info_ptr) > 8 )
00384 png_set_swap(p_->png_ptr);
00385 #endif
00386
00387 png_color_8p sig_bit;
00388 if (png_get_valid(p_->png_ptr, p_->info_ptr, PNG_INFO_sBIT) && png_get_sBIT(p_->png_ptr, p_->info_ptr, &sig_bit)) {
00389 png_byte max_bits = sig_bit->red;
00390 max_bits = vcl_max( max_bits, sig_bit->green );
00391 max_bits = vcl_max( max_bits, sig_bit->blue );
00392 max_bits = vcl_max( max_bits, sig_bit->gray );
00393 max_bits = vcl_max( max_bits, sig_bit->alpha );
00394 png_set_shift(p_->png_ptr, sig_bit);
00395 }
00396
00397
00398
00399
00400
00401 png_read_update_info(p_->png_ptr, p_->info_ptr);
00402
00403 this->width_ = png_get_image_width(p_->png_ptr, p_->info_ptr);
00404 this->height_ = png_get_image_height(p_->png_ptr, p_->info_ptr);
00405 this->components_ = p_->channels = png_get_channels(p_->png_ptr, p_->info_ptr);
00406 this->bits_per_component_ = png_get_bit_depth(p_->png_ptr, p_->info_ptr);
00407
00408
00409 if (is_bool_image)
00410 this->bits_per_component_ = 1;
00411
00412 if (this->bits_per_component_ == 1) format_ = VIL_PIXEL_FORMAT_BOOL;
00413 else if (this->bits_per_component_==8) format_ = VIL_PIXEL_FORMAT_BYTE;
00414 else if (this->bits_per_component_==16) format_ = VIL_PIXEL_FORMAT_UINT_16;
00415 else return problem("Bad bit depth");
00416
00417
00418 png_setjmp_off();
00419 return true;
00420 }
00421
00422 bool vil_png_image::write_header()
00423 {
00424 if (!p_->ok)
00425 return false;
00426
00427 png_setjmp_on( return false );
00428
00429 vs_->seek(0L);
00430
00431 png_set_write_fn(p_->png_ptr, vs_, user_write_data, user_flush_data);
00432
00433 int color_type;
00434 if (components_ == 4)
00435 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
00436 else if (components_ == 3)
00437 color_type = PNG_COLOR_TYPE_RGB;
00438 else if (components_ == 2)
00439 color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
00440 else
00441 color_type = PNG_COLOR_TYPE_GRAY;
00442
00443 png_set_IHDR(p_->png_ptr, p_->info_ptr, width_, height_, bits_per_component_, color_type,
00444 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00445
00446 png_write_info(p_->png_ptr, p_->info_ptr);
00447
00448 #if VXL_LITTLE_ENDIAN
00449
00450 if ( bits_per_component_ > 8 )
00451 png_set_swap(p_->png_ptr);
00452 #endif
00453
00454
00455 p_->channels = components_;
00456 p_->alloc_image();
00457
00458 png_setjmp_off();
00459
00460 return true;
00461 }
00462
00463 vil_image_view_base_sptr vil_png_image::get_copy_view(unsigned x0,
00464 unsigned nx,
00465 unsigned y0,
00466 unsigned ny) const
00467 {
00468 if (!p_->ok)
00469 return 0;
00470
00471
00472 png_byte** rows = p_->get_rows();
00473 if (!rows) return 0;
00474
00475 int bit_depth = bits_per_component_;
00476 int bytes_per_pixel = (bit_depth * p_->channels + 7) / 8;
00477 int bytes_per_row_dst = nx*nplanes() * vil_pixel_format_sizeof_components(format_);
00478
00479 vil_memory_chunk_sptr chunk = new vil_memory_chunk(ny*bytes_per_row_dst, format_);
00480
00481 if (nx == png_get_image_width(p_->png_ptr, p_->info_ptr))
00482 {
00483 assert(x0 == 0);
00484
00485 if (bit_depth==1)
00486 {
00487 assert(format_==VIL_PIXEL_FORMAT_BOOL);
00488
00489 vcl_memcpy(reinterpret_cast<char*>(chunk->data()), rows[y0], ny * bytes_per_row_dst);
00490 return new vil_image_view<bool>(chunk, reinterpret_cast<bool*>(chunk->data()),
00491 nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00492 }
00493 else if (bit_depth==16)
00494 {
00495 assert(format_==VIL_PIXEL_FORMAT_UINT_16);
00496
00497 vcl_memcpy(reinterpret_cast<char*>(chunk->data()), rows[y0], ny * bytes_per_row_dst);
00498 return new vil_image_view<vxl_uint_16>(chunk, reinterpret_cast<vxl_uint_16*>(chunk->data()),
00499 nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00500 }
00501 else if (bit_depth ==8)
00502 {
00503 vcl_memcpy(reinterpret_cast<char*>(chunk->data()), rows[y0], ny * bytes_per_row_dst);
00504 return new vil_image_view<vxl_byte>(chunk, reinterpret_cast<vxl_byte*>(chunk->data()),
00505 nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00506 }
00507 else return 0;
00508 }
00509 else
00510 {
00511 if (bit_depth==1)
00512 {
00513 assert(format_==VIL_PIXEL_FORMAT_BOOL);
00514
00515 png_byte* dst = reinterpret_cast<png_byte*>(chunk->data());
00516 for (unsigned y = 0; y < ny; ++y, dst += bytes_per_row_dst)
00517 vcl_memcpy(dst, &rows[y0+y][x0*bytes_per_pixel], nx*bytes_per_pixel);
00518 return new vil_image_view<bool>(chunk, reinterpret_cast<bool*>(chunk->data()),
00519 nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00520 }
00521 else if (bit_depth==16)
00522 {
00523 assert(format_==VIL_PIXEL_FORMAT_UINT_16);
00524
00525 png_byte* dst = reinterpret_cast<png_byte*>(chunk->data());
00526 for (unsigned y = 0; y < ny; ++y, dst += bytes_per_row_dst)
00527 vcl_memcpy(dst, &rows[y0+y][x0*bytes_per_pixel], nx*bytes_per_pixel);
00528 return new vil_image_view<vxl_uint_16>(chunk, reinterpret_cast<vxl_uint_16*>(chunk->data()),
00529 nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00530 }
00531 else if (bit_depth==8)
00532 {
00533 png_byte* dst = reinterpret_cast<png_byte*>(chunk->data());
00534 for (unsigned y = 0; y < ny; ++y, dst += bytes_per_row_dst)
00535 vcl_memcpy(dst, &rows[y0+y][x0*bytes_per_pixel], nx*bytes_per_pixel);
00536 return new vil_image_view<vxl_byte>(chunk, reinterpret_cast<vxl_byte*>(chunk->data()),
00537 nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00538 }
00539 else return 0;
00540 }
00541 }
00542
00543 bool vil_png_image::put_view(const vil_image_view_base &view,
00544 unsigned x0, unsigned y0)
00545 {
00546 if (!view_fits(view, x0, y0))
00547 {
00548 vil_exception_warning(vil_exception_out_of_bounds("vil_png_image::put_view"));
00549 return false;
00550 }
00551
00552 if (!p_->ok) return false;
00553
00554
00555
00556 png_byte** rows = p_->get_rows();
00557 if (!rows) return false;
00558
00559
00560
00561 if (bits_per_component_ == 8)
00562 {
00563 if (view.pixel_format() != VIL_PIXEL_FORMAT_BYTE) return false;
00564 const vil_image_view<vxl_byte> &view2 = static_cast<const vil_image_view<vxl_byte>&>(view);
00565 if (nplanes()==1)
00566 {
00567 for (unsigned y = 0; y < view.nj(); ++y)
00568 for (unsigned x=0; x < view.ni(); ++x)
00569 rows[y0+y][x0+x] = view2(x,y);
00570 }
00571 else if (nplanes()==2)
00572 {
00573 for (unsigned y = 0; y < view.nj(); ++y)
00574 for (unsigned x=0; x < view.ni(); ++x)
00575 {
00576 rows[y0+y][(x0+x)*2] = view2(x,y,0);
00577 rows[y0+y][(x0+x)*2+1] = view2(x,y,1);
00578 }
00579 }
00580 else if (nplanes()==3)
00581 {
00582 for (unsigned y = 0; y < view.nj(); ++y)
00583 for (unsigned x=0; x < view.ni(); ++x)
00584 {
00585 rows[y0+y][(x0+x)*3] = view2(x,y,0);
00586 rows[y0+y][(x0+x)*3+1] = view2(x,y,1);
00587 rows[y0+y][(x0+x)*3+2] = view2(x,y,2);
00588 }
00589 }
00590 else
00591 {
00592 assert(nplanes() == 4);
00593 for (unsigned y = 0; y < view.nj(); ++y)
00594 for (unsigned x=0; x < view.ni(); ++x)
00595 {
00596 rows[y0+y][(x0+x)*4] = view2(x,y,0);
00597 rows[y0+y][(x0+x)*4+1] = view2(x,y,1);
00598 rows[y0+y][(x0+x)*4+2] = view2(x,y,2);
00599 rows[y0+y][(x0+x)*4+3] = view2(x,y,3);
00600 }
00601 }
00602 }
00603 else if (bits_per_component_ == 16)
00604 {
00605 if (view.pixel_format() != VIL_PIXEL_FORMAT_UINT_16) return false;
00606 const vil_image_view<vxl_uint_16> &view2 = static_cast<const vil_image_view<vxl_uint_16>&>(view);
00607 if (nplanes()==1)
00608 {
00609 for (unsigned y = 0; y < view.nj(); ++y)
00610 for (unsigned x=0; x < view.ni(); ++x)
00611 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*2]) = view2(x,y);
00612 }
00613 else if (nplanes() == 3)
00614 {
00615 for (unsigned y = 0; y < view.nj(); ++y)
00616 for (unsigned x=0; x < view.ni(); ++x)
00617 {
00618 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*6]) = view2(x,y,0);
00619 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*6+2]) = view2(x,y,1);
00620 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*6+4]) = view2(x,y,2);
00621 }
00622 }
00623 else
00624 {
00625 assert(nplanes() == 4);
00626 for (unsigned y = 0; y < view.nj(); ++y)
00627 for (unsigned x=0; x < view.ni(); ++x)
00628 {
00629 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8]) = view2(x,y,0);
00630 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8+2]) = view2(x,y,1);
00631 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8+4]) = view2(x,y,2);
00632 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8+6]) = view2(x,y,3);
00633 }
00634 }
00635 }
00636
00637 else return false;
00638
00639 return true;
00640 }