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 static vil_jmpbuf_wrapper pngtopnm_jmpbuf_struct;
00105 static bool jmpbuf_ok = false;
00106
00107
00108 #define png_setjmp_on(ACTION) \
00109 do {\
00110 jmpbuf_ok = true;\
00111 if (setjmp (pngtopnm_jmpbuf_struct.jmpbuf) != 0) {\
00112 problem("png_setjmp_on");\
00113 ACTION;\
00114 }\
00115 } while (false);
00116 #define png_setjmp_off() (jmpbuf_ok = false)
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127 static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
00128 {
00129 vcl_cerr << "vil_png: fatal libpng error: " << msg << '\n';
00130
00131 if (!jmpbuf_ok) {
00132
00133 vcl_cerr << "vil_png: jmpbuf is pretty far from ok. returning\n";
00134
00135 return;
00136 }
00137
00138 vil_jmpbuf_wrapper *jmpbuf_ptr = static_cast<vil_jmpbuf_wrapper*>(png_get_error_ptr(png_ptr));
00139 if (jmpbuf_ptr == NULL) {
00140 vcl_cerr << "pnmtopng: EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n";
00141 vcl_exit(99);
00142 }
00143
00144 longjmp(jmpbuf_ptr->jmpbuf, 1);
00145 }
00146
00147 struct vil_png_structures
00148 {
00149 bool reading_;
00150 png_struct *png_ptr;
00151 png_info *info_ptr;
00152 png_byte **rows;
00153 int channels;
00154 bool ok;
00155
00156 vil_png_structures(bool reading)
00157 {
00158 reading_ = reading;
00159 png_ptr = 0;
00160 info_ptr = 0;
00161 rows = 0;
00162 channels = 0;
00163 ok = false;
00164
00165 png_setjmp_on(return);
00166
00167 if (reading)
00168 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
00169 else
00170 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
00171
00172 if (!png_ptr) {
00173 problem("cannot allocate LIBPNG structure");
00174 return;
00175 }
00176
00177 info_ptr = png_create_info_struct (png_ptr);
00178 if (!info_ptr) {
00179 png_destroy_read_struct(&png_ptr, NULL, NULL);
00180 problem("cannot allocate LIBPNG structures");
00181 return;
00182 }
00183
00184 ok = true;
00185
00186
00187 png_setjmp_off();
00188 }
00189
00190 bool alloc_image()
00191 {
00192 rows = new png_byte* [info_ptr->height];
00193 if (rows == 0)
00194 return ok = problem("couldn't allocate space for image");
00195
00196 unsigned long linesize;
00197 if (png_get_bit_depth(png_ptr, info_ptr) == 16)
00198 linesize = 2 * info_ptr->width;
00199 else
00200 linesize = info_ptr->width;
00201
00202 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_GRAY_ALPHA)
00203 linesize *= 2;
00204 else
00205 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB)
00206 linesize *= 3;
00207 else
00208 if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB_ALPHA)
00209 linesize *= 4;
00210
00211 unsigned int height = png_get_image_height(png_ptr,info_ptr);
00212
00213 rows[0] = new png_byte[linesize * height];
00214 if (!rows[0])
00215 return ok = problem("couldn't allocate space for image");
00216
00217
00218 for (unsigned int y = 1; y < height; ++y)
00219 rows[y] = rows[0] + y * linesize;
00220
00221 return true;
00222 }
00223
00224 png_byte** get_rows()
00225 {
00226 if (reading_) {
00227 if (!rows) {
00228 if (alloc_image()) {
00229 png_setjmp_on(return 0);
00230 png_read_image (png_ptr, rows);
00231 png_read_end (png_ptr, info_ptr);
00232 png_setjmp_off();
00233 }
00234 }
00235 } else {
00236 assert(rows != 0);
00237 }
00238
00239 return rows;
00240 }
00241
00242 ~vil_png_structures()
00243 {
00244 png_setjmp_on(goto del);
00245 if (reading_) {
00246
00247 png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
00248
00249 } else {
00250
00251 png_write_image(png_ptr, rows);
00252 png_write_end(png_ptr, info_ptr);
00253
00254 png_destroy_write_struct (&png_ptr, &info_ptr);
00255 }
00256 png_setjmp_off();
00257
00258 del:
00259 if (rows) {
00260 delete [] rows[0];
00261 delete [] rows;
00262 }
00263 }
00264 };
00265
00266
00267
00268
00269 vil_png_image::vil_png_image(vil_stream* is)
00270 : vs_(is),
00271 p_(new vil_png_structures(true))
00272 {
00273 vs_->ref();
00274 read_header();
00275 }
00276
00277 bool vil_png_image::get_property(char const *key, void * value) const
00278 {
00279 if (vcl_strcmp(vil_property_quantisation_depth, key)==0)
00280 {
00281 if (value)
00282 *static_cast<unsigned int*>(value) = bits_per_component_;
00283 return true;
00284 }
00285
00286 return false;
00287 }
00288
00289 vil_png_image::vil_png_image(vil_stream *s,
00290 unsigned nx,
00291 unsigned ny,
00292 unsigned nplanes,
00293 enum vil_pixel_format format)
00294 : vs_(s),
00295 width_(nx),
00296 height_(ny),
00297 components_(nplanes),
00298 format_(format),
00299 p_(new vil_png_structures(false))
00300 {
00301 if (format == VIL_PIXEL_FORMAT_BOOL) bits_per_component_ = 1;
00302 else bits_per_component_ = vil_pixel_format_sizeof_components(format) * 8;
00303
00304 vs_->ref();
00305 write_header();
00306 assert(format == VIL_PIXEL_FORMAT_BYTE ||
00307 format == VIL_PIXEL_FORMAT_UINT_16);
00308
00309 }
00310
00311 vil_png_image::~vil_png_image()
00312 {
00313 delete p_;
00314 vs_->unref();
00315 }
00316
00317
00318 char const* vil_png_image::file_format() const
00319 {
00320 return vil_png_format_tag;
00321 }
00322
00323 bool vil_png_image::read_header()
00324 {
00325 if (!p_->ok)
00326 return false;
00327
00328 png_setjmp_on(return false);
00329
00330 vs_->seek(0L);
00331 png_byte sig_buf [SIG_CHECK_SIZE];
00332 if (vs_->read(sig_buf, SIG_CHECK_SIZE) != SIG_CHECK_SIZE) {
00333 png_setjmp_off();
00334 return problem("Initial header fread");
00335 }
00336
00337 if (png_sig_cmp (sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE) != 0) {
00338 png_setjmp_off();
00339 return problem("png_sig_cmp");
00340 }
00341
00342 png_set_read_fn(p_->png_ptr, vs_, user_read_data);
00343 png_set_sig_bytes (p_->png_ptr, SIG_CHECK_SIZE);
00344 png_read_info (p_->png_ptr, p_->info_ptr);
00345
00346 if (png_get_bit_depth(p_->png_ptr, p_->info_ptr) < 8)
00347 png_set_packing (p_->png_ptr);
00348
00349 #if 0
00350 int maxval;
00351 if (p->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { maxval = 255; }
00352 else { maxval = (1l << p->info_ptr->bit_depth) - 1; }
00353 #endif
00354
00355 p_->channels = png_get_channels(p_->png_ptr, p_->info_ptr);
00356
00357 #if VXL_LITTLE_ENDIAN
00358
00359 if ( png_get_bit_depth(p_->png_ptr, p_->info_ptr) > 8 )
00360 png_set_swap(p_->png_ptr);
00361 #endif
00362
00363 this->width_ = png_get_image_width(p_->png_ptr, p_->info_ptr);
00364 this->height_ = png_get_image_height(p_->png_ptr, p_->info_ptr);
00365 this->components_ = png_get_channels(p_->png_ptr, p_->info_ptr);
00366 this->bits_per_component_ = png_get_bit_depth(p_->png_ptr, p_->info_ptr);
00367
00368 png_color_8p sig_bit;
00369 if (png_get_sBIT(p_->png_ptr, p_->info_ptr, &sig_bit)) {
00370 png_byte max_bits = sig_bit->red;
00371 max_bits = vcl_max( max_bits, sig_bit->green );
00372 max_bits = vcl_max( max_bits, sig_bit->blue );
00373 max_bits = vcl_max( max_bits, sig_bit->gray );
00374 max_bits = vcl_max( max_bits, sig_bit->alpha );
00375 this->bits_per_component_ = max_bits;
00376
00377 png_set_shift(p_->png_ptr, sig_bit);
00378 }
00379
00380 if (this->bits_per_component_ == 1) format_ = VIL_PIXEL_FORMAT_BOOL;
00381 else if (this->bits_per_component_ <=8) format_=VIL_PIXEL_FORMAT_BYTE;
00382 else format_ = VIL_PIXEL_FORMAT_UINT_16;
00383
00384
00385
00386 png_setjmp_off();
00387 return true;
00388 }
00389
00390 bool vil_png_image::write_header()
00391 {
00392 if (!p_->ok)
00393 return false;
00394
00395 png_setjmp_on( return false );
00396
00397 vs_->seek(0L);
00398
00399 png_set_write_fn(p_->png_ptr, vs_, user_write_data, user_flush_data);
00400
00401 int color_type;
00402 if (components_ == 4)
00403 color_type = PNG_COLOR_TYPE_RGB_ALPHA;
00404 else if (components_ == 3)
00405 color_type = PNG_COLOR_TYPE_RGB;
00406 else if (components_ == 2)
00407 color_type = PNG_COLOR_TYPE_GRAY_ALPHA;
00408 else
00409 color_type = PNG_COLOR_TYPE_GRAY;
00410
00411 png_set_IHDR(p_->png_ptr, p_->info_ptr, width_, height_, bits_per_component_, color_type,
00412 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00413
00414 png_write_info(p_->png_ptr, p_->info_ptr);
00415
00416 #if VXL_LITTLE_ENDIAN
00417
00418 if ( bits_per_component_ > 8 )
00419 png_set_swap(p_->png_ptr);
00420 #endif
00421
00422
00423 p_->channels = components_;
00424 p_->alloc_image();
00425
00426 png_setjmp_off();
00427
00428 return true;
00429 }
00430
00431 vil_image_view_base_sptr vil_png_image::get_copy_view(unsigned x0,
00432 unsigned nx,
00433 unsigned y0,
00434 unsigned ny) const
00435 {
00436 if (!p_->ok)
00437 return 0;
00438
00439
00440 png_byte** rows = p_->get_rows();
00441 if (!rows) return 0;
00442
00443 int bit_depth = ((bits_per_component_ + 7)/8)*8;
00444 int bytes_per_pixel = (bit_depth * p_->channels + 7) / 8;
00445 int bytes_per_row_dst = nx*nplanes() * vil_pixel_format_sizeof_components(format_);
00446
00447 vil_memory_chunk_sptr chunk = new vil_memory_chunk(ny*bytes_per_row_dst, format_);
00448
00449 if ((bit_depth==16) &&
00450 (nx == png_get_image_width(p_->png_ptr, p_->info_ptr)))
00451 {
00452 assert(x0 == 0);
00453
00454 vcl_memcpy(reinterpret_cast<char*>(chunk->data()), rows[y0], ny * bytes_per_row_dst);
00455 return new vil_image_view<vxl_uint_16>(chunk, reinterpret_cast<vxl_uint_16*>(chunk->data()),
00456 nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00457 }
00458 if ((bit_depth ==8) &&
00459 (nx == png_get_image_width(p_->png_ptr, p_->info_ptr)))
00460 {
00461 assert(x0 == 0);
00462
00463 vcl_memcpy(reinterpret_cast<char*>(chunk->data()), rows[y0], ny * bytes_per_row_dst);
00464 return new vil_image_view<vxl_byte>(chunk, reinterpret_cast<vxl_byte*>(chunk->data()),
00465 nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00466 }
00467 else if (bit_depth==16)
00468 {
00469 png_byte* dst = reinterpret_cast<png_byte*>(chunk->data());
00470 for (unsigned y = 0; y < ny; ++y, dst += bytes_per_row_dst)
00471 vcl_memcpy(dst, &rows[y0+y][x0*bytes_per_pixel], nx*bytes_per_pixel);
00472 return new vil_image_view<vxl_uint_16>(chunk, reinterpret_cast<vxl_uint_16*>(chunk->data()),
00473 nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00474 }
00475 else if (bit_depth==8)
00476 {
00477 png_byte* dst = reinterpret_cast<png_byte*>(chunk->data());
00478 for (unsigned y = 0; y < ny; ++y, dst += bytes_per_row_dst)
00479 vcl_memcpy(dst, &rows[y0+y][x0*bytes_per_pixel], nx*bytes_per_pixel);
00480 return new vil_image_view<vxl_byte>(chunk, reinterpret_cast<vxl_byte*>(chunk->data()),
00481 nx, ny, nplanes(), nplanes(), nplanes()*nx, 1);
00482 }
00483
00484 else return 0;
00485 }
00486
00487 bool vil_png_image::put_view(const vil_image_view_base &view,
00488 unsigned x0, unsigned y0)
00489 {
00490 if (!view_fits(view, x0, y0))
00491 {
00492 vil_exception_warning(vil_exception_out_of_bounds("vil_png_image::put_view"));
00493 return false;
00494 }
00495
00496 if (!p_->ok) return false;
00497
00498
00499
00500 png_byte** rows = p_->get_rows();
00501 if (!rows) return false;
00502
00503
00504
00505 if (bits_per_component_ == 8)
00506 {
00507 if (view.pixel_format() != VIL_PIXEL_FORMAT_BYTE) return false;
00508 const vil_image_view<vxl_byte> &view2 = static_cast<const vil_image_view<vxl_byte>&>(view);
00509 if (nplanes()==1)
00510 {
00511 for (unsigned y = 0; y < view.nj(); ++y)
00512 for (unsigned x=0; x < view.ni(); ++x)
00513 rows[y0+y][x0+x] = view2(x,y);
00514 }
00515 else if (nplanes()==2)
00516 {
00517 for (unsigned y = 0; y < view.nj(); ++y)
00518 for (unsigned x=0; x < view.ni(); ++x)
00519 {
00520 rows[y0+y][(x0+x)*2] = view2(x,y,0);
00521 rows[y0+y][(x0+x)*2+1] = view2(x,y,1);
00522 }
00523 }
00524 else if (nplanes()==3)
00525 {
00526 for (unsigned y = 0; y < view.nj(); ++y)
00527 for (unsigned x=0; x < view.ni(); ++x)
00528 {
00529 rows[y0+y][(x0+x)*3] = view2(x,y,0);
00530 rows[y0+y][(x0+x)*3+1] = view2(x,y,1);
00531 rows[y0+y][(x0+x)*3+2] = view2(x,y,2);
00532 }
00533 }
00534 else
00535 {
00536 assert(nplanes() == 4);
00537 for (unsigned y = 0; y < view.nj(); ++y)
00538 for (unsigned x=0; x < view.ni(); ++x)
00539 {
00540 rows[y0+y][(x0+x)*4] = view2(x,y,0);
00541 rows[y0+y][(x0+x)*4+1] = view2(x,y,1);
00542 rows[y0+y][(x0+x)*4+2] = view2(x,y,2);
00543 rows[y0+y][(x0+x)*4+3] = view2(x,y,3);
00544 }
00545 }
00546 }
00547 else if (bits_per_component_ == 16)
00548 {
00549 if (view.pixel_format() != VIL_PIXEL_FORMAT_UINT_16) return false;
00550 const vil_image_view<vxl_uint_16> &view2 = static_cast<const vil_image_view<vxl_uint_16>&>(view);
00551 if (nplanes()==1)
00552 {
00553 for (unsigned y = 0; y < view.nj(); ++y)
00554 for (unsigned x=0; x < view.ni(); ++x)
00555 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*2]) = view2(x,y);
00556 }
00557 else if (nplanes() == 3)
00558 {
00559 for (unsigned y = 0; y < view.nj(); ++y)
00560 for (unsigned x=0; x < view.ni(); ++x)
00561 {
00562 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*6]) = view2(x,y,0);
00563 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*6+2]) = view2(x,y,1);
00564 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*6+4]) = view2(x,y,2);
00565 }
00566 }
00567 else
00568 {
00569 assert(nplanes() == 4);
00570 for (unsigned y = 0; y < view.nj(); ++y)
00571 for (unsigned x=0; x < view.ni(); ++x)
00572 {
00573 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8]) = view2(x,y,0);
00574 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8+2]) = view2(x,y,1);
00575 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8+4]) = view2(x,y,2);
00576 *reinterpret_cast<vxl_uint_16*>(&rows[y0+y][(x0+x)*8+6]) = view2(x,y,3);
00577 }
00578 }
00579 }
00580
00581 else return false;
00582
00583 return true;
00584 }