00001 #include <memory>
00002 #include <png.h>
00003 #include "Debug.h"
00004 #include "Save.h"
00005 #include "Types.h"
00006
00007
00008 namespace corona {
00009
00010 void PNG_write(png_structp png_ptr, png_bytep data, png_size_t length) {
00011 File* file = (File*)png_get_io_ptr(png_ptr);
00012 if (file->write(data, length) != int(length)) {
00013 png_error(png_ptr, "Write error");
00014 }
00015 }
00016
00017 void PNG_flush(png_structp png_ptr) {
00018
00019 }
00020
00021 bool SavePNG(File* file, Image* image) {
00022 COR_GUARD("SavePNG");
00023
00024 if (!image) {
00025 return false;
00026 }
00027
00028
00029
00030 switch (image->getFormat()) {
00031 case PF_R8G8B8A8:
00032 case PF_R8G8B8:
00033 case PF_I8:
00034 break;
00035 default: {
00036 COR_LOG("Unsupported pixel format... cloning");
00037 std::auto_ptr<Image> cloned(CloneImage(image, PF_R8G8B8A8));
00038 return SavePNG(file, cloned.get());
00039 }
00040 }
00041
00042
00043 png_structp png_ptr = png_create_write_struct(
00044 PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
00045 if (!png_ptr) {
00046 return false;
00047 }
00048
00049
00050 if (setjmp(png_jmpbuf(png_ptr))) {
00051 png_destroy_write_struct(&png_ptr, NULL);
00052 return false;
00053 }
00054
00055
00056 png_infop info_ptr = png_create_info_struct(png_ptr);
00057 if (!info_ptr) {
00058 png_destroy_write_struct(&png_ptr, NULL);
00059 return false;
00060 }
00061
00062 int width = image->getWidth();
00063 int height = image->getHeight();
00064
00065
00066 png_set_write_fn(png_ptr, file, PNG_write, PNG_flush);
00067
00068 int color_format = 0;
00069 int color_format_bpp = 0;
00070 bool color_format_paletted = false;
00071
00072
00073 switch (image->getFormat()) {
00074 case PF_R8G8B8A8:
00075 color_format = PNG_COLOR_TYPE_RGB_ALPHA;
00076 color_format_bpp = 4;
00077 break;
00078 case PF_R8G8B8:
00079 color_format = PNG_COLOR_TYPE_RGB;
00080 color_format_bpp = 3;
00081 break;
00082 case PF_I8:
00083 color_format = PNG_COLOR_TYPE_PALETTE;
00084 color_format_bpp = 1;
00085 color_format_paletted = true;
00086 break;
00087 default:
00088
00089
00090 png_destroy_write_struct(&png_ptr, &info_ptr);
00091 return false;
00092 }
00093
00094 png_set_IHDR(
00095 png_ptr, info_ptr,
00096 width, height,
00097 8,
00098 color_format,
00099 PNG_INTERLACE_NONE,
00100 PNG_COMPRESSION_TYPE_DEFAULT,
00101 PNG_FILTER_TYPE_DEFAULT);
00102
00103 png_color* png_palette = 0;
00104 if (color_format_paletted) {
00105 COR_LOG("Saving palettized image...");
00106
00107 int image_palette_format = image->getPaletteFormat();
00108 int image_palette_size = image->getPaletteSize();
00109
00110
00111 png_palette = (png_color*)png_malloc(
00112 png_ptr, sizeof(png_color) * image_palette_size);
00113 byte* image_palette = (byte*)image->getPalette();
00114
00115
00116 if (image_palette_format == PF_R8G8B8) {
00117
00118 for (int i = 0; i < image_palette_size; i++) {
00119
00120 png_palette[i].red = *image_palette++;
00121 png_palette[i].green = *image_palette++;
00122 png_palette[i].blue = *image_palette++;
00123 }
00124 } else if (image_palette_format == PF_R8G8B8A8) {
00125
00126 for (int i = 0; i < image_palette_size; i++) {
00127
00128 png_palette[i].red = *image_palette++;
00129 png_palette[i].green = *image_palette++;
00130 png_palette[i].blue = *image_palette++;
00131 image_palette++;
00132 }
00133 }
00134
00135 png_set_PLTE(png_ptr, info_ptr, png_palette, image_palette_size);
00136 }
00137
00138 byte* pixels = (byte*)image->getPixels();
00139
00140
00141 void** rows = (void**)png_malloc(png_ptr, sizeof(void*) * height);
00142 for (int i = 0; i < height; ++i) {
00143 rows[i] = png_malloc(png_ptr, color_format_bpp * width);
00144 memcpy(rows[i], pixels, color_format_bpp * width);
00145 pixels += width * color_format_bpp;
00146 }
00147 png_set_rows(png_ptr, info_ptr, (png_bytepp)rows);
00148 info_ptr->valid |= PNG_INFO_IDAT;
00149
00150
00151 png_write_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
00152
00153
00154 for (int i = 0; i < height; ++i) {
00155 png_free(png_ptr, rows[i]);
00156 }
00157 png_free(png_ptr, rows);
00158
00159 if (png_palette) {
00160 png_free(png_ptr, png_palette);
00161 }
00162
00163 png_destroy_write_struct(&png_ptr, &info_ptr);
00164
00165 return true;
00166 }
00167
00168 }