| /* Convert 1 to 4-band 8 or 16-bit VIPS images to/from PNG. |
| * |
| * 28/11/03 JC |
| * - better no-overshoot on tile loop |
| * 22/2/05 |
| * - read non-interlaced PNG with a line buffer (thanks Michel Brabants) |
| * 11/1/06 |
| * - read RGBA palette-ized images more robustly (thanks Tom) |
| * 20/4/06 |
| * - auto convert to sRGB/mono (with optional alpha) for save |
| * 1/5/06 |
| * - from vips_png.c |
| * 8/5/06 |
| * - set RGB16/GREY16 if appropriate |
| * 28/2/09 |
| * - small cleanups |
| * 4/2/10 |
| * - gtkdoc |
| */ |
| |
| /* |
| |
| This file is part of VIPS. |
| |
| VIPS is free software; you can redistribute it and/or modify |
| it under the terms of the GNU Lesser 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 Lesser General Public License for more details. |
| |
| You should have received a copy of the GNU Lesser 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 |
| |
| */ |
| |
| /* |
| |
| These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk |
| |
| */ |
| |
| /* |
| #define DEBUG |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif /*HAVE_CONFIG_H*/ |
| #include <vips/intl.h> |
| |
| #ifndef HAVE_PNG |
| |
| #include <vips/vips.h> |
| |
| int |
| im_png2vips( const char *name, IMAGE *out ) |
| { |
| im_error( "im_png2vips", "%s", |
| _( "PNG support disabled" ) ); |
| return( -1 ); |
| } |
| |
| #else /*HAVE_PNG*/ |
| |
| #include <stdio.h> |
| #include <assert.h> |
| #include <stdlib.h> |
| |
| #include <vips/vips.h> |
| #include <vips/internal.h> |
| |
| #include <png.h> |
| |
| #ifdef WITH_DMALLOC |
| #include <dmalloc.h> |
| #endif /*WITH_DMALLOC*/ |
| |
| #if PNG_LIBPNG_VER < 10003 |
| #error "PNG library too old." |
| #endif |
| |
| static void |
| user_error_function( png_structp png_ptr, png_const_charp error_msg ) |
| { |
| im_error( "im_png2vips", _( "PNG error: \"%s\"" ), error_msg ); |
| } |
| |
| static void |
| user_warning_function( png_structp png_ptr, png_const_charp warning_msg ) |
| { |
| im_error( "im_png2vips", _( "PNG warning: \"%s\"" ), warning_msg ); |
| } |
| |
| /* What we track during a PNG read. |
| */ |
| typedef struct { |
| char *name; |
| IMAGE *out; |
| |
| FILE *fp; |
| png_structp pPng; |
| png_infop pInfo; |
| png_bytep *row_pointer; |
| png_bytep data; |
| } Read; |
| |
| static void |
| read_destroy( Read *read ) |
| { |
| IM_FREE( read->name ); |
| IM_FREEF( fclose, read->fp ); |
| if( read->pPng ) |
| png_destroy_read_struct( &read->pPng, &read->pInfo, NULL ); |
| IM_FREE( read->row_pointer ); |
| IM_FREE( read->data ); |
| |
| im_free( read ); |
| } |
| |
| static Read * |
| read_new( const char *name, IMAGE *out ) |
| { |
| Read *read; |
| |
| if( !(read = IM_NEW( NULL, Read )) ) |
| return( NULL ); |
| |
| read->name = im_strdup( NULL, name ); |
| read->out = out; |
| read->fp = NULL; |
| read->pPng = NULL; |
| read->pInfo = NULL; |
| read->row_pointer = NULL; |
| read->data = NULL; |
| |
| if( !(read->fp = im__file_open_read( name, NULL )) ) { |
| read_destroy( read ); |
| return( NULL ); |
| } |
| |
| if( !(read->pPng = png_create_read_struct( |
| PNG_LIBPNG_VER_STRING, NULL, |
| user_error_function, user_warning_function )) ) { |
| read_destroy( read ); |
| return( NULL ); |
| } |
| |
| /* Catch PNG errors from png_create_info_struct(). |
| */ |
| if( setjmp( read->pPng->jmpbuf ) ) { |
| read_destroy( read ); |
| return( NULL ); |
| } |
| |
| if( !(read->pInfo = png_create_info_struct( read->pPng )) ) { |
| read_destroy( read ); |
| return( NULL ); |
| } |
| |
| return( read ); |
| } |
| |
| /* Yuk! Have to malloc enough space for the whole image. Interlaced PNG |
| * is not really suitable for large objects ... |
| */ |
| static int |
| png2vips_interlace( Read *read ) |
| { |
| const int rowbytes = IM_IMAGE_SIZEOF_LINE( read->out ); |
| int y; |
| |
| if( !(read->row_pointer = IM_ARRAY( NULL, |
| read->pInfo->height, png_bytep )) ) |
| return( -1 ); |
| if( !(read->data = (png_bytep) im_malloc( NULL, |
| read->pInfo->height * rowbytes )) ) |
| return( -1 ); |
| |
| for( y = 0; y < (int) read->pInfo->height; y++ ) |
| read->row_pointer[y] = read->data + y * rowbytes; |
| if( im_outcheck( read->out ) || |
| im_setupout( read->out ) || |
| setjmp( read->pPng->jmpbuf ) ) |
| return( -1 ); |
| |
| png_read_image( read->pPng, read->row_pointer ); |
| |
| for( y = 0; y < (int) read->pInfo->height; y++ ) |
| if( im_writeline( y, read->out, read->row_pointer[y] ) ) |
| return( -1 ); |
| |
| return( 0 ); |
| } |
| |
| /* Noninterlaced images can be read without needing enough RAM for the whole |
| * image. |
| */ |
| static int |
| png2vips_noninterlace( Read *read ) |
| { |
| const int rowbytes = IM_IMAGE_SIZEOF_LINE( read->out ); |
| int y; |
| |
| if( !(read->data = (png_bytep) im_malloc( NULL, rowbytes )) ) |
| return( -1 ); |
| if( im_outcheck( read->out ) || |
| im_setupout( read->out ) || |
| setjmp( read->pPng->jmpbuf ) ) |
| return( -1 ); |
| |
| for( y = 0; y < (int) read->pInfo->height; y++ ) { |
| png_read_row( read->pPng, read->data, NULL ); |
| |
| if( im_writeline( y, read->out, read->data ) ) |
| return( -1 ); |
| } |
| |
| return( 0 ); |
| } |
| |
| /* Read a PNG file (header) into a VIPS (header). |
| */ |
| static int |
| png2vips( Read *read, int header_only ) |
| { |
| int bands, bpp, type; |
| |
| if( setjmp( read->pPng->jmpbuf ) ) |
| return( -1 ); |
| |
| png_init_io( read->pPng, read->fp ); |
| png_read_info( read->pPng, read->pInfo ); |
| |
| /* png_get_channels() gives us 1 band for palette images ... so look |
| * at colour_type for output bands. |
| */ |
| switch( read->pInfo->color_type ) { |
| case PNG_COLOR_TYPE_PALETTE: |
| bands = 3; |
| |
| /* Don't know if this is really correct. If there are |
| * transparent pixels, assume we're going to output RGBA. |
| */ |
| if( read->pInfo->num_trans ) |
| bands = 4; |
| |
| break; |
| |
| case PNG_COLOR_TYPE_GRAY: bands = 1; break; |
| case PNG_COLOR_TYPE_GRAY_ALPHA: bands = 2; break; |
| case PNG_COLOR_TYPE_RGB: bands = 3; break; |
| case PNG_COLOR_TYPE_RGB_ALPHA: bands = 4; break; |
| |
| default: |
| im_error( "im_png2vips", "%s", _( "unsupported color type" ) ); |
| return( -1 ); |
| } |
| |
| /* 8 or 16 bit. |
| */ |
| bpp = read->pInfo->bit_depth > 8 ? 2 : 1; |
| |
| if( bpp > 1 ) { |
| if( bands < 3 ) |
| type = IM_TYPE_GREY16; |
| else |
| type = IM_TYPE_RGB16; |
| } |
| else { |
| if( bands < 3 ) |
| type = IM_TYPE_B_W; |
| else |
| type = IM_TYPE_sRGB; |
| } |
| |
| /* Expand palette images. |
| */ |
| if( read->pInfo->color_type == PNG_COLOR_TYPE_PALETTE ) |
| png_set_expand( read->pPng ); |
| |
| /* Expand <8 bit images to full bytes. |
| */ |
| if( read->pInfo->bit_depth < 8 ) { |
| png_set_packing( read->pPng ); |
| png_set_shift( read->pPng, &(read->pInfo->sig_bit) ); |
| } |
| |
| /* If we're an INTEL byte order machine and this is 16bits, we need |
| * to swap bytes. |
| */ |
| if( read->pInfo->bit_depth > 8 && !im_amiMSBfirst() ) |
| png_set_swap( read->pPng ); |
| |
| /* Set VIPS header. |
| */ |
| im_initdesc( read->out, |
| read->pInfo->width, read->pInfo->height, bands, |
| bpp == 1 ? IM_BBITS_BYTE : IM_BBITS_SHORT, |
| bpp == 1 ? IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT, |
| IM_CODING_NONE, type, 1.0, 1.0, 0, 0 ); |
| |
| if( !header_only ) { |
| if( png_set_interlace_handling( read->pPng ) > 1 ) { |
| if( png2vips_interlace( read ) ) |
| return( -1 ); |
| } |
| else { |
| if( png2vips_noninterlace( read ) ) |
| return( -1 ); |
| } |
| } |
| |
| return( 0 ); |
| } |
| |
| /* Read a PNG file header into a VIPS header. |
| */ |
| static int |
| png2vips_header( const char *name, IMAGE *out ) |
| { |
| Read *read; |
| |
| if( !(read = read_new( name, out )) ) |
| return( -1 ); |
| |
| if( png2vips( read, 1 ) ) { |
| read_destroy( read ); |
| return( -1 ); |
| } |
| |
| read_destroy( read ); |
| |
| return( 0 ); |
| } |
| |
| /** |
| * im_png2vips: |
| * @filename: file to load |
| * @out: image to write to |
| * |
| * Read a PNG file into a VIPS image. It can read all png images, including 8- |
| * and 16-bit images, 1 and 3 channel, with and without an alpha channel. |
| * |
| * There is no support for embedded ICC profiles. |
| * |
| * See also: #VipsFormat, im_vips2png(). |
| * |
| * Returns: 0 on success, -1 on error. |
| */ |
| int |
| im_png2vips( const char *name, IMAGE *out ) |
| { |
| Read *read; |
| |
| #ifdef DEBUG |
| printf( "im_png2vips: reading \"%s\"\n", name ); |
| #endif /*DEBUG*/ |
| |
| if( !(read = read_new( name, out )) ) |
| return( -1 ); |
| |
| if( png2vips( read, 0 ) ) { |
| read_destroy( read ); |
| return( -1 ); |
| } |
| |
| read_destroy( read ); |
| |
| return( 0 ); |
| } |
| |
| static int |
| ispng( const char *filename ) |
| { |
| unsigned char buf[8]; |
| |
| return( im__get_bytes( filename, buf, 8 ) && |
| !png_sig_cmp( buf, 0, 8 ) ); |
| } |
| |
| static const char *png_suffs[] = { ".png", NULL }; |
| |
| /* png format adds no new members. |
| */ |
| typedef VipsFormat VipsFormatPng; |
| typedef VipsFormatClass VipsFormatPngClass; |
| |
| static void |
| vips_format_png_class_init( VipsFormatPngClass *class ) |
| { |
| VipsObjectClass *object_class = (VipsObjectClass *) class; |
| VipsFormatClass *format_class = (VipsFormatClass *) class; |
| |
| object_class->nickname = "png"; |
| object_class->description = _( "PNG" ); |
| |
| format_class->is_a = ispng; |
| format_class->header = png2vips_header; |
| format_class->load = im_png2vips; |
| format_class->save = im_vips2png; |
| format_class->suffs = png_suffs; |
| } |
| |
| static void |
| vips_format_png_init( VipsFormatPng *object ) |
| { |
| } |
| |
| G_DEFINE_TYPE( VipsFormatPng, vips_format_png, VIPS_TYPE_FORMAT ); |
| |
| #endif /*HAVE_PNG*/ |