00001
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005
00006
00007
00008
00009 #include "vil1_png.h"
00010
00011 #include <vcl_cassert.h>
00012 #include <vcl_cstring.h>
00013 #include <vcl_iostream.h>
00014
00015 #include <vil1/vil1_stream.h>
00016 #include <vil1/vil1_image_impl.h>
00017 #include <vil1/vil1_image.h>
00018 #include <vil1/vil1_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* vil1_png_format_tag = "png";
00032
00033
00034 static bool problem(char const* msg)
00035 {
00036 vcl_cerr << "[vil1_png: PROBLEM " <<msg << ']';
00037 return false;
00038 }
00039
00040 vil1_image_impl* vil1_png_file_format::make_input_image(vil1_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 vil1_png_generic_image(is);
00053 }
00054
00055 vil1_image_impl* vil1_png_file_format::make_output_image(vil1_stream* is, int planes,
00056 int width,
00057 int height,
00058 int components,
00059 int bits_per_component,
00060 vil1_component_format format)
00061 {
00062 return new vil1_png_generic_image(is, planes, width, height, components, bits_per_component, format);
00063 }
00064
00065 char const* vil1_png_file_format::tag() const
00066 {
00067 return vil1_png_format_tag;
00068 }
00069
00070
00071
00072 static void user_read_data(png_structp png_ptr, png_bytep data, png_size_t length)
00073 {
00074 vil1_stream* f = (vil1_stream*)png_get_io_ptr(png_ptr);
00075 f->read(data, length);
00076 }
00077
00078 static void user_write_data(png_structp png_ptr, png_bytep data, png_size_t length)
00079 {
00080 vil1_stream* f = (vil1_stream*)png_get_io_ptr(png_ptr);
00081 f->write(data, length);
00082 }
00083
00084 static void user_flush_data(png_structp )
00085 {
00086 vcl_cerr << "vil1_png_generic_image::user_flush_data() NYI\n";
00087 #if 0 // NYI
00088 IOFile* f = (IOFile*)png_get_io_ptr(png_ptr);
00089
00090 #endif
00091 }
00092
00093 struct vil1_jmpbuf_wrapper
00094 {
00095 jmp_buf jmpbuf;
00096 };
00097 static vil1_jmpbuf_wrapper pngtopnm_jmpbuf_struct;
00098 static bool jmpbuf_ok = false;
00099
00100
00101 #define png_setjmp_on(ACTION) \
00102 do {\
00103 jmpbuf_ok = true;\
00104 if (setjmp (pngtopnm_jmpbuf_struct.jmpbuf) != 0) {\
00105 problem("png_setjmp_on");\
00106 ACTION;\
00107 }\
00108 } while (false);
00109 #define png_setjmp_off() (jmpbuf_ok = false)
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120 static void pngtopnm_error_handler (png_structp png_ptr, png_const_charp msg)
00121 {
00122 vcl_cerr << "vil1_png: fatal libpng error: " << msg << '\n';
00123
00124 if (!jmpbuf_ok) {
00125
00126 vcl_cerr << "vil1_png: jmpbuf is pretty far from ok. returning\n";
00127
00128 return;
00129 }
00130
00131 vil1_jmpbuf_wrapper *jmpbuf_ptr = (vil1_jmpbuf_wrapper*) png_get_error_ptr(png_ptr);
00132 if (jmpbuf_ptr == NULL) {
00133 vcl_cerr << "pnmtopng: EXTREMELY fatal error: jmpbuf unrecoverable; terminating.\n";
00134 vcl_exit(99);
00135 }
00136
00137 longjmp(jmpbuf_ptr->jmpbuf, 1);
00138 }
00139
00140 struct vil1_png_structures
00141 {
00142 bool reading_;
00143 png_struct *png_ptr;
00144 png_info *info_ptr;
00145 png_byte **rows;
00146 int channels;
00147 bool ok;
00148
00149 vil1_png_structures(bool reading)
00150 {
00151 reading_ = reading;
00152 png_ptr = 0;
00153 info_ptr = 0;
00154 rows = 0;
00155 channels = 0;
00156 ok = false;
00157
00158 png_setjmp_on(return);
00159
00160 if (reading)
00161 png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
00162 else
00163 png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, &pngtopnm_jmpbuf_struct, pngtopnm_error_handler, NULL);
00164
00165 if (!png_ptr) {
00166 problem("cannot allocate LIBPNG structure");
00167 return;
00168 }
00169
00170 info_ptr = png_create_info_struct (png_ptr);
00171 if (!info_ptr) {
00172 problem("cannot allocate LIBPNG structures");
00173 return;
00174 }
00175
00176 ok = true;
00177
00178
00179 png_setjmp_off();
00180 }
00181
00182 bool alloc_image()
00183 {
00184 rows = new png_byte* [info_ptr->height];
00185 if (rows == 0)
00186 return ok = problem("couldn't allocate space for image");
00187
00188 unsigned long linesize;
00189 if (png_get_bit_depth( png_ptr, info_ptr ) == 16)
00190 linesize = 2 * info_ptr->width;
00191 else
00192 linesize = info_ptr->width;
00193
00194 if (png_get_color_type( png_ptr, info_ptr ) == PNG_COLOR_TYPE_GRAY_ALPHA)
00195 linesize *= 2;
00196 else
00197 if (png_get_color_type( png_ptr, info_ptr ) == PNG_COLOR_TYPE_RGB)
00198 linesize *= 3;
00199 else
00200 if (png_get_color_type( png_ptr, info_ptr ) == PNG_COLOR_TYPE_RGB_ALPHA)
00201 linesize *= 4;
00202
00203 unsigned int height = png_get_image_height(png_ptr,info_ptr);
00204
00205 rows[0] = new png_byte[linesize * height];
00206 if (!rows[0])
00207 return ok = problem("couldn't allocate space for image");
00208
00209
00210 for (unsigned int y = 1; y < height; ++y)
00211 rows[y] = rows[0] + y * linesize;
00212
00213 return true;
00214 }
00215
00216 png_byte** get_rows()
00217 {
00218 if (reading_) {
00219 if (!rows) {
00220 if (alloc_image()) {
00221 png_setjmp_on(return 0);
00222 png_read_image (png_ptr, rows);
00223 png_read_end (png_ptr, info_ptr);
00224 png_setjmp_off();
00225 }
00226 }
00227 } else {
00228 assert(rows != 0);
00229 }
00230
00231 return rows;
00232 }
00233
00234 ~vil1_png_structures()
00235 {
00236 png_setjmp_on(goto del);
00237 if (reading_) {
00238
00239 png_destroy_read_struct (&png_ptr, &info_ptr, (png_infopp)NULL);
00240
00241 } else {
00242
00243 png_write_image(png_ptr, rows);
00244 png_write_end(png_ptr, info_ptr);
00245
00246 png_destroy_write_struct (&png_ptr, &info_ptr);
00247 }
00248 png_setjmp_off();
00249
00250 del:
00251 if (rows) {
00252 delete [] rows[0];
00253 delete [] rows;
00254 }
00255 }
00256 };
00257
00258
00259
00260
00261 vil1_png_generic_image::vil1_png_generic_image(vil1_stream* is)
00262 : vs_(is),
00263 p(new vil1_png_structures(true))
00264 {
00265 vs_->ref();
00266 read_header();
00267 }
00268
00269 bool vil1_png_generic_image::get_property(char const *tag, void *prop) const
00270 {
00271 if (0==vcl_strcmp(tag, vil1_property_top_row_first))
00272 return prop ? (*(bool*)prop) = true : true;
00273
00274 if (0==vcl_strcmp(tag, vil1_property_left_first))
00275 return prop ? (*(bool*)prop) = true : true;
00276
00277 return false;
00278 }
00279
00280 vil1_png_generic_image::vil1_png_generic_image(vil1_stream* is, int ,
00281 int width,
00282 int height,
00283 int components,
00284 int bits_per_component,
00285 vil1_component_format )
00286 : vs_(is),
00287 width_(width),
00288 height_(height),
00289 components_(components),
00290 bits_per_component_(bits_per_component),
00291 p(new vil1_png_structures(false))
00292 {
00293 vs_->ref();
00294 write_header();
00295 }
00296
00297 vil1_png_generic_image::~vil1_png_generic_image()
00298 {
00299 delete p;
00300 vs_->unref();
00301 }
00302
00303 char const* vil1_png_generic_image::file_format() const
00304 {
00305 return vil1_png_format_tag;
00306 }
00307
00308 bool vil1_png_generic_image::read_header()
00309 {
00310 if (!p->ok)
00311 return false;
00312
00313 png_setjmp_on(return false);
00314
00315 vs_->seek(0L);
00316 png_byte sig_buf [SIG_CHECK_SIZE];
00317 if (vs_->read(sig_buf, SIG_CHECK_SIZE) != SIG_CHECK_SIZE) {
00318 png_setjmp_off();
00319 return problem("Initial header fread");
00320 }
00321
00322 if (png_sig_cmp (sig_buf, (png_size_t) 0, (png_size_t) SIG_CHECK_SIZE) != 0) {
00323 png_setjmp_off();
00324 return problem("png_sig_cmp");
00325 }
00326
00327 png_set_read_fn(p->png_ptr, vs_, user_read_data);
00328 png_set_sig_bytes (p->png_ptr, SIG_CHECK_SIZE);
00329 png_read_info (p->png_ptr, p->info_ptr);
00330
00331 if (png_get_bit_depth( p->png_ptr, p->info_ptr ) < 8)
00332 png_set_packing (p->png_ptr);
00333
00334 #if 0
00335 int maxval;
00336 if (p->info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { maxval = 255; }
00337 else { maxval = (1l << p->info_ptr->bit_depth) - 1; }
00338 #endif
00339
00340 p->channels = png_get_channels(p->png_ptr, p->info_ptr);
00341
00342 #if VXL_LITTLE_ENDIAN
00343
00344 if ( png_get_bit_depth( p->png_ptr, p->info_ptr ) > 8 )
00345 png_set_swap( p->png_ptr );
00346 #endif
00347
00348 this->width_ = png_get_image_width( p->png_ptr, p->info_ptr );
00349 this->height_ = png_get_image_height( p->png_ptr, p->info_ptr );
00350 this->components_ = png_get_channels( p->png_ptr, p->info_ptr );
00351 this->bits_per_component_ = png_get_bit_depth( p->png_ptr, p->info_ptr );
00352
00353 if (png_get_valid( p->png_ptr, p->info_ptr, PNG_INFO_sBIT )) problem("LAZY AWF! PNG_INFO_sBIT");
00354
00355
00356 png_setjmp_off();
00357 return true;
00358 }
00359
00360 bool vil1_png_generic_image::write_header()
00361 {
00362 if (!p->ok)
00363 return false;
00364
00365 png_setjmp_on( return false );
00366
00367 vs_->seek(0L);
00368
00369 png_set_write_fn(p->png_ptr, vs_, user_write_data, user_flush_data);
00370
00371 int color_type;
00372 if (components_ == 3)
00373 color_type = PNG_COLOR_TYPE_RGB;
00374 else
00375 color_type = PNG_COLOR_TYPE_GRAY;
00376
00377 png_set_IHDR(p->png_ptr, p->info_ptr, width_, height_, bits_per_component_, color_type,
00378 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
00379
00380 png_write_info(p->png_ptr, p->info_ptr);
00381
00382 #if VXL_LITTLE_ENDIAN
00383
00384 if ( bits_per_component_ > 8 )
00385 png_set_swap( p->png_ptr );
00386 #endif
00387
00388
00389 p->channels = components_;
00390 p->alloc_image();
00391
00392 png_setjmp_off();
00393
00394 return true;
00395 }
00396
00397 bool vil1_png_generic_image::get_section(void* buf, int x0, int y0, int xs, int ys) const
00398 {
00399 if (!p->ok)
00400 return false;
00401
00402
00403 png_byte** rows = p->get_rows();
00404 if (!rows) return 0;
00405
00406 int bytes_per_pixel = png_get_bit_depth(p->png_ptr,p->info_ptr) * p->channels / 8;
00407 int bytes_per_row_dst = xs*bytes_per_pixel;
00408 if ((unsigned int)xs == png_get_image_width(p->png_ptr, p->info_ptr)) {
00409 assert(x0 == 0);
00410 vcl_memcpy(buf, rows[y0], ys * bytes_per_row_dst);
00411 }
00412 else {
00413 png_byte* dst = (png_byte*)buf;
00414 for (int y = 0; y < ys; ++y, dst += bytes_per_row_dst)
00415 vcl_memcpy(dst, &rows[y0+y][x0*bytes_per_pixel], xs*bytes_per_pixel);
00416 }
00417
00418 return true;
00419 }
00420
00421 bool vil1_png_generic_image::put_section(void const* buf, int x0, int y0, int xs, int ys)
00422 {
00423 if (!p->ok)
00424 return false;
00425
00426
00427
00428 png_byte** rows = p->get_rows();
00429 if (!rows) return false;
00430
00431 int bytes_per_pixel = png_get_bit_depth(p->png_ptr,p->info_ptr) * p->channels / 8;
00432 int bytes_per_row_dst = xs*bytes_per_pixel;
00433 if ((unsigned int)xs == png_get_image_width(p->png_ptr, p->info_ptr)) {
00434 assert(x0 == 0);
00435 vcl_memcpy(rows[y0], buf, ys * bytes_per_row_dst);
00436 }
00437 else {
00438 const png_byte* dst = (const png_byte*)buf;
00439 for (int y = 0; y < ys; ++y, dst += bytes_per_row_dst)
00440 vcl_memcpy(&rows[y0+y][x0*bytes_per_pixel], dst, xs*bytes_per_pixel);
00441 }
00442
00443 return true;
00444 }
00445
00446 vil1_image vil1_png_generic_image::get_plane(unsigned int plane) const
00447 {
00448 assert(plane == 0);
00449 return const_cast<vil1_png_generic_image*>(this);
00450 }