/* This file is part of icoformat, a Windows Icon (ICO) File Format plugin for Adobe Photoshop Copyright (C) 2002-2010 Toby Thain, toby@telegraphics.com.au 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 of the License, 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 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "icoformat.h" static png_structp png_ptr; static png_infop info_ptr; OSErr writePNGheader(FormatRecordPtr pb){ int color_type = 0, i, n; png_colorp palette; if( !(png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL)) ) return ioErr; if( !(info_ptr = png_create_info_struct(png_ptr)) || setjmp(png_jmpbuf(png_ptr)) ){ /* If we get here, libpng had a problem writing */ png_destroy_write_struct(&png_ptr, &info_ptr); return ioErr; } png_set_write_fn(png_ptr, (png_voidp)&pb->dataFork, my_write_data, my_flush_data); switch(pb->imageMode) { case plugInModeBitmap: png_set_invert_mono(png_ptr); case plugInModeGrayScale: case plugInModeGray16: if(pb->planes == 1) color_type = PNG_COLOR_TYPE_GRAY; else if(pb->planes == 2) color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case plugInModeIndexedColor: // write_start() already ensured we have 1 channel data. // we can't enforce this in the PiPL, because standard ICO uses // a 2nd channel to encode mask (by convention). We could do // some fancy processing of the mask channel to set transparent index // in the image channel (possible TODO) color_type = PNG_COLOR_TYPE_PALETTE; n = pb->lutCount ? pb->lutCount : 0x100; palette = malloc(sizeof(png_color)*n); for(i = 0; i < n; ++i){ palette[i].red = pb->redLUT[i]; palette[i].green = pb->greenLUT[i]; palette[i].blue = pb->blueLUT[i]; } png_set_PLTE(png_ptr, info_ptr, palette, n); free(palette); if(pb->transparentIndex >= 0 && pb->transparentIndex <= 0xff){ png_byte idx = pb->transparentIndex; png_set_tRNS(png_ptr, info_ptr, &idx, 1, NULL); // this isn't working; FIXME } break; case plugInModeRGBColor: case plugInModeRGB48: if(pb->planes == 3) color_type = PNG_COLOR_TYPE_RGB; else if(pb->planes == 4) color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; default: return formatBadParameters; } png_set_pHYs(png_ptr, info_ptr, (png_uint_32)(0.5+(pb->imageHRes * (39.37/65536.))), (png_uint_32)(0.5+(pb->imageVRes * (39.37/65536.))), PNG_RESOLUTION_METER); png_set_IHDR(png_ptr, info_ptr, pb->imageSize.h, pb->imageSize.v, pb->depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); png_write_info(png_ptr, info_ptr); if(pb->depth > 8 && platform_is_LittleEndian()) png_set_swap(png_ptr); png_set_compression_level(png_ptr, Z_BEST_COMPRESSION); return noErr; } OSErr writePNG(FormatRecordPtr pb){ int j; png_bytep p; FILEPOS pos; OSErr e = noErr; if(setjmp(png_jmpbuf(png_ptr))){ /* If we get here, we had a problem writing */ png_destroy_write_struct(&png_ptr, &info_ptr); return ioErr; } for(j = 0, p = indata; j < pb->imageSize.v; ++j, p += inrb) png_write_row(png_ptr, p); png_write_end(png_ptr, NULL /*info_ptr*/); png_destroy_write_struct(&png_ptr, &info_ptr); // now do fixup in ICONDIRENTRY, write correct dwBytesInRes field if( !(e = GetFPos((FILEREF)pb->dataFork, &pos)) && !(e = SetFPos((FILEREF)pb->dataFork, fsFromStart, FIRST_ICONDIRENTRY + 8)) ) e = write4L((FILEREF)pb->dataFork, pos - (SIZEOF_ICONDIR + SIZEOF_ICONDIRENTRY)); return e; } void my_write_data(png_structp png_ptr, png_bytep data, png_size_t length){ long count = length; if(FSWrite(*(int32*)png_get_io_ptr(png_ptr), &count, data)) png_error(png_ptr, "write error"); } void my_flush_data(png_structp png_ptr){ // nothing needed; we're not buffering }