00001
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
00033
00034
00035
00036 #ifdef EM_PNG
00037
00038 #include <climits>
00039 #include "pngio.h"
00040 #include "geometry.h"
00041 #include "util.h"
00042
00043
00044 using namespace EMAN;
00045
00046 PngIO::PngIO(const string & file, IOMode rw)
00047 : filename(file), rw_mode(rw), png_file(0), initialized(false),
00048 png_ptr(0), info_ptr(0), end_info(0), nx(0), ny(0),
00049 depth_type(PNG_INVALID_DEPTH), number_passes(0), rendermin(0), rendermax(0)
00050 {}
00051
00052 PngIO::~PngIO()
00053 {
00054 if (png_file) {
00055 fclose(png_file);
00056 png_file = 0;
00057 }
00058
00059 png_ptr = 0;
00060 info_ptr = 0;
00061 end_info = 0;
00062 }
00063
00064 void PngIO::init()
00065 {
00066 ENTERFUNC;
00067 if (initialized) {
00068 return;
00069 }
00070
00071 initialized = true;
00072
00073 bool is_new_file = false;
00074 png_file = sfopen(filename, rw_mode, &is_new_file, true);
00075
00076 if (!is_new_file) {
00077 png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
00078 }
00079 else {
00080 png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
00081 }
00082
00083 if (!png_ptr) {
00084 throw ImageReadException(filename, "cannot initialize libpng data structure");
00085 }
00086
00087 info_ptr = png_create_info_struct(png_ptr);
00088 if (!info_ptr) {
00089 throw ImageReadException(filename, "cannot create png info data structure");
00090 }
00091
00092 end_info = png_create_info_struct(png_ptr);
00093 if (!end_info) {
00094 throw ImageReadException(filename, "cannot create png end info structure");
00095 }
00096
00097 if (setjmp (png_jmpbuf (png_ptr))) {
00098 throw ImageReadException(filename, "an error occurs within png");
00099 }
00100
00101 png_init_io(png_ptr, png_file);
00102
00103 if (!is_new_file) {
00104 unsigned char header[PNG_BYTES_TO_CHECK];
00105 fread(header, sizeof(unsigned char), PNG_BYTES_TO_CHECK, png_file);
00106 if (!is_valid(header)) {
00107 throw ImageReadException(filename, "invalid PNG format");
00108 }
00109
00110 png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);
00111
00112 png_read_info(png_ptr, info_ptr);
00113
00114 nx = png_get_image_width(png_ptr, info_ptr);
00115 ny = png_get_image_height(png_ptr, info_ptr);
00116 int bit_depth = png_get_bit_depth(png_ptr, info_ptr);
00117 int color_type = png_get_color_type(png_ptr, info_ptr);
00118
00119 if (nx == 0 || ny == 0) {
00120 throw ImageReadException(filename, "PNG file size = 0");
00121 }
00122
00123 if (bit_depth == CHAR_BIT) {
00124 depth_type = PNG_CHAR_DEPTH;
00125 }
00126 else if (bit_depth == CHAR_BIT * sizeof(short)) {
00127 depth_type = PNG_SHORT_DEPTH;
00128 }
00129 else {
00130 depth_type = PNG_INVALID_DEPTH;
00131 char desc[256];
00132 sprintf(desc, "not support png with depth = %d bit", bit_depth);
00133 throw ImageReadException(filename, desc);
00134 }
00135
00136 png_set_packing(png_ptr);
00137
00138 if ((color_type == PNG_COLOR_TYPE_GRAY) && (bit_depth < CHAR_BIT)) {
00139 png_set_expand(png_ptr);
00140 }
00141
00142 number_passes = png_set_interlace_handling(png_ptr);
00143
00144 if (bit_depth > CHAR_BIT) {
00145 png_set_swap(png_ptr);
00146 }
00147
00148 png_read_update_info(png_ptr, info_ptr);
00149 }
00150 EXITFUNC;
00151 }
00152
00153 bool PngIO::is_valid(const void *first_block)
00154 {
00155 ENTERFUNC;
00156 bool result = false;
00157
00158 if (!first_block) {
00159 result = false;
00160 }
00161 else {
00162 if (png_sig_cmp((png_byte *) first_block, (png_size_t) 0, PNG_BYTES_TO_CHECK) == 0) {
00163 result = true;
00164 }
00165 }
00166 EXITFUNC;
00167 return result;
00168 }
00169
00170 int PngIO::read_header(Dict & dict, int image_index, const Region * area, bool)
00171 {
00172 ENTERFUNC;
00173
00174
00175 if(image_index == -1) {
00176 image_index = 0;
00177 }
00178
00179 if(image_index != 0) {
00180 throw ImageReadException(filename, "no stack allowed for MRC image. For take 2D slice out of 3D image, read the 3D image first, then use get_clip().");
00181 }
00182
00183 init();
00184
00185 int nx1 = static_cast < int >(nx);
00186 int ny1 = static_cast < int >(ny);
00187 check_region(area, IntSize(nx1, ny1));
00188 int xlen = 0, ylen = 0;
00189 EMUtil::get_region_dims(area, nx1, &xlen, ny1, &ylen);
00190
00191 dict["nx"] = xlen;
00192 dict["ny"] = ylen;
00193 dict["nz"] = 1;
00194
00195 if (depth_type == PNG_CHAR_DEPTH) {
00196 dict["datatype"] = EMUtil::EM_UCHAR;
00197 }
00198 else if (depth_type == PNG_SHORT_DEPTH) {
00199 dict["datatype"] = EMUtil::EM_USHORT;
00200 }
00201 else {
00202 throw ImageReadException(filename, "unsupported PNG bit depth");
00203 }
00204
00205 EXITFUNC;
00206 return 0;
00207 }
00208
00209 int PngIO::write_header(const Dict & dict, int image_index, const Region*,
00210 EMUtil::EMDataType, bool)
00211 {
00212 ENTERFUNC;
00213
00214
00215 if(image_index == -1) {
00216 image_index = 0;
00217 }
00218 if(image_index != 0) {
00219 throw ImageWriteException(filename, "PNG file does not support stack.");
00220 }
00221 check_write_access(rw_mode, image_index);
00222
00223 nx = (png_uint_32) (int) dict["nx"];
00224 ny = (png_uint_32) (int) dict["ny"];
00225 int nz = dict["nz"];
00226 if (nz != 1) {
00227 LOGERR("Only support 2D PNG file write");
00228 return 1;
00229 }
00230
00231 int bit_depth = 0;
00232 EMUtil::EMDataType datatype = (EMUtil::EMDataType) (int) dict["datatype"];
00233
00234 if (datatype == EMUtil::EM_UCHAR) {
00235 depth_type = PNG_CHAR_DEPTH;
00236 bit_depth = CHAR_BIT;
00237 }
00238 else {
00239 if (datatype != EMUtil::EM_USHORT) {
00240 LOGWARN("Don't support data type '%s' in PNG. Convert to '%s'.",
00241 EMUtil::get_datatype_string(datatype),
00242 EMUtil::get_datatype_string(EMUtil::EM_USHORT));
00243 }
00244 depth_type = PNG_SHORT_DEPTH;
00245 bit_depth = sizeof(unsigned short) * CHAR_BIT;
00246 }
00247
00248 png_set_IHDR(png_ptr, info_ptr, nx, ny, bit_depth, PNG_COLOR_TYPE_GRAY,
00249 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
00250
00251 png_write_info(png_ptr, info_ptr);
00252
00253 if (depth_type == PNG_SHORT_DEPTH) {
00254 png_set_swap(png_ptr);
00255 }
00256
00257 if(dict.has_key("render_min")) rendermin=(float)dict["render_min"];
00258 else rendermin=0;
00259 if(dict.has_key("render_max")) rendermax=(float)dict["render_max"];
00260 else rendermax=0;
00261 EXITFUNC;
00262 return 0;
00263 }
00264
00265 int PngIO::read_data(float *data, int image_index, const Region * area, bool)
00266 {
00267 ENTERFUNC;
00268
00269
00270 image_index = 0;
00271 check_read_access(image_index, data);
00272
00273 int nx1 = static_cast < int >(nx);
00274 int ny1 = static_cast < int >(ny);
00275
00276 check_region(area, IntSize(nx1, ny1));
00277
00278 png_init_io(png_ptr, png_file);
00279 png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);
00280
00281 int xlen = 0, ylen = 0, x0 = 0, y0 = 0;
00282 EMUtil::get_region_dims(area, nx1, &xlen, ny1, &ylen);
00283 EMUtil::get_region_origins(area, &x0, &y0);
00284
00285 png_uint_32 rowbytes = png_get_rowbytes(png_ptr, info_ptr);
00286 unsigned char *cdata = new unsigned char[rowbytes];
00287 unsigned short *sdata = (unsigned short *) cdata;
00288
00289 int k = 0;
00290 for (int i = y0; i < y0 + ylen; i++) {
00291 for (int pass = 0; pass < number_passes; pass++) {
00292 png_read_rows(png_ptr, (png_byte **) & cdata, 0, 1);
00293 }
00294
00295 if (depth_type == PNG_CHAR_DEPTH) {
00296 for (int x = x0; x < x0 + xlen; x++) {
00297 data[k] = static_cast < float >(cdata[x]);
00298 k++;
00299 }
00300 }
00301 else if (depth_type == PNG_SHORT_DEPTH) {
00302 for (int x = x0; x < x0 + xlen; x++) {
00303 data[k] = static_cast < float >(sdata[x]);
00304 k++;
00305 }
00306 }
00307 }
00308
00311 Util::flip_image(data, nx, ny);
00312
00313 if( cdata )
00314 {
00315 delete[]cdata;
00316 cdata = 0;
00317 }
00318
00319 png_read_end(png_ptr, end_info);
00320 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00321 EXITFUNC;
00322 return 0;
00323 }
00324
00325 int PngIO::write_data(float *data, int image_index, const Region*,
00326 EMUtil::EMDataType, bool)
00327 {
00328 ENTERFUNC;
00329
00330
00331 image_index = 0;
00332 check_write_access(rw_mode, image_index, 1, data);
00333
00334
00335 if (!rendermin && !rendermax) EMUtil::getRenderMinMax(data, nx, ny, rendermin, rendermax);
00336
00339 if (depth_type == PNG_CHAR_DEPTH) {
00340 unsigned char *cdata = new unsigned char[nx];
00341
00342 for (int y = (int)ny-1; y >= 0; y--) {
00343 for (int x = 0; x < (int)nx; x++) {
00344 if(data[y * nx + x] <= rendermin){
00345 cdata[x] = 0;
00346 }
00347 else if(data[y * nx + x] >= rendermax) {
00348 cdata[x] = UCHAR_MAX;
00349 }
00350 else {
00351 cdata[x] = (unsigned char)((data[y * nx + x] - rendermin) / (rendermax - rendermin) * 256);
00352 }
00353 }
00354 png_write_row(png_ptr, (png_byte *) cdata);
00355 }
00356
00357 if( cdata )
00358 {
00359 delete[]cdata;
00360 cdata = 0;
00361 }
00362 }
00363 else if (depth_type == PNG_SHORT_DEPTH) {
00364 unsigned short *sdata = new unsigned short[nx];
00365
00366 for (int y = (int)ny-1; y >= 0 ; y--) {
00367 for (int x = 0; x < (int)nx; x++) {
00368 if(data[y * nx + x] <= rendermin){
00369 sdata[x] = 0;
00370 }
00371 else if(data[y * nx + x] >= rendermax) {
00372 sdata[x] = USHRT_MAX;
00373 }
00374 else {
00375 sdata[x] = (unsigned short)((data[y * nx + x] - rendermin) / (rendermax - rendermin) * 65536);
00376 }
00377 }
00378
00379 png_write_row(png_ptr, (png_byte *) sdata);
00380 }
00381
00382 if( sdata )
00383 {
00384 delete[]sdata;
00385 sdata = 0;
00386 }
00387 }
00388
00389 png_write_end(png_ptr, info_ptr);
00390 png_destroy_write_struct(&png_ptr, &info_ptr);
00391
00392 EXITFUNC;
00393 return 0;
00394 }
00395
00396 void PngIO::flush()
00397 {
00398 png_write_flush(png_ptr);
00399 }
00400
00401 bool PngIO::is_complex_mode()
00402 {
00403 return false;
00404 }
00405
00406 bool PngIO::is_image_big_endian()
00407 {
00408 return true;
00409 }
00410
00411
00412 #endif //EM_PNG