/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * filename: m-png.c * * * * UTIL C-source: Medical Image Conversion Utility * * * * purpose : read and write PNG files * * * * project : (X)MedCon by Erik Nolf * * * * Functions : MdcPngErr() - PNG Error message routine * * MdcPngWarn() - PNG Warn message routine * * MdcCheckPNG() - Check for PNG format * * MdcReadPNG() - Read PNG format * * MdcWritePNG() - Write PNG format * * * * Notes : code fragments from 'example.c' included with PNG lib * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* $Id: m-png.c,v 1.35 2007/05/21 20:16:14 enlf Exp $ */ /* Copyright (C) 1997-2007 by Erik Nolf This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Place - Suite 330, Boston, MA 02111-1307, USA. */ /**************************************************************************** H E A D E R S ****************************************************************************/ #include "m-depend.h" #include #ifdef HAVE_STDLIB_H #include #endif #include "medcon.h" /**************************************************************************** D E F I N E S ****************************************************************************/ /**************************************************************************** F U N C T I O N S ****************************************************************************/ static void MdcPngErr(png_structp png_ptr, png_const_charp error_msg) { MdcPrntWarn("PNG %s\n",error_msg); if (!png_ptr) return; longjmp(png_ptr->jmpbuf, 1); } static void MdcPngWarn(png_structp png_ptr, png_const_charp warning_msg) { if (!png_ptr) return; MdcPrntWarn("PNG %s\n",warning_msg); } int MdcCheckPNG(FILEINFO *fi) { unsigned char buf[MDC_PNG_BYTES_TO_CHECK]; /* read in some of the signature bytes */ if (fread(buf, 1, MDC_PNG_BYTES_TO_CHECK, fi->ifp) != MDC_PNG_BYTES_TO_CHECK) return(MDC_BAD_READ); /* compare the first MDC_PNG_BYTES_TO_CHECK bytes of the signature */ /* png_sig_cmp() returns zero if image is a PNG and nonzero if it isn't */ if (png_sig_cmp(buf,(png_size_t)0,MDC_PNG_BYTES_TO_CHECK)) return(MDC_FRMT_NONE); return(MDC_FRMT_PNG); } char *MdcReadPNG(FILEINFO *fi) { png_structp png_ptr; png_infop info_ptr; png_uint_32 width, height, rowbytes; png_colorp palette; png_bytepp row_pointers; Uint32 i, commentsize; int bit_depth, color_type, transform, num_palette; Uint8 *imgRGB, *pbuf; IMG_DATA *id; if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Reading PNG:"); if (MDC_VERBOSE) MdcPrntMesg("PNG Reading <%s> ...",fi->ifname); /* put some defaults we use */ fi->endian = MDC_FILE_ENDIAN=MDC_BIG_ENDIAN; /* always for a PNG */ fi->dim[0] = 4; fi->dim[4]=1; /* Create and initialize the png_struct with the desired error handler */ /* functions. If you want to use the default stderr and longjump method, */ /* you can supply NULL for the last three parameters. We also supply the */ /* the compiler header file version, so that we know if the application */ /* was compiled with a compatible version of the library. REQUIRED */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING , NULL, MdcPngErr, MdcPngWarn); if (png_ptr == NULL) return("PNG Couldn't create read struct"); /* allocate/initialize the memory for image information. REQUIRED. */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return("PNG Couldn't create read info struct"); } /* Set error handling if you are using the setjmp/longjmp method (this is */ /* the normal method of doing things with libpng). REQUIRED unless you */ /* set up your own error handlers in the png_create_read_struct() earlier.*/ if (setjmp(png_jmpbuf(png_ptr))) { /* free all of the memory associated with the png_ptr and info_ptr */ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); /* if we get here, we had a problem reading the file */ return("PNG Unexpected file reading error"); } /* I/O initialization with standard C streams */ png_init_io(png_ptr, fi->ifp); /* only allow 8bit or 24bit images */ transform = PNG_TRANSFORM_PACKING | PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_STRIP_ALPHA; if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_SET,0.3,NULL); /* read image, the hilevel way */ png_read_png(png_ptr, info_ptr , transform, NULL); if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_SET,0.6,NULL); /* get image information */ width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) { png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); } /* get comment */ if(info_ptr->num_text > 0) { commentsize = 1; for(i = 0; i < info_ptr->num_text; i++) commentsize += strlen(info_ptr->text[i].key) + 1 + info_ptr->text[i].text_length + 2; if ((fi->comment = malloc(commentsize)) == NULL) { MdcPngWarn(png_ptr,"PNG Can't malloc comment string"); }else{ fi->comment[0] = '\0'; for (i = 0; i < info_ptr->num_text; i++) { strcat(fi->comment, info_ptr->text[i].key); strcat(fi->comment, "::"); strcat(fi->comment, info_ptr->text[i].text); strcat(fi->comment, "\n"); } } } if (MDC_INFO) { MdcPrintLine('-',MDC_HALF_LENGTH); MdcPrntScrn("Short PNG Information (ver %s)\n",png_get_libpng_ver(png_ptr)); MdcPrintLine('-',MDC_HALF_LENGTH); MdcPrntScrn("image width : %u\n",width); MdcPrntScrn("image height : %u\n",height); MdcPrntScrn("bit depth : %u\n",bit_depth); MdcPrntScrn("color type : %u\n",color_type); MdcPrintLine('-',MDC_HALF_LENGTH); MdcPrntScrn("comment block :\n\n%s\n",fi->comment); MdcPrintLine('-',MDC_HALF_LENGTH); } /* preset FILEINFO info */ fi->mwidth = width; fi->mheight = height; fi->bits = 8; fi->type = BIT8_U; if (!MdcGetStructID(fi,1)) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return("PNG Bad malloc IMG_DATA struct"); } id = (IMG_DATA *)&fi->image[0]; id->width = fi->mwidth; id->height= fi->mheight; id->bits = fi->bits; id->type = fi->type; id->buf = MdcGetImgBuffer(width * height); if (id->buf == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return("PNG Bad malloc image buffer"); } /* get images: png_destroy will free this one later */ row_pointers = png_get_rows(png_ptr, info_ptr); if (row_pointers == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return("PNG Unexpected error retrieving row_pointers"); } rowbytes = png_get_rowbytes(png_ptr, info_ptr); switch(color_type) { case PNG_COLOR_TYPE_PALETTE: /* copy image rows */ if (rowbytes != width) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return("PNG Unexpected number of bytes per row"); } for (i=0; ibuf + (i*width); memcpy(pbuf,row_pointers[i],width); } /* copy color palette */ for (i=0; i < num_palette; i++) { fi->palette[i * 3 + 0] = (Uint8) palette[i].red; fi->palette[i * 3 + 1] = (Uint8) palette[i].green; fi->palette[i * 3 + 2] = (Uint8) palette[i].blue; } fi->map = MDC_MAP_PRESENT; break; case PNG_COLOR_TYPE_GRAY: /* copy image rows */ if (rowbytes != width) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return("PNG Unexpeted number of bytes per row"); } for (i=0; ibuf + (i*rowbytes); memcpy(pbuf,row_pointers[i],rowbytes); } fi->map = MDC_MAP_GRAY; break; case PNG_COLOR_TYPE_GRAY_ALPHA: png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return("PNG Color type GRAY + ALPHA unsupported"); break; case PNG_COLOR_TYPE_RGB: /* get contiguous RGB memory block */ imgRGB = malloc(height * rowbytes); if (imgRGB == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return("PNG Couldn't allocate RGB buffer"); } for (i=0; imap = MDC_MAP_PRESENT; fi->type = COLRGB; fi->bits = 24; id->type = COLRGB; id->bits = 24; id->buf = imgRGB; break; case PNG_COLOR_TYPE_RGB_ALPHA: png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return("PNG Color type RGB + ALPHA unsupported"); break; default: return("PNG Unsupported color type"); } /* finishing up */ png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_SET,1.0,NULL); return(NULL); } char *MdcWritePNG(FILEINFO *fi) { char suffix[11], *pext; png_structp png_ptr; png_infop info_ptr; png_colorp palette; png_bytepp row_pointers; png_text text_ptr[3]; IMG_DATA *id; Uint32 n, i, width, height, length, row_bytes; Uint8 *pbuf, FREE = MDC_NO; int bit_depth, color_type, interlace, compression, filter; MDC_FILE_ENDIAN = MDC_BIG_ENDIAN; /* always for a PNG */ if ((MDC_FILE_STDOUT == MDC_YES) && (fi->number > 1)) return("PNG Output to stdout not appropriate for multiple images"); if (XMDC_GUI == MDC_NO) { MdcDefaultName(fi,MDC_FRMT_PNG,fi->ofname,fi->ifname); } if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_BEGIN,0.,"Writing PNG:"); if (MDC_VERBOSE) MdcPrntMesg("PNG Writing <%s> ...",fi->ofname); /* desktop output - no use of 16 bit feature */ if (MDC_FORCE_INT != MDC_NO) { if (MDC_FORCE_INT != BIT8_U) { MdcPrntWarn("PNG Only Uint8 pixels supported"); } } /* check supported things */ if (MDC_QUANTIFY || MDC_CALIBRATE) { MdcPrntWarn("PNG Normalization loses quantified values!"); } if (MDC_PROGRESS) MdcProgress(MDC_PROGRESS_SET,0.0,NULL); length = strlen(fi->ofname); pext = strrchr(fi->ofname,'.'); if (pext == NULL) pext = &fi->ofname[length]; /* split up in separate files */ /* PNG is a single image format */ for (n=0; n < fi->number; n++) { /* add slice number to filename */ if (fi->number > 1) { sprintf(suffix,"-%.5u.%.3s",n+1,FrmtExt[MDC_FRMT_PNG]); strcpy(pext,suffix); } if ((MDC_FILE_STDOUT == MDC_YES) && (fi->number == 1)) { fi->ofp = stdout; }else{ if (MdcKeepFile(fi->ofname)) return("PNG File exists!!"); if ( (fi->ofp=fopen(fi->ofname,"wb")) == NULL ) return ("PNG Couldn't open file"); } /* set some defaults */ id = &fi->image[n]; width = id->width; height= id->height; bit_depth = 8; if (fi->type == COLRGB) { /* true color */ color_type = PNG_COLOR_TYPE_RGB; row_bytes = width * 3; }else{ /* indexed */ if (fi->map == MDC_MAP_GRAY) { /* gray */ color_type = PNG_COLOR_TYPE_GRAY; row_bytes = width; }else{ /* color */ color_type = PNG_COLOR_TYPE_PALETTE; row_bytes = width; } } compression = PNG_COMPRESSION_TYPE_BASE; interlace = PNG_INTERLACE_NONE; filter = PNG_FILTER_TYPE_BASE; /* Create and initialize the png_struct with the desired error handler */ /* functions. If you want to use the default stderr and longjump method, */ /* you can supply NULL for the last three parameters. We also check that */ /* the library version is compatible with the one used at compile time, */ /* in case we are using dynamically linked libraries. REQUIRED. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING , NULL, MdcPngErr, MdcPngWarn); if (png_ptr == NULL) return("PNG Couldn't create write struct"); /* allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return ("PNG Couldn't create write info struct"); } /* Set error handling. REQUIRED if you aren't supplying your own */ /* error handling functions in the png_create_write_struct() call. */ if (setjmp(png_jmpbuf(png_ptr))) { /* if we get here, we had a problem writing the file */ png_destroy_write_struct(&png_ptr, &info_ptr); return ("PNG Unexpected fire write error"); } /* set up the output control using standard C streams */ png_init_io(png_ptr, fi->ofp); /* can't write hilevel way, so here goes the hard way */ /* Set the image information here. Width and height are up to 2^31, */ /* bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on */ /* the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, */ /* PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, */ /* or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or */ /* PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST */ /* currently be PNG_COMPRESSION_TYPE_BASE & PNG_FILTER_TYPE_BASE. REQUIRED */ png_set_IHDR(png_ptr, info_ptr, width, height, bit_depth, color_type , interlace, compression, filter); /* set the palette if there is one. REQUIRED for indexed-color images */ palette = (png_colorp)png_malloc(png_ptr, 256 * sizeof (png_color)); if (color_type == PNG_COLOR_TYPE_PALETTE) { for (i=0; i<256; i++) { palette[i].red = fi->palette[i*3 + 0]; palette[i].green = fi->palette[i*3 + 1]; palette[i].blue = fi->palette[i*3 + 2]; } png_set_PLTE(png_ptr, info_ptr, palette, 256); } /* You must not free palette here, because png_set_PLTE only makes a link */ /* to the palette that you malloced. Wait until you are about to destroy */ /* the png structure. */ /* optional significant bit chunk */ /* if we are dealing with a grayscale image then */ /* sig_bit.gray = true_bit_depth; */ /* otherwise, if we are dealing with a color image then */ /* sig_bit.red = true_bit_depth; */ /* sig_bit.green = true_bit_depth; */ /* sig_bit.blue = true_bit_depth; */ /* if the image has an alpha channel then */ /* sig_bit.alpha = true_bit_depth; */ /* png_set_sBIT(png_ptr, info_ptr, sig_bit); */ /* Optional gamma chunk is strongly suggested if you have any guess */ /* as to the correct gamma of the image. */ /* png_set_gAMA(png_ptr, info_ptr, gamma); */ /* Optionally write comments into the image */ mdcbufr[0] = '\0'; if ( fi->acquisition_type != MDC_ACQUISITION_UNKNOWN ) { if ( !MdcMakeScanInfoStr(fi)) mdcbufr[0]='\0'; } text_ptr[0].key = "Program"; text_ptr[0].text = XMEDCON_PRGR; text_ptr[0].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[1].key = "Version"; text_ptr[1].text = XMEDCON_VERSION; text_ptr[1].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[2].key = "Information"; text_ptr[2].text = mdcbufr; text_ptr[2].compression = PNG_TEXT_COMPRESSION_zTXt; #ifdef PNG_iTXt_SUPPORTED text_ptr[0].lang = NULL; text_ptr[1].lang = NULL; text_ptr[2].lang = NULL; #endif png_set_text(png_ptr, info_ptr, text_ptr, 3); /* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */ /* note that if sRGB is present the gAMA and cHRM chunks must be ignored */ /* on read and must be written in accordance with the sRGB profile */ /* write the file header information. REQUIRED */ png_write_info(png_ptr, info_ptr); /* get 8bits image */ if ((id->type != COLRGB) && (id->type != BIT8_U)) { if ((pbuf = MdcGetImgBIT8_U(fi, n)) == NULL) { png_free(png_ptr, palette); png_destroy_write_struct(&png_ptr, &info_ptr); return("PNG Bad malloc new image buffer"); } FREE = MDC_YES; }else{ pbuf = id->buf; FREE = MDC_NO; } /* allocate pointers to rows */ row_pointers = (png_bytepp)malloc(sizeof(png_bytep) * height); if (row_pointers == NULL) { if (FREE == MDC_YES) MdcFree(pbuf); png_free(png_ptr, palette); png_destroy_write_struct(&png_ptr, &info_ptr); return("PNG Couldn't alloc row_pointers table"); } for (i=0; inumber,NULL); MdcCloseFile(fi->ofp); } return(NULL); }