corona  1.0.2
SavePNG.cpp
Go to the documentation of this file.
00001 #include <memory>
00002 #include <png.h>
00003 #include "Debug.h"
00004 #include "Save.h"
00005 #include "Types.h"
00006 
00007 namespace corona {
00008 
00009   void PNG_write(png_structp png_ptr, png_bytep data, png_size_t length) {
00010     File* file = (File*)png_get_io_ptr(png_ptr);
00011     if (file->write(data, length) != int(length)) {
00012       png_error(png_ptr, "Write error");
00013     }
00014   }
00015 
00016   void PNG_flush(png_structp png_ptr) {
00017     // assume that files always flush
00018   }
00019 
00020   bool SavePNG(File* file, Image* image) {
00021     COR_GUARD("SavePNG");
00022 
00023     if (!image) {
00024       return false;
00025     }
00026 
00027     // If the image format isn't supported directly by this function,
00028     // clone to a supported format and try to save with that.
00029     switch (image->getFormat()) {
00030       case PF_R8G8B8A8:
00031       case PF_R8G8B8:
00032       case PF_I8:
00033         break;
00034       default: {
00035         COR_LOG("Unsupported pixel format... cloning");
00036         std::auto_ptr<Image> cloned(CloneImage(image, PF_R8G8B8A8));
00037         return SavePNG(file, cloned.get());
00038       }
00039     }
00040 
00041     // create write struct
00042     png_structp png_ptr = png_create_write_struct(
00043       PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00044     if (!png_ptr) {
00045       return false;
00046     }
00047 
00048     // error handling!
00049     if (setjmp(png_jmpbuf(png_ptr))) {
00050       png_destroy_write_struct(&png_ptr, NULL);
00051       return false;
00052     }
00053 
00054     // create info struct
00055     png_infop info_ptr = png_create_info_struct(png_ptr);
00056     if (!info_ptr) {
00057       png_destroy_write_struct(&png_ptr, NULL);
00058       return false;
00059     }
00060 
00061     int width  = image->getWidth();
00062     int height = image->getHeight();
00063 
00064     // set image characteristics
00065     png_set_write_fn(png_ptr, file, PNG_write, PNG_flush);
00066 
00067     int color_format = 0; // png output format
00068     int color_format_bpp = 0; // png bytes per pixel
00069     bool color_format_paletted = false; // png palette needed flag
00070 
00071     // figure out output format
00072     switch (image->getFormat()) {
00073       case PF_R8G8B8A8:
00074         color_format = PNG_COLOR_TYPE_RGB_ALPHA;
00075         color_format_bpp = 4;
00076         break;
00077       case PF_R8G8B8:
00078         color_format = PNG_COLOR_TYPE_RGB;
00079         color_format_bpp = 3;
00080         break;
00081       case PF_I8:
00082         color_format = PNG_COLOR_TYPE_PALETTE;
00083         color_format_bpp = 1;
00084         color_format_paletted = true;
00085         break;
00086       default:
00087         // Unsupported format.  This should already be taken care of
00088         // by the test at the beginning of this function.
00089         png_destroy_write_struct(&png_ptr, &info_ptr);
00090         return false;
00091     }
00092 
00093     png_set_IHDR(
00094       png_ptr, info_ptr,
00095       width, height,
00096       8,
00097       color_format,
00098       PNG_INTERLACE_NONE,
00099       PNG_COMPRESSION_TYPE_DEFAULT,
00100       PNG_FILTER_TYPE_DEFAULT);
00101 
00102     png_color* png_palette = 0;
00103     if (color_format_paletted) {
00104       COR_LOG("Saving palettized image...");
00105 
00106       int image_palette_format = image->getPaletteFormat(); // palette format
00107       int image_palette_size = image->getPaletteSize(); // palette size
00108 
00109       // allocate png palette and get pointer to image palette
00110       png_palette = (png_color*)png_malloc(
00111         png_ptr, sizeof(png_color) * image_palette_size);
00112       byte* image_palette = (byte*)image->getPalette();
00113 
00114 
00115       if (image_palette_format == PF_R8G8B8) {
00116         // 24 bit source palette
00117         for (int i = 0; i < image_palette_size; i++) {
00118           // copy entry directly
00119           png_palette[i].red   = *image_palette++;
00120           png_palette[i].green = *image_palette++;
00121           png_palette[i].blue  = *image_palette++;
00122         }
00123       } else if (image_palette_format == PF_R8G8B8A8) {
00124         // 32 bit source palette
00125         for (int i = 0; i < image_palette_size; i++) {
00126           // copy entry, skip alpha
00127           png_palette[i].red   = *image_palette++;
00128           png_palette[i].green = *image_palette++;
00129           png_palette[i].blue  = *image_palette++;
00130           image_palette++;
00131         }
00132       }
00133       // write palette
00134       png_set_PLTE(png_ptr, info_ptr, png_palette, image_palette_size);
00135     }
00136 
00137     byte* pixels = (byte*)image->getPixels();
00138 
00139     // build rows
00140     void** rows = (void**)png_malloc(png_ptr, sizeof(void*) * height);
00141     for (int i = 0; i < height; ++i) {
00142       rows[i] = png_malloc(png_ptr, color_format_bpp * width);
00143       memcpy(rows[i], pixels, color_format_bpp * width);
00144       pixels += width * color_format_bpp;      
00145     }
00146     png_set_rows(png_ptr, info_ptr, (png_bytepp)rows);
00147 
00148                 //zero 28-sep-2012 - it was never necessary, at least as far back as png 1.2.1, to set this
00149                 //png_set_rows sets it.
00150                 //good thing, because info_ptr is private >= png 1.5
00151     //info_ptr->valid |= PNG_INFO_IDAT;
00152 
00153     // actually write the image
00154     png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
00155 
00156     // clean up memory
00157     for (int i = 0; i < height; ++i) {
00158       png_free(png_ptr, rows[i]);
00159     }
00160     png_free(png_ptr, rows);
00161 
00162     if (png_palette) {
00163       png_free(png_ptr, png_palette);
00164     }
00165 
00166     png_destroy_write_struct(&png_ptr, &info_ptr);
00167 
00168     return true;
00169   }
00170 
00171 }