Blender  V2.59
png.c
Go to the documentation of this file.
00001 /*
00002  *
00003  * ***** BEGIN GPL LICENSE BLOCK *****
00004  *
00005  * This program is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License
00007  * as published by the Free Software Foundation; either version 2
00008  * of the License, or (at your option) any later version.
00009  *
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program; if not, write to the Free Software Foundation,
00017  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00018  *
00019  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
00020  * All rights reserved.
00021  *
00022  * The Original Code is: all of this file.
00023  *
00024  * Contributor(s): none yet.
00025  *
00026  * ***** END GPL LICENSE BLOCK *****
00027  * $Id: png.c 36777 2011-05-19 11:54:03Z blendix $
00028  */
00029 
00036 #include "png.h"
00037 
00038 #include "BLI_blenlib.h"
00039 #include "MEM_guardedalloc.h"
00040 
00041 #include "imbuf.h"
00042 
00043 #include "IMB_imbuf_types.h"
00044 #include "IMB_imbuf.h"
00045 
00046 #include "IMB_allocimbuf.h"
00047 #include "IMB_metadata.h"
00048 #include "IMB_filetype.h"
00049 
00050 typedef struct PNGReadStruct {
00051         unsigned char *data;
00052         unsigned int size;
00053         unsigned int seek;
00054 }PNGReadStruct;
00055 
00056 static void ReadData( png_structp png_ptr, png_bytep data, png_size_t length);
00057 static void WriteData( png_structp png_ptr, png_bytep data, png_size_t length);
00058 static void Flush( png_structp png_ptr);
00059 
00060 int imb_is_a_png(unsigned char *mem)
00061 {
00062         int ret_val = 0;
00063 
00064         if (mem) ret_val = !png_sig_cmp(mem, 0, 8);
00065         return(ret_val);
00066 }
00067 
00068 static void Flush(png_structp png_ptr) 
00069 {
00070         (void)png_ptr;
00071 }
00072 
00073 static void WriteData( png_structp png_ptr, png_bytep data, png_size_t length)
00074 {
00075         ImBuf *ibuf = (ImBuf *) png_get_io_ptr(png_ptr);
00076 
00077         // if buffer is to small increase it.
00078         while (ibuf->encodedsize + length > ibuf->encodedbuffersize) {
00079                 imb_enlargeencodedbufferImBuf(ibuf);
00080         }
00081 
00082         memcpy(ibuf->encodedbuffer + ibuf->encodedsize, data, length);
00083         ibuf->encodedsize += length;
00084 }
00085 
00086 static void ReadData( png_structp png_ptr, png_bytep data, png_size_t length)
00087 {
00088         PNGReadStruct *rs= (PNGReadStruct *) png_get_io_ptr(png_ptr);
00089 
00090         if (rs) {
00091                 if (length <= rs->size - rs->seek) {
00092                         memcpy(data, rs->data + rs->seek, length);
00093                         rs->seek += length;
00094                         return;
00095                 }
00096         }
00097 
00098         printf("Reached EOF while decoding PNG\n");
00099         longjmp(png_jmpbuf(png_ptr), 1);
00100 }
00101 
00102 int imb_savepng(struct ImBuf *ibuf, const char *name, int flags)
00103 {
00104         png_structp png_ptr;
00105         png_infop info_ptr;
00106 
00107         unsigned char *pixels = NULL;
00108         unsigned char *from, *to;
00109         png_bytepp row_pointers = NULL;
00110         int i, bytesperpixel, color_type = PNG_COLOR_TYPE_GRAY;
00111         FILE *fp = NULL;
00112 
00113         /* use the jpeg quality setting for compression */
00114         int compression;
00115         compression= (int)(((float)(ibuf->ftype & 0xff) / 11.1111f));
00116         compression= compression < 0 ? 0 : (compression > 9 ? 9 : compression);
00117 
00118         /* for prints */
00119         if(flags & IB_mem)
00120                 name= "<memory>";
00121 
00122         bytesperpixel = (ibuf->depth + 7) >> 3;
00123         if ((bytesperpixel > 4) || (bytesperpixel == 2)) {
00124                 printf("imb_savepng: Cunsupported bytes per pixel: %d for file: '%s'\n", bytesperpixel, name);
00125                 return (0);
00126         }
00127 
00128         png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
00129                 NULL, NULL, NULL);
00130         if (png_ptr == NULL) {
00131                 printf("imb_savepng: Cannot png_create_write_struct for file: '%s'\n", name);
00132                 return 0;
00133         }
00134 
00135         info_ptr = png_create_info_struct(png_ptr);
00136         if (info_ptr == NULL) {
00137                 png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
00138                 printf("imb_savepng: Cannot png_create_info_struct for file: '%s'\n", name);
00139                 return 0;
00140         }
00141 
00142         if (setjmp(png_jmpbuf(png_ptr))) {
00143                 png_destroy_write_struct(&png_ptr, &info_ptr);
00144                 printf("imb_savepng: Cannot setjmp for file: '%s'\n", name);
00145                 return 0;
00146         }
00147 
00148         // copy image data
00149 
00150         pixels = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(unsigned char), "pixels");
00151         if (pixels == NULL) {
00152                 png_destroy_write_struct(&png_ptr, &info_ptr);
00153                 printf("imb_savepng: Cannot allocate pixels array of %dx%d, %d bytes per pixel for file: '%s'\n", ibuf->x, ibuf->y, bytesperpixel, name);
00154                 return 0;
00155         }
00156 
00157         from = (unsigned char *) ibuf->rect;
00158         to = pixels;
00159 
00160         switch (bytesperpixel) {
00161         case 4:
00162                 color_type = PNG_COLOR_TYPE_RGBA;
00163                 for (i = ibuf->x * ibuf->y; i > 0; i--) {
00164                         to[0] = from[0];
00165                         to[1] = from[1];
00166                         to[2] = from[2];
00167                         to[3] = from[3];
00168                         to += 4; from += 4;
00169                 }
00170                 break;
00171         case 3:
00172                 color_type = PNG_COLOR_TYPE_RGB;
00173                 for (i = ibuf->x * ibuf->y; i > 0; i--) {
00174                         to[0] = from[0];
00175                         to[1] = from[1];
00176                         to[2] = from[2];
00177                         to += 3; from += 4;
00178                 }
00179                 break;
00180         case 1:
00181                 color_type = PNG_COLOR_TYPE_GRAY;
00182                 for (i = ibuf->x * ibuf->y; i > 0; i--) {
00183                         to[0] = from[0];
00184                         to++; from += 4;
00185                 }
00186                 break;
00187         }
00188 
00189         if (flags & IB_mem) {
00190                 // create image in memory
00191                 imb_addencodedbufferImBuf(ibuf);
00192                 ibuf->encodedsize = 0;
00193 
00194                 png_set_write_fn(png_ptr,
00195                          (png_voidp) ibuf,
00196                          WriteData,
00197                          Flush);
00198         } else {
00199                 fp = fopen(name, "wb");
00200                 if (!fp) {
00201                         png_destroy_write_struct(&png_ptr, &info_ptr);
00202                         MEM_freeN(pixels);
00203                         printf("imb_savepng: Cannot open file for writing: '%s'\n", name);
00204                         return 0;
00205                 }
00206                 png_init_io(png_ptr, fp);
00207         }
00208 
00209         /*
00210         png_set_filter(png_ptr, 0,
00211                 PNG_FILTER_NONE  | PNG_FILTER_VALUE_NONE |
00212                 PNG_FILTER_SUB   | PNG_FILTER_VALUE_SUB  |
00213                 PNG_FILTER_UP    | PNG_FILTER_VALUE_UP   |
00214                 PNG_FILTER_AVG   | PNG_FILTER_VALUE_AVG  |
00215                 PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH|
00216                 PNG_ALL_FILTERS);
00217         */
00218 
00219         png_set_compression_level(png_ptr, compression);
00220 
00221         // png image settings
00222         png_set_IHDR(png_ptr,
00223                  info_ptr,
00224                  ibuf->x,
00225                  ibuf->y,
00226                  8,
00227                  color_type,
00228                  PNG_INTERLACE_NONE,
00229                  PNG_COMPRESSION_TYPE_DEFAULT,
00230                  PNG_FILTER_TYPE_DEFAULT);
00231 
00232         /* image text info */
00233         if (ibuf->metadata) {
00234                 png_text*  metadata;
00235                 ImMetaData* iptr;
00236                 int  num_text = 0;
00237                 iptr = ibuf->metadata;
00238                 while (iptr) {
00239                         num_text++;
00240                         iptr = iptr->next;
00241                 }
00242                 
00243                 metadata = MEM_callocN(num_text*sizeof(png_text), "png_metadata");
00244                 iptr = ibuf->metadata;
00245                 num_text = 0;
00246                 while (iptr) {
00247                         
00248                         metadata[num_text].compression = PNG_TEXT_COMPRESSION_NONE;
00249                         metadata[num_text].key = iptr->key;
00250                         metadata[num_text].text = iptr->value;
00251                         num_text++;
00252                         iptr = iptr->next;
00253                 }
00254                 
00255                 png_set_text(png_ptr, info_ptr, metadata, num_text);
00256                 MEM_freeN(metadata);
00257 
00258         }
00259 
00260         if(ibuf->ppm[0] > 0.0 && ibuf->ppm[1] > 0.0) {
00261                 png_set_pHYs(png_ptr, info_ptr, (unsigned int)(ibuf->ppm[0] + 0.5), (unsigned int)(ibuf->ppm[1] + 0.5), PNG_RESOLUTION_METER);
00262         }
00263 
00264         // write the file header information
00265         png_write_info(png_ptr, info_ptr);
00266 
00267         // allocate memory for an array of row-pointers
00268         row_pointers = (png_bytepp) MEM_mallocN(ibuf->y * sizeof(png_bytep), "row_pointers");
00269         if (row_pointers == NULL) {
00270                 printf("imb_savepng: Cannot allocate row-pointers array for file '%s'\n", name);
00271                 png_destroy_write_struct(&png_ptr, &info_ptr);
00272                 MEM_freeN(pixels);
00273                 if (fp) {
00274                         fclose(fp);
00275                 }
00276                 return 0;
00277         }
00278 
00279         // set the individual row-pointers to point at the correct offsets
00280         for (i = 0; i < ibuf->y; i++) {
00281                 row_pointers[ibuf->y-1-i] = (png_bytep)
00282                         ((unsigned char *)pixels + (i * ibuf->x) * bytesperpixel * sizeof(unsigned char));
00283         }
00284 
00285         // write out the entire image data in one call
00286         png_write_image(png_ptr, row_pointers);
00287 
00288         // write the additional chunks to the PNG file (not really needed)
00289         png_write_end(png_ptr, info_ptr);
00290 
00291         // clean up
00292         MEM_freeN(pixels);
00293         MEM_freeN(row_pointers);
00294         png_destroy_write_struct(&png_ptr, &info_ptr);
00295 
00296         if (fp) {
00297                 fflush(fp);
00298                 fclose(fp);
00299         }
00300 
00301         return(1);
00302 }
00303 
00304 struct ImBuf *imb_loadpng(unsigned char *mem, size_t size, int flags)
00305 {
00306         struct ImBuf *ibuf = NULL;
00307         png_structp png_ptr;
00308         png_infop info_ptr;
00309         unsigned char *pixels = NULL;
00310         png_bytepp row_pointers = NULL;
00311         png_uint_32 width, height;
00312         int bit_depth, color_type;
00313         PNGReadStruct ps;
00314 
00315         unsigned char *from, *to;
00316         int i, bytesperpixel;
00317 
00318         if (imb_is_a_png(mem) == 0) return(NULL);
00319 
00320         png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
00321                 NULL, NULL, NULL);
00322         if (png_ptr == NULL) {
00323                 printf("Cannot png_create_read_struct\n");
00324                 return NULL;
00325         }
00326 
00327         info_ptr = png_create_info_struct(png_ptr);
00328         if (info_ptr == NULL) {
00329                 png_destroy_read_struct(&png_ptr, (png_infopp)NULL, 
00330                         (png_infopp)NULL);
00331                 printf("Cannot png_create_info_struct\n");
00332                 return NULL;
00333         }
00334 
00335         ps.size = size; /* XXX, 4gig limit! */
00336         ps.data = mem;
00337         ps.seek = 0;
00338 
00339         png_set_read_fn(png_ptr, (void *) &ps, ReadData);
00340 
00341         if (setjmp(png_jmpbuf(png_ptr))) {
00342                 png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00343                 if (pixels) MEM_freeN(pixels);
00344                 if (row_pointers) MEM_freeN(row_pointers);
00345                 if (ibuf) IMB_freeImBuf(ibuf);
00346                 return NULL;
00347         }
00348 
00349         // png_set_sig_bytes(png_ptr, 8);
00350 
00351         png_read_info(png_ptr, info_ptr);
00352         png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, 
00353                 &color_type, NULL, NULL, NULL);
00354 
00355         if (bit_depth == 16) {
00356                 png_set_strip_16(png_ptr);
00357                 bit_depth = 8;
00358         }
00359 
00360         bytesperpixel = png_get_channels(png_ptr, info_ptr);
00361 
00362         switch(color_type) {
00363         case PNG_COLOR_TYPE_RGB:
00364         case PNG_COLOR_TYPE_RGB_ALPHA:
00365                 break;
00366         case PNG_COLOR_TYPE_PALETTE:
00367                 png_set_palette_to_rgb(png_ptr);
00368                 if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) {
00369                         bytesperpixel = 4;
00370                 } else {
00371                         bytesperpixel = 3;
00372                 }
00373                 break;
00374         case PNG_COLOR_TYPE_GRAY:
00375         case PNG_COLOR_TYPE_GRAY_ALPHA:
00376                 if (bit_depth < 8) {
00377                         png_set_expand(png_ptr);
00378                         bit_depth = 8;
00379                 }
00380                 break;
00381         default:
00382                 printf("PNG format not supported\n");
00383                 longjmp(png_jmpbuf(png_ptr), 1);
00384         }
00385         
00386         ibuf = IMB_allocImBuf(width, height, 8 * bytesperpixel, 0);
00387 
00388         if (ibuf) {
00389                 ibuf->ftype = PNG;
00390                 ibuf->profile = IB_PROFILE_SRGB;
00391 
00392                 if (png_get_valid (png_ptr, info_ptr, PNG_INFO_pHYs)) {
00393                         int unit_type;
00394                         png_uint_32 xres, yres;
00395 
00396                         if(png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type))
00397                         if(unit_type == PNG_RESOLUTION_METER) {
00398                                 ibuf->ppm[0]= xres;
00399                                 ibuf->ppm[1]= yres;
00400                         }
00401                 }
00402         }
00403         else {
00404                 printf("Couldn't allocate memory for PNG image\n");
00405         }
00406 
00407         if (ibuf && ((flags & IB_test) == 0)) {
00408                 imb_addrectImBuf(ibuf);
00409 
00410                 pixels = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(unsigned char), "pixels");
00411                 if (pixels == NULL) {
00412                         printf("Cannot allocate pixels array\n");
00413                         longjmp(png_jmpbuf(png_ptr), 1);
00414                 }
00415 
00416                 // allocate memory for an array of row-pointers
00417                 row_pointers = (png_bytepp) MEM_mallocN(ibuf->y * sizeof(png_bytep), "row_pointers");
00418                 if (row_pointers == NULL) {
00419                         printf("Cannot allocate row-pointers array\n");
00420                         longjmp(png_jmpbuf(png_ptr), 1);
00421                 }
00422 
00423                 // set the individual row-pointers to point at the correct offsets
00424                 for (i = 0; i < ibuf->y; i++) {
00425                         row_pointers[ibuf->y-1-i] = (png_bytep)
00426                         ((unsigned char *)pixels + (i * ibuf->x) * bytesperpixel * sizeof(unsigned char));
00427                 }
00428 
00429                 png_read_image(png_ptr, row_pointers);
00430 
00431                 // copy image data
00432 
00433                 to = (unsigned char *) ibuf->rect;
00434                 from = pixels;
00435 
00436                 switch (bytesperpixel) {
00437                 case 4:
00438                         for (i = ibuf->x * ibuf->y; i > 0; i--) {
00439                                 to[0] = from[0];
00440                                 to[1] = from[1];
00441                                 to[2] = from[2];
00442                                 to[3] = from[3];
00443                                 to += 4; from += 4;
00444                         }
00445                         break;
00446                 case 3:
00447                         for (i = ibuf->x * ibuf->y; i > 0; i--) {
00448                                 to[0] = from[0];
00449                                 to[1] = from[1];
00450                                 to[2] = from[2];
00451                                 to[3] = 0xff;
00452                                 to += 4; from += 3;
00453                         }
00454                         break;
00455                 case 2:
00456                         for (i = ibuf->x * ibuf->y; i > 0; i--) {
00457                                 to[0] = to[1] = to[2] = from[0];
00458                                 to[3] = from[1];
00459                                 to += 4; from += 2;
00460                         }
00461                         break;
00462                 case 1:
00463                         for (i = ibuf->x * ibuf->y; i > 0; i--) {
00464                                 to[0] = to[1] = to[2] = from[0];
00465                                 to[3] = 0xff;
00466                                 to += 4; from++;
00467                         }
00468                         break;
00469                 }
00470 
00471                 if (flags & IB_metadata) {
00472                         png_text* text_chunks;
00473                         int count = png_get_text(png_ptr, info_ptr, &text_chunks, NULL);
00474                         for(i = 0; i < count; i++) {
00475                                 IMB_metadata_add_field(ibuf, text_chunks[i].key, text_chunks[i].text);
00476                                 ibuf->flags |= IB_metadata;                             
00477                          }
00478                 }
00479 
00480                 png_read_end(png_ptr, info_ptr);
00481         }
00482 
00483         // clean up
00484         MEM_freeN(pixels);
00485         MEM_freeN(row_pointers);
00486         png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
00487 
00488         return(ibuf);
00489 }
00490