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