/* 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" OSErr getPNGinfo(int32 f, ICONDIRENTRY *pide, ICONIMAGE *pii, char *desc){ OSErr e = noErr; png_structp png_ptr; png_infop info_ptr = NULL,end_info = NULL; png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_method; if( !(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, my_error_fn, my_warning_fn)) ) return formatCannotRead; if( !(info_ptr = png_create_info_struct(png_ptr)) || !(end_info = png_create_info_struct(png_ptr)) || setjmp(png_jmpbuf(png_ptr)) ) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return formatCannotRead; } png_set_read_fn(png_ptr, (png_voidp)&f, my_read_data); // reposition file at start of PNG data if( !(e = SetFPos(f,fsFromStart,pide->dwImageOffset)) ) { // get information we need and fake out the fields in the header struct png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); //DWORD biSize; pii->icHeader.biWidth = width; pii->icHeader.biHeight = height*2; // just like ICO, which counts height of both image and mask pii->icHeader.biPlanes = png_get_channels(png_ptr, info_ptr); pii->icHeader.biBitCount = bit_depth; //DWORD biCompression; //DWORD biSizeImage; //LONG biXPelsPerMeter; //LONG biYPelsPerMeter; //DWORD biClrUsed; //DWORD biClrImportant; switch(color_type){ case PNG_COLOR_TYPE_GRAY: strcpy(desc,"PNG (Grey)"); break; case PNG_COLOR_TYPE_GRAY_ALPHA: strcpy(desc,"PNG (Grey+Alpha)"); break; case PNG_COLOR_TYPE_PALETTE: strcpy(desc,"PNG (Palette)"); break; case PNG_COLOR_TYPE_RGB: strcpy(desc,"PNG (RGB)"); break; case PNG_COLOR_TYPE_RGB_ALPHA: strcpy(desc,"PNG (RGB+Alpha)"); break; default: strcpy(desc,"PNG (unknown type)"); break; } } png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return e; } OSErr readPNG(FormatRecordPtr pb, ICONDIRENTRY *pide, ICONIMAGE *pii){ OSErr e = noErr; png_uint_32 width, height, res_x, res_y; int bit_depth, color_type, interlace_type, compression_type, filter_method, unit_type, outrb, i, num_palette; png_colorp palette; png_bytep *row_pointers,p; png_structp png_ptr; png_infop info_ptr = NULL, end_info = NULL; if( !(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, my_error_fn, my_warning_fn)) ) return formatCannotRead; if( !(info_ptr = png_create_info_struct(png_ptr)) || !(end_info = png_create_info_struct(png_ptr)) || setjmp(png_jmpbuf(png_ptr)) ) { pngerr: png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return formatCannotRead; } png_set_read_fn(png_ptr, (png_voidp)&pb->dataFork, my_read_data); if( !(e = SetFPos((FILEREF)pb->dataFork, fsFromStart, pide->dwImageOffset)) ) { png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); switch(color_type){ case PNG_COLOR_TYPE_GRAY_ALPHA: #ifdef PIFmtCanWriteTransparency pb->transparencyPlane = 1; // PS 6.0 or later #endif case PNG_COLOR_TYPE_GRAY: if(bit_depth == 1){ // PNG files describe monochrome as black being zero and white being one. pb->imageMode = plugInModeBitmap; png_set_invert_mono(png_ptr); }else if(bit_depth < 8){ pb->imageMode = plugInModeGrayScale; png_set_packing(png_ptr); // expand to bytes bit_depth = 8; }else pb->imageMode = bit_depth == 8 ? plugInModeGrayScale : plugInModeGray16; break; case PNG_COLOR_TYPE_PALETTE: png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); if(bit_depth < 8){ png_set_packing(png_ptr); // expand to bytes bit_depth = 8; }else if(bit_depth > 8 || num_palette > 0x100) goto pngerr; for(i = 0; i < num_palette; ++i){ pb->redLUT[i] = palette[i].red; pb->greenLUT[i] = palette[i].green; pb->blueLUT[i] = palette[i].blue; } // pad out the rest of Photoshop's colour table with black for(; i < 0x100; ++i) pb->redLUT[i] = pb->greenLUT[i] = pb->blueLUT[i] = 0; #ifdef PS6 pb->lutCount = num_palette; // PS 5.5 or later #endif pb->imageMode = plugInModeIndexedColor; break; case PNG_COLOR_TYPE_RGB_ALPHA: #ifdef PIFmtCanWriteTransparency pb->transparencyPlane = 3; // PS 6.0 or later #endif case PNG_COLOR_TYPE_RGB: pb->imageMode = bit_depth == 8 ? plugInModeRGBColor : plugInModeRGB48; break; default: goto pngerr; } if(bit_depth > 8 && platform_is_LittleEndian()) png_set_swap(png_ptr); png_read_update_info(png_ptr, info_ptr); // recompute rowbytes based on transformations pb->depth = bit_depth; pb->planes = png_get_channels(png_ptr, info_ptr); pb->loPlane = 0; pb->hiPlane = pb->planes-1; pb->colBytes = pb->planes << (bit_depth==16); pb->rowBytes = outrb = png_get_rowbytes(png_ptr, info_ptr); pb->planeBytes = 1; // planes are interleaved pb->imageSize.h = width; pb->imageSize.v = height; SETRECT(pb->theRect, 0, 0, pb->imageSize.h, pb->imageSize.v); png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type); if(unit_type == PNG_RESOLUTION_METER){ pb->imageHRes = (Fixed)(0.5+(res_x / (39.37/65536.))); // adding 0.5 achieves rounding pb->imageVRes = (Fixed)(0.5+(res_y / (39.37/65536.))); }else pb->imageHRes = pb->imageVRes = DEFAULT_RES; e = PS_BUFFER_ALLOC(outrb*height, &outBufferID); if( e == noErr && (row_pointers = malloc(sizeof(Ptr)*height)) ) { pb->data = outdata = (unsigned char*)PS_BUFFER_LOCK(outBufferID, true); for(i = 0, p = outdata; i < height; ++i, p += outrb) row_pointers[i] = p; png_read_image(png_ptr, row_pointers); png_read_end(png_ptr, end_info); free(row_pointers); } } png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return e; } void my_read_data(png_structp png_ptr, png_bytep data, png_size_t length){ long count = length; if(FSRead(*(int32*)png_get_io_ptr(png_ptr), &count, data)) png_error(png_ptr, "read error"); } void my_error_fn(png_structp png_ptr, png_const_charp error_msg){ dbg(error_msg); } void my_warning_fn(png_structp png_ptr, png_const_charp warning_msg){ dbg(warning_msg); }