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_ptr->jmpbuf)) {
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
00309
00310
00311 if( cdata )
00312 {
00313 delete[]cdata;
00314 cdata = 0;
00315 }
00316
00317 png_read_end(png_ptr, end_info);
00318 png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
00319 EXITFUNC;
00320 return 0;
00321 }
00322
00323 int PngIO::write_data(float *data, int image_index, const Region*,
00324 EMUtil::EMDataType, bool)
00325 {
00326 ENTERFUNC;
00327
00328
00329 image_index = 0;
00330 check_write_access(rw_mode, image_index, 1, data);
00331
00332
00333 if (!rendermin && !rendermax) EMUtil::getRenderMinMax(data, nx, ny, rendermin, rendermax);
00334
00335 if (depth_type == PNG_CHAR_DEPTH) {
00336 unsigned char *cdata = new unsigned char[nx];
00337
00338 for (unsigned int y = 0; y < ny; y++) {
00339 for (unsigned int x = 0; x < nx; x++) {
00340 if(data[y * nx + x] <= rendermin){
00341 cdata[x] = 0;
00342 }
00343 else if(data[y * nx + x] >= rendermax) {
00344 cdata[x] = UCHAR_MAX;
00345 }
00346 else {
00347 cdata[x] = (unsigned char)((data[y * nx + x] - rendermin) / (rendermax - rendermin) * 256);
00348 }
00349 }
00350 png_write_row(png_ptr, (png_byte *) cdata);
00351 }
00352
00353 if( cdata )
00354 {
00355 delete[]cdata;
00356 cdata = 0;
00357 }
00358 }
00359 else if (depth_type == PNG_SHORT_DEPTH) {
00360 unsigned short *sdata = new unsigned short[nx];
00361
00362 for (unsigned int y = 0; y < ny; y++) {
00363 for (unsigned int x = 0; x < nx; x++) {
00364 if(data[y * nx + x] <= rendermin){
00365 sdata[x] = 0;
00366 }
00367 else if(data[y * nx + x] >= rendermax) {
00368 sdata[x] = USHRT_MAX;
00369 }
00370 else {
00371 sdata[x] = (unsigned short)((data[y * nx + x] - rendermin) / (rendermax - rendermin) * 65536);
00372 }
00373 }
00374
00375 png_write_row(png_ptr, (png_byte *) sdata);
00376 }
00377
00378 if( sdata )
00379 {
00380 delete[]sdata;
00381 sdata = 0;
00382 }
00383 }
00384
00385 png_write_end(png_ptr, info_ptr);
00386 png_destroy_write_struct(&png_ptr, &info_ptr);
00387
00388 EXITFUNC;
00389 return 0;
00390 }
00391
00392 void PngIO::flush()
00393 {
00394 png_write_flush(png_ptr);
00395 }
00396
00397 bool PngIO::is_complex_mode()
00398 {
00399 return false;
00400 }
00401
00402 bool PngIO::is_image_big_endian()
00403 {
00404 return true;
00405 }
00406
00407
00408 #endif //EM_PNG