diff --git a/ChangeLog b/ChangeLog index 67e4c795..f1446d00 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,7 +15,7 @@ im_bandmean(), im_c2real(), im_c2imag(), im_ri2c(), im_jpeg*2vips(), im_vips2jpeg*(), im_tiff2vips(), im_vips2tiff(), im_exr2vips(), im_fits2vips(), im_vips2fits(), im_analyze2vips(), im_raw2vips(), - im_vips2raw() + im_vips2raw(), im_magick2vips(), im_png2vips(), im_png2*() redone as classes - added argument priorites to help control arg ordering - generate has a 'stop' param to signal successful early termination diff --git a/TODO b/TODO index f65f2522..aae2f040 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,19 @@ +- vips2bufpng() is leaky + +- interlace should be a bool? + + + +- radiance, matlab, ppm to go still + +- saverawfd should be renamed saveraw_fd, cf. pngsave_buffer + - remove uses of vips_image_pio_output(), this is done for you by generate now + do we need vips_image_wio_output()? can't vips_image_write_line() call this + as well? + - magickload is broken $ vips magickload healthygirl.jpg x.v @@ -10,6 +23,9 @@ seems to be a magick bug: ping jpg reports 1 band, load jpg reports 3 band + posted a question, wait for reply + + - "header fred.png" does not work, since header uses im_open() which uses diff --git a/configure.in b/configure.in index 2bedc94d..385d47c2 100644 --- a/configure.in +++ b/configure.in @@ -537,17 +537,6 @@ FIND_ZIP( [AC_MSG_WARN([libz not found; disabling ZIP support]) with_zip=no ]) -FIND_JPEG( - [with_jpeg=yes], - [AC_MSG_WARN([libjpeg not found; disabling JPEG support]) - with_jpeg=no - ]) - -if test x"$with_jpeg" = x"yes"; then - AM_CONDITIONAL(HAVE_JPEG, true) -else - AM_CONDITIONAL(HAVE_JPEG, false) -fi # look for PNG with pkg-config ... fall back to our tester AC_ARG_WITH([png], @@ -565,6 +554,24 @@ if test x"$with_png" != "xno"; then ]) fi +if test x"$with_png" = x"yes"; then + AM_CONDITIONAL(HAVE_PNG, true) +else + AM_CONDITIONAL(HAVE_PNG, false) +fi + +FIND_JPEG( + [with_jpeg=yes], + [AC_MSG_WARN([libjpeg not found; disabling JPEG support]) + with_jpeg=no + ]) + +if test x"$with_jpeg" = x"yes"; then + AM_CONDITIONAL(HAVE_JPEG, true) +else + AM_CONDITIONAL(HAVE_JPEG, false) +fi + # libexif AC_ARG_WITH([libexif], AS_HELP_STRING([--without-libexif], [build without libexif (default: test)])) diff --git a/libvips/foreign/Makefile.am b/libvips/foreign/Makefile.am index 61435dac..9eba49d2 100644 --- a/libvips/foreign/Makefile.am +++ b/libvips/foreign/Makefile.am @@ -28,6 +28,20 @@ EXTRA_DIST += \ magickload.c endif +if HAVE_PNG +libforeign_la_SOURCES += \ + pngload.c \ + pngsave.c \ + vipspng.h \ + vipspng.c +else +EXTRA_DIST += \ + pngload.c \ + pngsave.c \ + vipspng.h \ + vipspng.c +endif + if HAVE_OPENEXR libforeign_la_SOURCES += \ openexr2vips.h \ diff --git a/libvips/foreign/foreign.c b/libvips/foreign/foreign.c index 9b1f891e..71d9bd75 100644 --- a/libvips/foreign/foreign.c +++ b/libvips/foreign/foreign.c @@ -1194,6 +1194,9 @@ vips_foreign_write_options( VipsImage *in, const char *filename ) void vips_foreign_operation_init( void ) { + extern GType vips_foreign_load_png_get_type( void ); + extern GType vips_foreign_save_png_file_get_type( void ); + extern GType vips_foreign_save_png_buffer_get_type( void ); extern GType vips_foreign_load_csv_get_type( void ); extern GType vips_foreign_save_csv_get_type( void ); extern GType vips_foreign_load_fits_get_type( void ); @@ -1224,6 +1227,12 @@ vips_foreign_operation_init( void ) vips_foreign_load_vips_get_type(); vips_foreign_save_vips_get_type(); +#ifdef HAVE_PNG + vips_foreign_load_png_get_type(); + vips_foreign_save_png_file_get_type(); + vips_foreign_save_png_buffer_get_type(); +#endif /*HAVE_PNG*/ + #ifdef HAVE_JPEG vips_foreign_load_jpeg_file_get_type(); vips_foreign_load_jpeg_buffer_get_type(); @@ -1747,3 +1756,113 @@ vips_fitssave( VipsImage *in, const char *filename, ... ) return( result ); } + +/** + * vips_pngload: + * @filename: file to load + * @out: decompressed image + * @...: %NULL-terminated list of optional named arguments + * + * 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: vips_image_new_from_file(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_pngload( const char *filename, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "pngload", ap, filename, out ); + va_end( ap ); + + return( result ); +} + +/** + * vips_pngsave: + * @in: image to save + * @filename: file to write to + * @compression: compression level + * @interlace: interlace image + * @...: %NULL-terminated list of optional named arguments + * + * Write a VIPS image to a file as PNG. + * + * @compression means compress with this much effort (0 - 9). Default 6. + * + * Set @interlace to %TRUE to interlace the image with ADAM7 + * interlacing. Beware + * than an interlaced PNG can be up to 7 times slower to write than a + * non-interlaced image. + * + * There is no support for attaching ICC profiles to PNG images. + * + * The image is automatically converted to RGB, RGBA, Monochrome or Mono + + * alpha before saving. Images with more than one byte per band element are + * saved as 16-bit PNG, others are saved as 8-bit PNG. + * + * See also: vips_image_new_from_file(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_pngsave( VipsImage *in, const char *filename, ... ) +{ + va_list ap; + int result; + + va_start( ap, filename ); + result = vips_call_split( "pngsave", ap, in, filename ); + va_end( ap ); + + return( result ); +} + +/** + * vips_pngsave_buffer: + * @in: image to save + * @buf: return output buffer here + * @len: return output length here + * @compression: compression level + * @interlace: interlace image + * @...: %NULL-terminated list of optional named arguments + * + * As vips_pngsave(), but save to a memory buffer. + * + * The address of the buffer is returned in @obuf, the length of the buffer in + * @olen. You are responsible for freeing the buffer with g_free() when you + * are done with it. + * + * See also: vips_pngsave(), vips_image_write_to_file(). + * + * Returns: 0 on success, -1 on error. + */ +int +vips_pngsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) +{ + va_list ap; + VipsArea *area; + int result; + + va_start( ap, len ); + result = vips_call_split( "pngsave_buffer", ap, in, &area ); + va_end( ap ); + + if( buf ) { + *buf = area->data; + area->free_fn = NULL; + } + if( buf ) + *len = area->length; + + vips_area_unref( area ); + + return( result ); +} diff --git a/libvips/foreign/pngload.c b/libvips/foreign/pngload.c new file mode 100644 index 00000000..0e56d465 --- /dev/null +++ b/libvips/foreign/pngload.c @@ -0,0 +1,136 @@ +/* load png from a file + * + * 5/12/11 + * - from tiffload.c + */ + +/* + + 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 +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include +#include + +#include "vipspng.h" + +typedef struct _VipsForeignLoadPng { + VipsForeignLoad parent_object; + + /* Filename for load. + */ + char *filename; + +} VipsForeignLoadPng; + +typedef VipsForeignLoadClass VipsForeignLoadPngClass; + +G_DEFINE_TYPE( VipsForeignLoadPng, vips_foreign_load_png, + VIPS_TYPE_FOREIGN_LOAD ); + +static VipsForeignFlags +vips_foreign_load_png_get_flags_filename( const char *filename ) +{ + return( 0 ); +} + +static VipsForeignFlags +vips_foreign_load_png_get_flags( VipsForeignLoad *load ) +{ + VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; + + return( vips_foreign_load_png_get_flags_filename( png->filename ) ); +} + +static int +vips_foreign_load_png_header( VipsForeignLoad *load ) +{ + VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; + + if( vips__png_header( png->filename, load->out ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_foreign_load_png_load( VipsForeignLoad *load ) +{ + VipsForeignLoadPng *png = (VipsForeignLoadPng *) load; + + if( vips__png_read( png->filename, load->real ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_load_png_class_init( VipsForeignLoadPngClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsForeignLoadClass *load_class = (VipsForeignLoadClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "pngload"; + object_class->description = _( "load png from file" ); + + foreign_class->suffs = vips__png_suffs; + + load_class->is_a = vips__png_ispng; + load_class->get_flags_filename = + vips_foreign_load_png_get_flags_filename; + load_class->get_flags = vips_foreign_load_png_get_flags; + load_class->header = vips_foreign_load_png_header; + load_class->load = vips_foreign_load_png_load; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to load from" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignLoadPng, filename ), + NULL ); +} + +static void +vips_foreign_load_png_init( VipsForeignLoadPng *png ) +{ +} diff --git a/libvips/foreign/pngsave.c b/libvips/foreign/pngsave.c new file mode 100644 index 00000000..9451997e --- /dev/null +++ b/libvips/foreign/pngsave.c @@ -0,0 +1,236 @@ +/* save to png + * + * 2/12/11 + * - wrap a class around the png writer + */ + +/* + + 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_VERBOSE +#define DEBUG + */ + +#ifdef HAVE_CONFIG_H +#include +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include + +#include "vipspng.h" + +typedef struct _VipsForeignSavePng { + VipsForeignSave parent_object; + + int compression; + gboolean interlace; +} VipsForeignSavePng; + +typedef VipsForeignSaveClass VipsForeignSavePngClass; + +G_DEFINE_ABSTRACT_TYPE( VipsForeignSavePng, vips_foreign_save_png, + VIPS_TYPE_FOREIGN_SAVE ); + +/* Save a bit of typing. + */ +#define UC VIPS_FORMAT_UCHAR +#define C VIPS_FORMAT_CHAR +#define US VIPS_FORMAT_USHORT +#define S VIPS_FORMAT_SHORT +#define UI VIPS_FORMAT_UINT +#define I VIPS_FORMAT_INT +#define F VIPS_FORMAT_FLOAT +#define X VIPS_FORMAT_COMPLEX +#define D VIPS_FORMAT_DOUBLE +#define DX VIPS_FORMAT_DPCOMPLEX + +static int bandfmt_png[10] = { +/* UC C US S UI I F X D DX */ + UC, UC, US, US, US, US, UC, UC, UC, UC +}; + +static void +vips_foreign_save_png_class_init( VipsForeignSavePngClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsForeignClass *foreign_class = (VipsForeignClass *) class; + VipsForeignSaveClass *save_class = (VipsForeignSaveClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "pngsave_base"; + object_class->description = _( "save png" ); + + foreign_class->suffs = vips__png_suffs; + + save_class->saveable = VIPS_SAVEABLE_RGBA; + save_class->format_table = bandfmt_png; + + VIPS_ARG_INT( class, "compression", 6, + _( "Compression" ), + _( "Compression factor" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSavePng, compression ), + 1, 10, 6 ); + + VIPS_ARG_BOOL( class, "interlace", 7, + _( "Interlace" ), + _( "Interlace image" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsForeignSavePng, interlace ), + FALSE ); +} + +static void +vips_foreign_save_png_init( VipsForeignSavePng *png ) +{ + png->compression = 6; +} + +typedef struct _VipsForeignSavePngFile { + VipsForeignSavePng parent_object; + + char *filename; +} VipsForeignSavePngFile; + +typedef VipsForeignSavePngClass VipsForeignSavePngFileClass; + +G_DEFINE_TYPE( VipsForeignSavePngFile, vips_foreign_save_png_file, + vips_foreign_save_png_get_type() ); + +static int +vips_foreign_save_png_file_build( VipsObject *object ) +{ + VipsForeignSave *save = (VipsForeignSave *) object; + VipsForeignSavePng *png = (VipsForeignSavePng *) object; + VipsForeignSavePngFile *png_file = (VipsForeignSavePngFile *) object; + + if( VIPS_OBJECT_CLASS( vips_foreign_save_png_file_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips__png_write( save->ready, png_file->filename, + png->compression, png->interlace ) ) + return( -1 ); + + return( 0 ); +} + +static void +vips_foreign_save_png_file_class_init( VipsForeignSavePngFileClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "pngsave"; + object_class->description = _( "save image to png file" ); + object_class->build = vips_foreign_save_png_file_build; + + VIPS_ARG_STRING( class, "filename", 1, + _( "Filename" ), + _( "Filename to save to" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsForeignSavePngFile, filename ), + NULL ); +} + +static void +vips_foreign_save_png_file_init( VipsForeignSavePngFile *file ) +{ +} + +typedef struct _VipsForeignSavePngBuffer { + VipsForeignSavePng parent_object; + + VipsArea *buf; +} VipsForeignSavePngBuffer; + +typedef VipsForeignSavePngClass VipsForeignSavePngBufferClass; + +G_DEFINE_TYPE( VipsForeignSavePngBuffer, vips_foreign_save_png_buffer, + vips_foreign_save_png_get_type() ); + +static int +vips_foreign_save_png_buffer_build( VipsObject *object ) +{ + VipsForeignSave *save = (VipsForeignSave *) object; + VipsForeignSavePng *png = (VipsForeignSavePng *) object; + + void *obuf; + size_t olen; + VipsArea *area; + + if( VIPS_OBJECT_CLASS( vips_foreign_save_png_buffer_parent_class )-> + build( object ) ) + return( -1 ); + + if( vips__png_write_buf( save->ready, &obuf, &olen, + png->compression, png->interlace ) ) + return( -1 ); + + area = vips_area_new_blob( (VipsCallbackFn) vips_free, obuf, olen ); + + g_object_set( object, "buf", area, NULL ); + + return( 0 ); +} + +static void +vips_foreign_save_png_buffer_class_init( VipsForeignSavePngBufferClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *object_class = (VipsObjectClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "pngsave_buffer"; + object_class->description = _( "save image to png buffer" ); + object_class->build = vips_foreign_save_png_buffer_build; + + VIPS_ARG_BOXED( class, "buffer", 1, + _( "Buffer" ), + _( "Buffer to save to" ), + VIPS_ARGUMENT_REQUIRED_OUTPUT, + G_STRUCT_OFFSET( VipsForeignSavePngBuffer, buf ), + VIPS_TYPE_BLOB ); +} + +static void +vips_foreign_save_png_buffer_init( VipsForeignSavePngBuffer *buffer ) +{ +} diff --git a/libvips/foreign/vipspng.c b/libvips/foreign/vipspng.c new file mode 100644 index 00000000..291fe85c --- /dev/null +++ b/libvips/foreign/vipspng.c @@ -0,0 +1,685 @@ +/* Load/save png image with libpng + * + * 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 + * 2/11/07 + * - use im_wbuffer() API for BG writes + * 28/2/09 + * - small cleanups + * 4/2/10 + * - gtkdoc + * - fixed 16-bit save + * 12/5/10 + * - lololo but broke 8-bit save, fixed again + * 20/7/10 Tim Elliott + * - added im_vips2bufpng() + * 8/1/11 + * - get set png resolution (thanks Zhiyu Wu) + * 17/3/11 + * - update for libpng-1.5 API changes + * - better handling of palette and 1-bit images + * - ... but we are now png 1.2.9 and later only :-( argh + * 28/3/11 + * - argh gamma was wrong when viewed in firefox + * 19/12/11 + * - rework as a set of fns ready for wrapping as a class + */ + +/* + + 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 +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include + +#include +#include +#include + +#include + +#include "vipspng.h" + +#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 ) +{ + vips_error( "png2vips", _( "PNG error: \"%s\"" ), error_msg ); +} + +static void +user_warning_function( png_structp png_ptr, png_const_charp warning_msg ) +{ + vips_error( "png2vips", _( "PNG warning: \"%s\"" ), warning_msg ); +} + +/* What we track during a PNG read. + */ +typedef struct { + char *name; + VipsImage *out; + + FILE *fp; + png_structp pPng; + png_infop pInfo; + png_bytep *row_pointer; + png_bytep data; +} Read; + +static void +read_destroy( VipsImage *out, Read *read ) +{ + VIPS_FREEF( fclose, read->fp ); + if( read->pPng ) + png_destroy_read_struct( &read->pPng, &read->pInfo, NULL ); + VIPS_FREE( read->row_pointer ); + VIPS_FREE( read->data ); +} + +static Read * +read_new( const char *name, VipsImage *out ) +{ + Read *read; + + if( !(read = VIPS_NEW( out, Read )) ) + return( NULL ); + + read->name = vips_strdup( VIPS_OBJECT( out ), name ); + read->out = out; + read->fp = NULL; + read->pPng = NULL; + read->pInfo = NULL; + read->row_pointer = NULL; + read->data = NULL; + + g_signal_connect( out, "close", + G_CALLBACK( read_destroy ), read ); + + if( !(read->fp = vips__file_open_read( name, NULL, FALSE )) ) + return( NULL ); + + if( !(read->pPng = png_create_read_struct( + PNG_LIBPNG_VER_STRING, NULL, + user_error_function, user_warning_function )) ) + return( NULL ); + + /* Catch PNG errors from png_create_info_struct(). + */ + if( setjmp( png_jmpbuf( read->pPng ) ) ) + return( NULL ); + + if( !(read->pInfo = png_create_info_struct( read->pPng )) ) + 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 = VIPS_IMAGE_SIZEOF_LINE( read->out ); + const int height = png_get_image_height( read->pPng, read->pInfo ); + + int y; + + if( !(read->row_pointer = VIPS_ARRAY( NULL, height, png_bytep )) ) + return( -1 ); + if( !(read->data = (png_bytep) + vips_malloc( NULL, height * rowbytes )) ) + return( -1 ); + + for( y = 0; y < (int) height; y++ ) + read->row_pointer[y] = read->data + y * rowbytes; + if( vips_image_wio_output( read->out ) || + setjmp( png_jmpbuf( read->pPng ) ) ) + return( -1 ); + + png_read_image( read->pPng, read->row_pointer ); + + for( y = 0; y < height; y++ ) + if( vips_image_write_line( read->out, + y, 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 = VIPS_IMAGE_SIZEOF_LINE( read->out ); + const int height = png_get_image_height( read->pPng, read->pInfo ); + + int y; + + if( !(read->data = (png_bytep) vips_malloc( NULL, rowbytes )) ) + return( -1 ); + if( vips_image_wio_output( read->out ) || + setjmp( png_jmpbuf( read->pPng ) ) ) + return( -1 ); + + for( y = 0; y < height; y++ ) { + png_read_row( read->pPng, read->data, NULL ); + + if( vips_image_write_line( read->out, y, read->data ) ) + return( -1 ); + } + + return( 0 ); +} + +/* Read a PNG file (header) into a VIPS (header). + */ +static int +png2vips( Read *read, int header_only ) +{ + png_uint_32 width, height; + int bit_depth, color_type, interlace_type; + + png_uint_32 res_x, res_y; + int unit_type; + + int bands; + VipsInterpretation interpretation; + double Xres, Yres; + + if( setjmp( png_jmpbuf( read->pPng ) ) ) + return( -1 ); + + png_init_io( read->pPng, read->fp ); + png_read_info( read->pPng, read->pInfo ); + png_get_IHDR( read->pPng, read->pInfo, + &width, &height, &bit_depth, &color_type, + &interlace_type, NULL, NULL ); + + /* png_get_channels() gives us 1 band for palette images ... so look + * at colour_type for output bands. + */ + switch( color_type ) { + case PNG_COLOR_TYPE_PALETTE: + bands = 3; + + /* If there's transparency in the palette, we make an alpha. + */ + if( png_get_valid( read->pPng, read->pInfo, PNG_INFO_tRNS ) ) + 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: + vips_error( "png2vips", "%s", _( "unsupported color type" ) ); + return( -1 ); + } + + if( bit_depth > 8 ) { + if( bands < 3 ) + interpretation = VIPS_INTERPRETATION_GREY16; + else + interpretation = VIPS_INTERPRETATION_RGB16; + } + else { + if( bands < 3 ) + interpretation = VIPS_INTERPRETATION_B_W; + else + interpretation = VIPS_INTERPRETATION_sRGB; + } + + /* Expand palette images, expand transparency too. + */ + if( color_type == PNG_COLOR_TYPE_PALETTE ) + png_set_palette_to_rgb( read->pPng ); + if( png_get_valid( read->pPng, read->pInfo, PNG_INFO_tRNS ) ) + png_set_tRNS_to_alpha( read->pPng ); + + /* Expand <8 bit images to full bytes. + */ + if( color_type == PNG_COLOR_TYPE_GRAY && + bit_depth < 8 ) + png_set_expand_gray_1_2_4_to_8( read->pPng ); + + /* If we're an INTEL byte order machine and this is 16bits, we need + * to swap bytes. + */ + if( bit_depth > 8 && !vips_amiMSBfirst() ) + png_set_swap( read->pPng ); + + /* Get resolution. I'm not sure what we should do for UNKNOWN, since + * vips is always pixels/mm. + */ + unit_type = PNG_RESOLUTION_METER; + res_x = 1000; + res_y = 1000; + png_get_pHYs( read->pPng, read->pInfo, &res_x, &res_y, &unit_type ); + switch( unit_type ) { + case PNG_RESOLUTION_METER: + Xres = res_x / 1000.0; + Yres = res_y / 1000.0; + break; + + default: + Xres = res_x; + Yres = res_y; + break; + } + + /* Set VIPS header. + */ + vips_image_init_fields( read->out, + width, height, bands, + bit_depth > 8 ? + VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR, + VIPS_CODING_NONE, interpretation, + Xres, Yres ); + + 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. + */ +int +vips__png_header( const char *name, VipsImage *out ) +{ + Read *read; + + if( !(read = read_new( name, out )) || + png2vips( read, 1 ) ) + return( -1 ); + + return( 0 ); +} + +int +vips__png_read( const char *name, VipsImage *out ) +{ + Read *read; + +#ifdef DEBUG + printf( "png2vips: reading \"%s\"\n", name ); +#endif /*DEBUG*/ + + if( !(read = read_new( name, out )) || + png2vips( read, 0 ) ) + return( -1 ); + + return( 0 ); +} + +int +vips__png_ispng( const char *filename ) +{ + unsigned char buf[8]; + + return( vips__get_bytes( filename, buf, 8 ) && + !png_sig_cmp( buf, 0, 8 ) ); +} + +const char *vips__png_suffs[] = { ".png", NULL }; + +/* What we track during a PNG write. + */ +typedef struct { + VipsImage *in; + + FILE *fp; + png_structp pPng; + png_infop pInfo; + png_bytep *row_pointer; +} Write; + +static void +write_destroy( VipsImage *out, Write *write ) +{ + VIPS_FREEF( fclose, write->fp ); + if( write->pPng ) + png_destroy_write_struct( &write->pPng, &write->pInfo ); +} + +static Write * +write_new( VipsImage *in ) +{ + Write *write; + + if( !(write = VIPS_NEW( in, Write )) ) + return( NULL ); + memset( write, 0, sizeof( Write ) ); + write->in = in; + g_signal_connect( in, "close", + G_CALLBACK( write_destroy ), write ); + + if( !(write->row_pointer = VIPS_ARRAY( in, in->Ysize, png_bytep )) ) + return( NULL ); + if( !(write->pPng = png_create_write_struct( + PNG_LIBPNG_VER_STRING, NULL, + user_error_function, user_warning_function )) ) + return( NULL ); + + /* Catch PNG errors from png_create_info_struct(). + */ + if( setjmp( png_jmpbuf( write->pPng ) ) ) + return( NULL ); + + if( !(write->pInfo = png_create_info_struct( write->pPng )) ) + return( NULL ); + + return( write ); +} + +static int +write_png_block( VipsRegion *region, Rect *area, void *a ) +{ + Write *write = (Write *) a; + + int i; + + /* The area to write is always a set of complete scanlines. + */ + g_assert( area->left == 0 ); + g_assert( area->width == region->im->Xsize ); + g_assert( area->top + area->height <= region->im->Ysize ); + + /* Catch PNG errors. Yuk. + */ + if( setjmp( png_jmpbuf( write->pPng ) ) ) + return( -1 ); + + for( i = 0; i < area->height; i++ ) + write->row_pointer[i] = (png_bytep) + VIPS_REGION_ADDR( region, 0, area->top + i ); + + png_write_rows( write->pPng, write->row_pointer, area->height ); + + return( 0 ); +} + +/* Write a VIPS image to PNG. + */ +static int +write_vips( Write *write, int compress, int interlace ) +{ + VipsImage *in = write->in; + + int bit_depth; + int color_type; + int interlace_type; + int i, nb_passes; + + g_assert( in->BandFmt == VIPS_FORMAT_UCHAR || + in->BandFmt == VIPS_FORMAT_USHORT ); + g_assert( in->Coding == VIPS_CODING_NONE ); + g_assert( in->Bands > 0 && in->Bands < 5 ); + + /* Catch PNG errors. + */ + if( setjmp( png_jmpbuf( write->pPng ) ) ) + return( -1 ); + + /* Check input image. + */ + if( vips_image_pio_input( in ) ) + return( -1 ); + if( compress < 0 || compress > 9 ) { + vips_error( "vips2png", + "%s", _( "compress should be in [0,9]" ) ); + return( -1 ); + } + + /* Set compression parameters. + */ + png_set_compression_level( write->pPng, compress ); + + bit_depth = in->BandFmt == VIPS_FORMAT_UCHAR ? 8 : 16; + + switch( in->Bands ) { + case 1: color_type = PNG_COLOR_TYPE_GRAY; break; + case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; + case 3: color_type = PNG_COLOR_TYPE_RGB; break; + case 4: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; + + default: + g_assert( 0 ); + } + + interlace_type = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; + + png_set_IHDR( write->pPng, write->pInfo, + in->Xsize, in->Ysize, bit_depth, color_type, interlace_type, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); + + /* Set resolution. libpnbg uses pixels per meter. + */ + png_set_pHYs( write->pPng, write->pInfo, + VIPS_RINT( in->Xres * 1000 ), VIPS_RINT( in->Yres * 1000 ), + PNG_RESOLUTION_METER ); + + png_write_info( write->pPng, write->pInfo ); + + /* If we're an intel byte order CPU and this is a 16bit image, we need + * to swap bytes. + */ + if( bit_depth > 8 && !vips_amiMSBfirst() ) + png_set_swap( write->pPng ); + + if( interlace ) + nb_passes = png_set_interlace_handling( write->pPng ); + else + nb_passes = 1; + + /* Write data. + */ + for( i = 0; i < nb_passes; i++ ) + if( vips_sink_disc( write->in, write_png_block, write ) ) + return( -1 ); + + /* The setjmp() was held by our background writer: reset it. + */ + if( setjmp( png_jmpbuf( write->pPng ) ) ) + return( -1 ); + + png_write_end( write->pPng, write->pInfo ); + + return( 0 ); +} + +int +vips__png_write( VipsImage *in, const char *filename, + int compress, int interlace ) +{ + Write *write; + + if( !(write = write_new( in )) ) + return( -1 ); + + /* Make output. + */ + if( !(write->fp = vips__file_open_write( filename, FALSE )) ) + return( -1 ); + png_init_io( write->pPng, write->fp ); + + /* Convert it! + */ + if( write_vips( write, compress, interlace ) ) { + vips_error( "vips2png", + _( "unable to write \"%s\"" ), filename ); + + return( -1 ); + } + + return( 0 ); +} + +typedef struct _WriteBuf { + char *buf; + size_t len; + size_t alloc; +} WriteBuf; + +static void +write_buf_free( WriteBuf *wbuf ) +{ + VIPS_FREE( wbuf ); +} + +static WriteBuf * +write_buf_new( void ) +{ + WriteBuf *wbuf; + + if( !(wbuf = VIPS_NEW( NULL, WriteBuf )) ) + return( NULL ); + + wbuf->buf = NULL; + wbuf->len = 0; + wbuf->alloc = 0; + + return( wbuf ); +} + +static void +write_buf_grow( WriteBuf *wbuf, size_t grow_len ) +{ + size_t new_len = wbuf->len + grow_len; + + if( new_len > wbuf->alloc ) { + size_t proposed_alloc = (16 + wbuf->alloc) * 3 / 2; + + wbuf->alloc = VIPS_MAX( proposed_alloc, new_len ); + + /* There's no vips_realloc(), so we call g_realloc() directly. + * This is safe, since vips_malloc() / vips_free() are wrappers + * over g_malloc() / g_free(). + * + * FIXME: add vips_realloc(). + */ + wbuf->buf = g_realloc( wbuf->buf, wbuf->alloc ); + + VIPS_DEBUG_MSG( "write_buf_grow: grown to %zd bytes\n", + wbuf->alloc ); + } +} + +static void +user_write_data( png_structp png_ptr, png_bytep data, png_size_t length ) +{ + WriteBuf *wbuf = (WriteBuf *) png_get_io_ptr( png_ptr ); + + char *write_start; + + write_buf_grow( wbuf, length ); + + write_start = wbuf->buf + wbuf->len; + memcpy( write_start, data, length ); + + wbuf->len += length; + + g_assert( wbuf->len <= wbuf->alloc ); +} + +int +vips__png_write_buf( VipsImage *in, + void **obuf, size_t *olen, int compression, int interlace ) +{ + WriteBuf *wbuf; + Write *write; + + if( !(wbuf = write_buf_new()) || + !(write = write_new( in )) ) + return( -1 ); + + png_set_write_fn( write->pPng, wbuf, user_write_data, NULL ); + + /* Convert it! + */ + if( write_vips( write, compression, interlace ) ) { + g_free( wbuf->buf ); + write_buf_free( wbuf ); + vips_error( "vips_vips2bufpng", + "%s", _( "unable to write to buffer" ) ); + + return( -1 ); + } + + *obuf = wbuf->buf; + *olen = wbuf->len; + + write_buf_free( wbuf ); + + return( 0 ); +} + diff --git a/libvips/foreign/vipspng.h b/libvips/foreign/vipspng.h new file mode 100644 index 00000000..1c6d687e --- /dev/null +++ b/libvips/foreign/vipspng.h @@ -0,0 +1,51 @@ +/* common defs for tiff read/write + */ + +/* + + Copyright (C) 1991-2005 The National Gallery + + 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 + + */ + +/* + + These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk + + */ + +#ifndef VIPS_PNG_H +#define VIPS_PNG_H + +#ifdef __cplusplus +extern "C" { +#endif /*__cplusplus*/ + +int vips__png_header( const char *name, VipsImage *out ); +int vips__png_read( const char *name, VipsImage *out ); +int vips__png_ispng( const char *filename ); +extern const char *vips__png_suffs[]; + +int vips__png_write( VipsImage *in, const char *filename, + int compress, int interlace ); +int vips__png_write_buf( VipsImage *in, + void **obuf, size_t *olen, int compression, int interlace ); + +#ifdef __cplusplus +} +#endif /*__cplusplus*/ + +#endif /*VIPS_PNG_H*/ diff --git a/libvips/format/im_jpeg2vips.c b/libvips/format/im_jpeg2vips.c index fd628726..122fb9b1 100644 --- a/libvips/format/im_jpeg2vips.c +++ b/libvips/format/im_jpeg2vips.c @@ -1,44 +1,5 @@ /* Convert 1 or 3-band 8-bit VIPS images to/from JPEG. * - * 28/11/03 JC - * - better no-overshoot on tile loop - * 12/11/04 - * - better demand size choice for eval - * 30/6/05 JC - * - update im_error()/im_warn() - * - now loads and saves exif data - * 30/7/05 - * - now loads ICC profiles - * - now saves ICC profiles from the VIPS header - * 24/8/05 - * - jpeg load sets vips xres/yres from exif, if possible - * - jpeg save sets exif xres/yres from vips, if possible - * 29/8/05 - * - cut from old vips_jpeg.c - * 13/10/06 - * - add -#ifndef HAVE_PNG - -#include - -int -im_png2vips( const char *name, IMAGE *out ) -{ - im_error( "im_png2vips", "%s", _( "PNG support disabled" ) ); - return( -1 ); -} - -#else /*HAVE_PNG*/ - #include -#include -#include #include #include -#include - -#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, FALSE )) ) { - 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( png_jmpbuf( read->pPng ) ) ) { - 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 ); - const int height = png_get_image_height( read->pPng, read->pInfo ); - - int y; - - if( !(read->row_pointer = IM_ARRAY( NULL, height, png_bytep )) ) - return( -1 ); - if( !(read->data = (png_bytep) im_malloc( NULL, height * rowbytes )) ) - return( -1 ); - - for( y = 0; y < (int) height; y++ ) - read->row_pointer[y] = read->data + y * rowbytes; - if( im_outcheck( read->out ) || - im_setupout( read->out ) || - setjmp( png_jmpbuf( read->pPng ) ) ) - return( -1 ); - - png_read_image( read->pPng, read->row_pointer ); - - for( y = 0; y < 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 ); - const int height = png_get_image_height( read->pPng, read->pInfo ); - - int y; - - if( !(read->data = (png_bytep) im_malloc( NULL, rowbytes )) ) - return( -1 ); - if( im_outcheck( read->out ) || - im_setupout( read->out ) || - setjmp( png_jmpbuf( read->pPng ) ) ) - return( -1 ); - - for( y = 0; y < 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 ) -{ - png_uint_32 width, height; - int bit_depth, color_type, interlace_type; - - png_uint_32 res_x, res_y; - int unit_type; - - int bands, bpp, type; - double Xres, Yres; - - if( setjmp( png_jmpbuf( read->pPng ) ) ) - return( -1 ); - - png_init_io( read->pPng, read->fp ); - png_read_info( read->pPng, read->pInfo ); - png_get_IHDR( read->pPng, read->pInfo, - &width, &height, &bit_depth, &color_type, - &interlace_type, NULL, NULL ); - - /* png_get_channels() gives us 1 band for palette images ... so look - * at colour_type for output bands. - */ - switch( color_type ) { - case PNG_COLOR_TYPE_PALETTE: - bands = 3; - - /* If there's transparency in the palette, we make an alpha. - */ - if( png_get_valid( read->pPng, read->pInfo, PNG_INFO_tRNS ) ) - 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 = 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, expand transparency too. - */ - if( color_type == PNG_COLOR_TYPE_PALETTE ) - png_set_palette_to_rgb( read->pPng ); - if( png_get_valid( read->pPng, read->pInfo, PNG_INFO_tRNS ) ) - png_set_tRNS_to_alpha( read->pPng ); - - /* Expand <8 bit images to full bytes. - */ - if( color_type == PNG_COLOR_TYPE_GRAY && - bit_depth < 8 ) - png_set_expand_gray_1_2_4_to_8( read->pPng ); - - /* If we're an INTEL byte order machine and this is 16bits, we need - * to swap bytes. - */ - if( bit_depth > 8 && !im_amiMSBfirst() ) - png_set_swap( read->pPng ); - - /* Get resolution. I'm not sure what we should do for UNKNOWN, since - * vips is always pixels/mm. - */ - unit_type = PNG_RESOLUTION_METER; - res_x = 1000; - res_y = 1000; - png_get_pHYs( read->pPng, read->pInfo, &res_x, &res_y, &unit_type ); - switch( unit_type ) { - case PNG_RESOLUTION_METER: - Xres = res_x / 1000.0; - Yres = res_y / 1000.0; - break; - - default: - Xres = res_x; - Yres = res_y; - break; - } - - /* Set VIPS header. - */ - im_initdesc( read->out, width, height, bands, - bpp == 1 ? IM_BBITS_BYTE : IM_BBITS_SHORT, - bpp == 1 ? IM_BANDFMT_UCHAR : IM_BANDFMT_USHORT, - IM_CODING_NONE, type, - Xres, Yres, - 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 ) +im_png2vips( const char *filename, IMAGE *out ) { - Read *read; + VipsImage *x; -#ifdef DEBUG - printf( "im_png2vips: reading \"%s\"\n", name ); -#endif /*DEBUG*/ - - if( !(read = read_new( name, out )) ) + if( vips_pngload( filename, &x, NULL ) ) return( -1 ); - if( png2vips( read, 0 ) ) { - read_destroy( read ); + if( vips_image_write( x, out ) ) { + g_object_unref( x ); return( -1 ); } - - read_destroy( read ); + g_object_unref( x ); return( 0 ); } @@ -421,16 +64,11 @@ im_png2vips( const char *name, IMAGE *out ) static int ispng( const char *filename ) { - unsigned char buf[8]; - - return( im__get_bytes( filename, buf, 8 ) && - !png_sig_cmp( buf, 0, 8 ) ); + return( vips_foreign_is_a( "pngload", filename ) ); } static const char *png_suffs[] = { ".png", NULL }; -/* png format adds no new members. - */ typedef VipsFormat VipsFormatPng; typedef VipsFormatClass VipsFormatPngClass; @@ -444,7 +82,6 @@ vips_format_png_class_init( VipsFormatPngClass *class ) 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; @@ -457,4 +94,3 @@ vips_format_png_init( VipsFormatPng *object ) G_DEFINE_TYPE( VipsFormatPng, vips_format_png, VIPS_TYPE_FORMAT ); -#endif /*HAVE_PNG*/ diff --git a/libvips/format/im_vips2jpeg.c b/libvips/format/im_vips2jpeg.c index e34141ff..83010ff8 100644 --- a/libvips/format/im_vips2jpeg.c +++ b/libvips/format/im_vips2jpeg.c @@ -47,222 +47,6 @@ #include #include -/* Convert to a saveable format. - * - * im__saveable_t gives the general type of image - * we make: vanilla 1/3 bands (eg. PPM), with an optional alpha (eg. PNG), or - * with CMYK as an option (eg. JPEG). - * - * format_table[] says how to convert each input format. - * - * Need to im_close() the result IMAGE. - */ - -/* Kept for back compat while we swap the rest of VipsFormat to the new - * system. - */ - -IMAGE * -im__convert_saveable( IMAGE *in, - im__saveable_t saveable, int format_table[10] ) -{ - IMAGE *out; - - if( !(out = im_open( "convert-for-save", "p" )) ) - return( NULL ); - - /* If this is an IM_CODING_LABQ, we can go straight to RGB. - */ - if( in->Coding == IM_CODING_LABQ ) { - IMAGE *t = im_open_local( out, "conv:1", "p" ); - static void *table = NULL; - - /* Make sure fast LabQ2disp tables are built. 7 is sRGB. - */ - if( !table ) - table = im_LabQ2disp_build_table( NULL, - im_col_displays( 7 ) ); - - if( !t || im_LabQ2disp_table( in, t, table ) ) { - im_close( out ); - return( NULL ); - } - - in = t; - } - - /* If this is an IM_CODING_RAD, we go to float RGB or XYZ. We should - * probably un-gamma-correct the RGB :( - */ - if( in->Coding == IM_CODING_RAD ) { - IMAGE *t; - - if( !(t = im_open_local( out, "conv:1", "p" )) || - im_rad2float( in, t ) ) { - im_close( out ); - return( NULL ); - } - - in = t; - } - - /* Get the bands right. - */ - if( in->Coding == IM_CODING_NONE ) { - if( in->Bands == 2 && saveable != IM__RGBA ) { - IMAGE *t = im_open_local( out, "conv:1", "p" ); - - if( !t || im_extract_band( in, t, 0 ) ) { - im_close( out ); - return( NULL ); - } - - in = t; - } - else if( in->Bands > 3 && saveable == IM__RGB ) { - IMAGE *t = im_open_local( out, "conv:1", "p" ); - - if( !t || - im_extract_bands( in, t, 0, 3 ) ) { - im_close( out ); - return( NULL ); - } - - in = t; - } - else if( in->Bands > 4 && - (saveable == IM__RGB_CMYK || saveable == IM__RGBA) ) { - IMAGE *t = im_open_local( out, "conv:1", "p" ); - - if( !t || - im_extract_bands( in, t, 0, 4 ) ) { - im_close( out ); - return( NULL ); - } - - in = t; - } - - /* Else we have saveable IM__ANY and we don't chop bands down. - */ - } - - /* Interpret the Type field for colorimetric images. - */ - if( in->Bands == 3 && in->BandFmt == IM_BANDFMT_SHORT && - in->Type == IM_TYPE_LABS ) { - IMAGE *t = im_open_local( out, "conv:1", "p" ); - - if( !t || im_LabS2LabQ( in, t ) ) { - im_close( out ); - return( NULL ); - } - - in = t; - } - - if( in->Coding == IM_CODING_LABQ ) { - IMAGE *t = im_open_local( out, "conv:1", "p" ); - - if( !t || im_LabQ2Lab( in, t ) ) { - im_close( out ); - return( NULL ); - } - - in = t; - } - - if( in->Coding != IM_CODING_NONE ) { - im_close( out ); - return( NULL ); - } - - if( in->Bands == 3 && in->Type == IM_TYPE_LCH ) { - IMAGE *t[2]; - - if( im_open_local_array( out, t, 2, "conv-1", "p" ) || - im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || - im_LCh2Lab( t[0], t[1] ) ) { - im_close( out ); - return( NULL ); - } - - in = t[1]; - } - - if( in->Bands == 3 && in->Type == IM_TYPE_YXY ) { - IMAGE *t[2]; - - if( im_open_local_array( out, t, 2, "conv-1", "p" ) || - im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || - im_Yxy2XYZ( t[0], t[1] ) ) { - im_close( out ); - return( NULL ); - } - - in = t[1]; - } - - if( in->Bands == 3 && in->Type == IM_TYPE_UCS ) { - IMAGE *t[2]; - - if( im_open_local_array( out, t, 2, "conv-1", "p" ) || - im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || - im_UCS2XYZ( t[0], t[1] ) ) { - im_close( out ); - return( NULL ); - } - - in = t[1]; - } - - if( in->Bands == 3 && in->Type == IM_TYPE_LAB ) { - IMAGE *t[2]; - - if( im_open_local_array( out, t, 2, "conv-1", "p" ) || - im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || - im_Lab2XYZ( t[0], t[1] ) ) { - im_close( out ); - return( NULL ); - } - - in = t[1]; - } - - if( in->Bands == 3 && in->Type == IM_TYPE_XYZ ) { - IMAGE *t[2]; - - if( im_open_local_array( out, t, 2, "conv-1", "p" ) || - im_clip2fmt( in, t[0], IM_BANDFMT_FLOAT ) || - im_XYZ2disp( t[0], t[1], im_col_displays( 7 ) ) ) { - im_close( out ); - return( NULL ); - } - - in = t[1]; - } - - /* Cast to the output format. - */ - { - IMAGE *t = im_open_local( out, "conv:1", "p" ); - - if( !t || im_clip2fmt( in, t, format_table[in->BandFmt] ) ) { - im_close( out ); - return( NULL ); - } - - in = t; - } - - if( im_copy( in, out ) ) { - im_close( out ); - return( NULL ); - } - - return( out ); -} - int im_vips2jpeg( IMAGE *in, const char *filename ) { diff --git a/libvips/format/im_vips2png.c b/libvips/format/im_vips2png.c index a8c05892..6d2895ff 100644 --- a/libvips/format/im_vips2png.c +++ b/libvips/format/im_vips2png.c @@ -1,30 +1,7 @@ /* 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 - * 2/11/07 - * - use im_wbuffer() API for BG writes - * 4/2/10 - * - gtkdoc - * - fixed 16-bit save - * 12/5/10 - * - lololo but broke 8-bit save, fixed again - * 20/7/10 Tim Elliott - * - added im_vips2bufpng() - * 8/1/11 - * - set png resolution (thanks Zhiyu Wu) - * 17/3/11 - * - update for libpng-1.5 API changes - * 28/3/11 - * - argh gamma was wrong when viewed in firefox + * 19/12/11 + * - just a stub */ /* @@ -62,295 +39,18 @@ #endif /*HAVE_CONFIG_H*/ #include -#ifndef HAVE_PNG - -#include - -int -im_vips2png( IMAGE *in, const char *filename ) -{ - im_error( "im_vips2png", "%s", - _( "PNG support disabled" ) ); - return( -1 ); -} - -#else /*HAVE_PNG*/ - #include #include +#include #include #include #include -#include - -#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_vips2png", _( "PNG error: \"%s\"" ), error_msg ); -} - -static void -user_warning_function( png_structp png_ptr, png_const_charp warning_msg ) -{ - im_error( "im_vips2png", _( "PNG warning: \"%s\"" ), warning_msg ); -} - -/* What we track during a PNG write. - */ -typedef struct { - IMAGE *in; - - FILE *fp; - png_structp pPng; - png_infop pInfo; - png_bytep *row_pointer; -} Write; - -static void -write_destroy( Write *write ) -{ - IM_FREEF( im_close, write->in ); - IM_FREEF( fclose, write->fp ); - if( write->pPng ) - png_destroy_write_struct( &write->pPng, &write->pInfo ); - IM_FREE( write->row_pointer ); - - im_free( write ); -} - -#define UC IM_BANDFMT_UCHAR -#define US IM_BANDFMT_USHORT - -/* Type promotion for save ... uchar or ushort. - */ -static int bandfmt_png[10] = { -/* UC C US S UI I F X D DX */ - UC, UC, US, US, US, US, UC, UC, UC, UC -}; - -static Write * -write_new( IMAGE *in ) -{ - Write *write; - - if( !(write = IM_NEW( NULL, Write )) ) - return( NULL ); - memset( write, 0, sizeof( Write ) ); - - if( !(write->in = im__convert_saveable( in, IM__RGBA, bandfmt_png )) ) { - im_error( "im_vips2png", - "%s", _( "unable to convert to saveable format" ) ); - write_destroy( write ); - return( NULL ); - } - - write->row_pointer = IM_ARRAY( NULL, in->Ysize, png_bytep ); - write->fp = NULL; - write->pPng = NULL; - write->pInfo = NULL; - - if( !write->row_pointer ) { - write_destroy( write ); - return( NULL ); - } - - if( !(write->pPng = png_create_write_struct( - PNG_LIBPNG_VER_STRING, NULL, - user_error_function, user_warning_function )) ) { - write_destroy( write ); - return( NULL ); - } - - /* Catch PNG errors from png_create_info_struct(). - */ - if( setjmp( png_jmpbuf( write->pPng ) ) ) { - write_destroy( write ); - return( NULL ); - } - - if( !(write->pInfo = png_create_info_struct( write->pPng )) ) { - write_destroy( write ); - return( NULL ); - } - - return( write ); -} - -static int -write_png_block( REGION *region, Rect *area, void *a ) -{ - Write *write = (Write *) a; - - int i; - - /* The area to write is always a set of complete scanlines. - */ - g_assert( area->left == 0 ); - g_assert( area->width == region->im->Xsize ); - g_assert( area->top + area->height <= region->im->Ysize ); - - /* Catch PNG errors. Yuk. - */ - if( setjmp( png_jmpbuf( write->pPng ) ) ) - return( -1 ); - - for( i = 0; i < area->height; i++ ) - write->row_pointer[i] = (png_bytep) - IM_REGION_ADDR( region, 0, area->top + i ); - - png_write_rows( write->pPng, write->row_pointer, area->height ); - - return( 0 ); -} - -/* Write a VIPS image to PNG. - */ -static int -write_vips( Write *write, int compress, int interlace ) -{ - IMAGE *in = write->in; - - int bit_depth; - int color_type; - int interlace_type; - int i, nb_passes; - - g_assert( in->BandFmt == IM_BANDFMT_UCHAR || - in->BandFmt == IM_BANDFMT_USHORT ); - g_assert( in->Coding == IM_CODING_NONE ); - g_assert( in->Bands > 0 && in->Bands < 5 ); - - /* Catch PNG errors. - */ - if( setjmp( png_jmpbuf( write->pPng ) ) ) - return( -1 ); - - /* Check input image. - */ - if( im_pincheck( in ) ) - return( -1 ); - if( compress < 0 || compress > 9 ) { - im_error( "im_vips2png", - "%s", _( "compress should be in [0,9]" ) ); - return( -1 ); - } - - /* Set compression parameters. - */ - png_set_compression_level( write->pPng, compress ); - - bit_depth = in->BandFmt == IM_BANDFMT_UCHAR ? 8 : 16; - - switch( in->Bands ) { - case 1: color_type = PNG_COLOR_TYPE_GRAY; break; - case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; - case 3: color_type = PNG_COLOR_TYPE_RGB; break; - case 4: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; - - default: - g_assert( 0 ); - } - - interlace_type = interlace ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE; - - png_set_IHDR( write->pPng, write->pInfo, - in->Xsize, in->Ysize, bit_depth, color_type, interlace_type, - PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT ); - - /* Set resolution. libpnbg uses pixels per meter. - */ - png_set_pHYs( write->pPng, write->pInfo, - IM_RINT( in->Xres * 1000 ), IM_RINT( in->Yres * 1000 ), - PNG_RESOLUTION_METER ); - - png_write_info( write->pPng, write->pInfo ); - - /* If we're an intel byte order CPU and this is a 16bit image, we need - * to swap bytes. - */ - if( bit_depth > 8 && !im_amiMSBfirst() ) - png_set_swap( write->pPng ); - - if( interlace ) - nb_passes = png_set_interlace_handling( write->pPng ); - else - nb_passes = 1; - - /* Write data. - */ - for( i = 0; i < nb_passes; i++ ) - if( vips_sink_disc( write->in, write_png_block, write ) ) - return( -1 ); - - /* The setjmp() was held by our background writer: reset it. - */ - if( setjmp( png_jmpbuf( write->pPng ) ) ) - return( -1 ); - - png_write_end( write->pPng, write->pInfo ); - - return( 0 ); -} - -/** - * im_vips2png: - * @in: image to save - * @filename: file to write to - * - * Write a VIPS image to a file as PNG. - * - * You can embed options in the filename. They have the form: - * - * |[ - * filename.png:compression,interlace - * ]| - * - * - * - * - * compression - * Compress with this much effort (0 - 9). Default 6. - * - * - * - * - * interlace - * 0 means don't interlace (the default), 1 selects ADAM7 interlacing. Beware - * than an interlaced PNG can be up to 7 times slower to write than a - * non-interlaced image. - * - * - * - * - * There is no support for attaching ICC profiles to PNG images. - * - * The image is automatically converted to RGB, RGBA, Monochrome or Mono + - * alpha before saving. Images with more than one byte per band element are - * saved as 16-bit PNG, others are saved as 8-bit PNG. - * - * Example: - * - * |[ - * im_vips2png( in, "fred.png:0,1" ); - * ]| - * - * Will write "fred.png" with no compression and with ADAM7 interlacing. - * - * See also: #VipsFormat, im_png2vips(). - * - * Returns: 0 on success, -1 on error. - */ int im_vips2png( IMAGE *in, const char *filename ) { - Write *write; - int compress; + int compression; int interlace; char *p, *q; @@ -359,169 +59,33 @@ im_vips2png( IMAGE *in, const char *filename ) char mode[FILENAME_MAX]; char buf[FILENAME_MAX]; - if( !(write = write_new( in )) ) - return( -1 ); - /* Extract write mode from filename and parse. */ im_filename_split( filename, name, mode ); strcpy( buf, mode ); p = &buf[0]; - compress = 6; + compression = 6; interlace = 0; if( (q = im_getnextoption( &p )) ) - compress = atoi( q ); + compression = atoi( q ); if( (q = im_getnextoption( &p )) ) interlace = atoi( q ); - /* Make output. - */ - if( !(write->fp = im__file_open_write( name, FALSE )) ) { - write_destroy( write ); - return( -1 ); - } - png_init_io( write->pPng, write->fp ); - - /* Convert it! - */ - if( write_vips( write, compress, interlace ) ) { - write_destroy( write ); - im_error( "im_vips2png", _( "unable to write \"%s\"" ), name ); - - return( -1 ); - } - write_destroy( write ); - - return( 0 ); + return( vips_pngsave( in, name, + "compression", compression, "interlace", interlace, NULL ) ); } -typedef struct _WriteBuf { - char *buf; - size_t len; - size_t alloc; -} WriteBuf; - -static void -write_buf_free( WriteBuf *wbuf ) -{ - IM_FREE( wbuf ); -} - -static WriteBuf * -write_buf_new( void ) -{ - WriteBuf *wbuf; - - if( !(wbuf = IM_NEW( NULL, WriteBuf )) ) - return( NULL ); - - wbuf->buf = NULL; - wbuf->len = 0; - wbuf->alloc = 0; - - return( wbuf ); -} - -static void -write_buf_grow( WriteBuf *wbuf, size_t grow_len ) -{ - size_t new_len = wbuf->len + grow_len; - - if( new_len > wbuf->alloc ) { - size_t proposed_alloc = (16 + wbuf->alloc) * 3 / 2; - - wbuf->alloc = IM_MAX( proposed_alloc, new_len ); - - /* There's no im_realloc(), so we call g_realloc() directly. - * This is safe, since im_malloc() / im_free() are wrappers - * over g_malloc() / g_free(). - * - * FIXME: add im_realloc(). - */ - wbuf->buf = g_realloc( wbuf->buf, wbuf->alloc ); - - VIPS_DEBUG_MSG( "write_buf_grow: grown to %zd bytes\n", - wbuf->alloc ); - } -} - -static void -user_write_data( png_structp png_ptr, png_bytep data, png_size_t length ) -{ - WriteBuf *wbuf = (WriteBuf *) png_get_io_ptr( png_ptr ); - - char *write_start; - - write_buf_grow( wbuf, length ); - - write_start = wbuf->buf + wbuf->len; - memcpy( write_start, data, length ); - - wbuf->len += length; - - g_assert( wbuf->len <= wbuf->alloc ); -} - -/** - * im_vips2bufpng: - * @in: image to save - * @out: allocate output buffer local to this - * @compression: compress with this much effort - * @interlace: 0 means don't interlace, 1 selects ADAM7 interlacing - * @obuf: return output buffer here - * @olen: return output length here - * - * As im_vips2png(), but save as a memory buffer. The memory is allocated - * local to @out (that is, when @out is closed the memory will be released, - * pass %NULL to release yourself). - * - * The address of the buffer is returned in @obuf, the length of the buffer in - * @olen. - * - * See also: #VipsFormat, im_vips2png(). - * - * Returns: 0 on success, -1 on error. - */ int im_vips2bufpng( IMAGE *in, IMAGE *out, int compression, int interlace, char **obuf, size_t *olen ) { - WriteBuf *wbuf; - Write *write; - - if( !(wbuf = write_buf_new()) || - !(write = write_new( in )) ) + if( vips_pngsave_buffer( in, (void **) obuf, olen, + "compression", compression, + "interlace", interlace, + NULL ) ) return( -1 ); - - png_set_write_fn( write->pPng, wbuf, user_write_data, NULL ); - - /* Convert it! - */ - if( write_vips( write, compression, interlace ) ) { - write_destroy( write ); - write_buf_free( wbuf ); - im_error( "im_vips2bufpng", - "%s", _( "unable to write to buffer" ) ); - - return( -1 ); - } - write_destroy( write ); - - *obuf = wbuf->buf; - *olen = wbuf->len; - - write_buf_free( wbuf ); - - if( out && im_add_close_callback( out, - (im_callback_fn) im_free, *obuf, NULL ) ) { - im_free( *obuf ); - *obuf = NULL; - *olen = 0; - - return( -1 ); - } + im_add_callback( out, "close", + (im_callback_fn) vips_free, obuf, NULL ); return( 0 ); } - -#endif /*HAVE_PNG*/ diff --git a/libvips/format/old_format.c b/libvips/format/old_format.c deleted file mode 100644 index 6ca3d330..00000000 --- a/libvips/format/old_format.c +++ /dev/null @@ -1,618 +0,0 @@ -/* VIPS function dispatch tables for image format load/save. - */ - -/* - - 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 - - */ - -#ifdef HAVE_CONFIG_H -#include -#endif /*HAVE_CONFIG_H*/ -#include - -#include - -#include -#include - -/** - * SECTION: format - * @short_description: load and save in a variety of formats - * @stability: Stable - * @see_also: image - * @include: vips/vips.h - * - * VIPS has a simple system for representing image load and save operations in - * a generic way. - * - * You can ask for a loader for a certain file or select a saver based on a - * filename. Once you have found a format, you can use it to load a file of - * that type, save an image to a file of that type, query files for their type - * and fields, and ask for supported features. You can also call the - * converters directly, if you like. - * - * If you define a new format, support for - * it automatically appears in all VIPS user-interfaces. It will also be - * transparently supported by vips_image_new_from_file() and friends. - * - * VIPS comes with VipsFormat for TIFF, JPEG, PNG, Analyze, PPM, OpenEXR, CSV, - * Matlab, Radiance, RAW, VIPS and one that wraps libMagick. - */ - -/** - * VipsFormatFlags: - * @VIPS_FORMAT_NONE: no flags set - * @VIPS_FORMAT_PARTIAL: the image may be read lazilly - * @VIPS_FORMAT_BIGENDIAN: image pixels are most-significant byte first - * - * Some hints about the image loader. - * - * @VIPS_FORMAT_PARTIAL means that the image can be read directly from the - * file without needing to be unpacked to a temporary image first. - * - * @VIPS_FORMAT_BIGENDIAN means that image pixels are most-significant byte - * first. Depending on the native byte order of the host machine, you may - * need to swap bytes. See copy_swap(). - */ - -/** - * VipsFormat: - * - * #VipsFormat has these virtual methods: - * - * |[ - * typedef struct _VipsFormatClass { - * VipsObjectClass parent_class; - * - * gboolean (*is_a)( const char *filename ); - * int (*header)( const char *filename, IMAGE *out ); - * int (*load)( const char *filename, IMAGE *out ); - * int (*save)( IMAGE *in, const char *filename ); - * VipsFormatFlags (*get_flags)( const char *filename ); - * int priority; - * const char **suffs; - * } VipsFormatClass; - * ]| - * - * Add a new format to VIPS by subclassing VipsFormat. Subclasses need to - * implement at least load() or save(). - * - * These members are: - * - * - * - * - * is_a() This function should return %TRUE if the file - * contains an image of this type. If you don't define this function, VIPS - * will use the list of suffixes you supply instead. - * - * - * - * - * header() This function should load the image header, - * but not load any pixel data. If you don't define it, VIPS will use your - * load() method instead. Return 0 for success, -1 for error, setting - * vips_error(). - * - * - * - * - * load() This function should load the image, or perhaps use - * vips_image_generate() to - * attach something to load sections of the image on demand. - * Users can embed - * load options in the filename, see (for example) im_jpeg2vips(). - * If you don't - * define this method, you can still define save() and have a save-only - * format. - * Return 0 for success, -1 for error, setting - * im_error(). - * - * - * - * - * save() This function should save the image to the file. - * Users can embed - * save options in the filename, see (for example) im_vips2tiff(). - * If you don't - * define this method, you can still define load() and have a load-only - * format. - * Return 0 for success, -1 for error, setting - * im_error(). - * - * - * - * - * get_flags() This function should return a hint about the properties of this - * loader on this file. If you don't define it, users will always see '0', or - * no flags. - * - * - * - * - * priority Where this format should fit in this - * list of - * supported formats. 0 is a sensible value for most formats. Set a negative - * value if you want to be lower on the list, positive to move up. - * - * - * - * - * suffs A %NULL-terminated list of possible file - * name - * suffixes, for example: - * |[ - * static const char *tiff_suffs[] = { ".tif", ".tiff", NULL }; - * ]| - * The suffix list is used to select a format to save a file in, and to pick a - * loader if you don't define is_a(). - * - * - * - * - * You should also define nickname and - * description in #VipsObject. - * - * At the command-line, use: - * - * |[ - * vips --list classes | grep Format - * ]| - * - * To see a list of all the supported formats. - * - * For example, the TIFF format is defined like this: - * -|[ -typedef VipsFormat VipsFormatTiff; -typedef VipsFormatClass VipsFormatTiffClass; - -static void -vips_format_tiff_class_init( VipsFormatTiffClass *class ) -{ - VipsObjectClass *object_class = (VipsObjectClass *) class; - VipsFormatClass *format_class = (VipsFormatClass *) class; - - object_class->nickname = "tiff"; - object_class->description = _( "TIFF" ); - - format_class->is_a = istiff; - format_class->header = tiff2vips_header; - format_class->load = im_tiff2vips; - format_class->save = im_vips2tiff; - format_class->get_flags = tiff_flags; - format_class->suffs = tiff_suffs; -} - -static void -vips_format_tiff_init( VipsFormatTiff *object ) -{ -} - -G_DEFINE_TYPE( VipsFormatTiff, vips_format_tiff, VIPS_TYPE_FORMAT ); -]| - * - * Then call vips_format_tiff_get_type() somewhere in your init code to link - * the format into VIPS (though of course the tiff format is linked in for you - * already). - * - */ - -/* To iterate over supported formats, we build a temp list of subclasses of - * VipsFormat, sort by priority, iterate, and free. - */ - -static void * -format_add_class( VipsFormatClass *format, GSList **formats ) -{ - /* Append so we don't reverse the list of formats. - */ - *formats = g_slist_append( *formats, format ); - - return( NULL ); -} - -static gint -format_compare( VipsFormatClass *a, VipsFormatClass *b ) -{ - return( b->priority - a->priority ); -} - -/** - * vips_format_map: - * @base: base class to search below (eg. "VipsFormatLoad") - * @fn: function to apply to each #VipsFormatClass - * @a: user data - * @b: user data - * - * Apply a function to every #VipsFormatClass that VIPS knows about. Formats - * are presented to the function in priority order. - * - * Like all VIPS map functions, if @fn returns %NULL, iteration continues. If - * it returns non-%NULL, iteration terminates and that value is returned. The - * map function returns %NULL if all calls return %NULL. - * - * See also: vips_slist_map(). - * - * Returns: the result of iteration - */ -void * -vips_format_map( const char *base, VipsSListMap2Fn fn, void *a, void *b ) -{ - GSList *formats; - void *result; - - formats = NULL; - (void) vips_class_map_all( g_type_from_name( base ), - (VipsClassMapFn) format_add_class, (void *) &formats ); - - formats = g_slist_sort( formats, (GCompareFunc) format_compare ); - result = vips_slist_map2( formats, fn, a, b ); - g_slist_free( formats ); - - return( result ); -} - -/* Abstract base class for image formats. - */ - -G_DEFINE_ABSTRACT_TYPE( VipsFormat, vips_format, VIPS_TYPE_OBJECT ); - -static void -vips_format_print_class( VipsObjectClass *object_class, VipsBuf *buf ) -{ - VipsFormatClass *class = VIPS_FORMAT_CLASS( object_class ); - const char **p; - - VIPS_OBJECT_CLASS( vips_format_parent_class )-> - print_class( object_class, buf ); - vips_buf_appends( buf, ", " ); - - if( class->suffs ) { - vips_buf_appends( buf, "(" ); - for( p = class->suffs; *p; p++ ) { - vips_buf_appendf( buf, "%s", *p ); - if( p[1] ) - vips_buf_appends( buf, ", " ); - } - vips_buf_appends( buf, ") " ); - } - - if( class->is_a ) - vips_buf_appends( buf, "is_a " ); - if( class->header ) - vips_buf_appends( buf, "header " ); - if( class->load ) - vips_buf_appends( buf, "load " ); - if( class->save ) - vips_buf_appends( buf, "save " ); - if( class->get_flags ) - vips_buf_appends( buf, "get_flags " ); -} - -static void -vips_format_class_init( VipsFormatClass *class ) -{ - VipsObjectClass *object_class = (VipsObjectClass *) class; - - object_class->nickname = "format"; - object_class->description = _( "VIPS file formats" ); - object_class->print_class = vips_format_print_class; -} - -static void -vips_format_init( VipsFormat *object ) -{ -} - -/** - * vips_format_get_flags: - * @format: format to test - * @filename: file to test - * - * Get a set of flags for this file. - * - * Returns: flags for this format and file - */ -VipsFormatFlags -vips_format_get_flags( VipsFormatClass *format, const char *filename ) -{ - return( format->get_flags ? format->get_flags( filename ) : 0 ); -} - -/* VIPS format class. - */ - -static const char *vips_suffs[] = { ".v", NULL }; - -static int -isvips( const char *filename ) -{ - unsigned char buf[4]; - - if( vips__get_bytes( filename, buf, 4 ) ) { - if( buf[0] == 0x08 && buf[1] == 0xf2 && - buf[2] == 0xa6 && buf[3] == 0xb6 ) - /* SPARC-order VIPS image. - */ - return( 1 ); - else if( buf[3] == 0x08 && buf[2] == 0xf2 && - buf[1] == 0xa6 && buf[0] == 0xb6 ) - /* INTEL-order VIPS image. - */ - return( 1 ); - } - - return( 0 ); -} - -static int -file2vips( const char *filename, VipsImage *out ) -{ - VipsImage *t; - - if( !(t = vips_image_new_from_file( filename )) ) - return( -1 ); - if( vips_image_write( t, out ) ) { - g_object_unref( t ); - return( -1 ); - } - g_object_unref( t ); - - return( 0 ); -} - -static VipsFormatFlags -vips_flags( const char *filename ) -{ - VipsFormatFlags flags; - unsigned char buf[4]; - - flags = VIPS_FORMAT_PARTIAL; - - if( vips__get_bytes( filename, buf, 4 ) && - buf[0] == 0x08 && - buf[1] == 0xf2 && - buf[2] == 0xa6 && - buf[3] == 0xb6 ) - flags |= VIPS_FORMAT_BIGENDIAN; - - return( flags ); -} - -/* Vips format adds no new members. - */ -typedef VipsFormat VipsFormatVips; -typedef VipsFormatClass VipsFormatVipsClass; - -static void -vips_format_vips_class_init( VipsFormatVipsClass *class ) -{ - VipsObjectClass *object_class = (VipsObjectClass *) class; - VipsFormatClass *format_class = (VipsFormatClass *) class; - - object_class->nickname = "vips"; - object_class->description = _( "VIPS" ); - - format_class->is_a = isvips; - format_class->header = file2vips; - format_class->load = file2vips; - format_class->save = vips_image_write_to_file; - format_class->get_flags = vips_flags; - format_class->suffs = vips_suffs; -} - -static void -vips_format_vips_init( VipsFormatVips *object ) -{ -} - -G_DEFINE_TYPE( VipsFormatVips, vips_format_vips, VIPS_TYPE_FORMAT ); - -/* Called on startup: register the base vips formats. - */ -void -vips__format_init( void ) -{ - extern GType vips_format_csv_get_type(); - extern GType vips_format_ppm_get_type(); - extern GType vips_format_analyze_get_type(); - extern GType vips_format_rad_get_type(); - - vips_format_vips_get_type(); -#ifdef HAVE_JPEG - extern GType vips_format_jpeg_get_type(); - vips_format_jpeg_get_type(); -#endif /*HAVE_JPEG*/ -#ifdef HAVE_PNG - extern GType vips_format_png_get_type(); - vips_format_png_get_type(); -#endif /*HAVE_PNG*/ - vips_format_csv_get_type(); - vips_format_ppm_get_type(); - vips_format_analyze_get_type(); -#ifdef HAVE_OPENEXR - extern GType vips_format_exr_get_type(); - vips_format_exr_get_type(); -#endif /*HAVE_OPENEXR*/ -#ifdef HAVE_MATIO - extern GType vips_format_mat_get_type(); - vips_format_mat_get_type(); -#endif /*HAVE_MATIO*/ -#ifdef HAVE_CFITSIO - extern GType vips_format_fits_get_type(); - vips_format_fits_get_type(); -#endif /*HAVE_CFITSIO*/ - vips_format_rad_get_type(); -#ifdef HAVE_MAGICK - extern GType vips_format_magick_get_type(); - vips_format_magick_get_type(); -#endif /*HAVE_MAGICK*/ -#ifdef HAVE_TIFF - extern GType vips_format_tiff_get_type(); - vips_format_tiff_get_type(); -#endif /*HAVE_TIFF*/ -} - -/* Can this format open this file? - */ -static void * -format_for_file_sub( VipsFormatClass *format, - const char *name, const char *filename ) -{ - if( format->is_a ) { - if( format->is_a( filename ) ) - return( format ); - } - else if( vips_filename_suffix_match( filename, format->suffs ) ) - return( format ); - - return( NULL ); -} - -/** - * vips_format_for_file: - * @filename: file to find a format for - * - * Searches for a format you could use to load a file. - * - * See also: vips_format_read(), vips_format_for_name(). - * - * Returns: a format on success, %NULL on error - */ -VipsFormatClass * -vips_format_for_file( const char *filename ) -{ - char name[FILENAME_MAX]; - char options[FILENAME_MAX]; - VipsFormatClass *format; - - /* Break any options off the name ... eg. "fred.tif:jpeg,tile" - * etc. - */ - vips_filename_split( filename, name, options ); - - if( !vips_existsf( "%s", name ) ) { - vips_error( "VipsFormat", _( "file \"%s\" not found" ), name ); - return( NULL ); - } - - if( !(format = (VipsFormatClass *) vips_format_map( - (VipsSListMap2Fn) format_for_file_sub, - (void *) filename, (void *) name )) ) { - vips_error( "VipsFormat", - _( "file \"%s\" not a known format" ), name ); - return( NULL ); - } - - return( format ); -} - -/* Can we write this filename with this format? Ignore formats without a save - * method. - */ -static void * -format_for_name_sub( VipsFormatClass *format, const char *name ) -{ - if( format->save && - vips_filename_suffix_match( name, format->suffs ) ) - return( format ); - - return( NULL ); -} - -/** - * vips_format_for_name: - * @filename: name to find a format for - * - * Searches for a format you could use to save a file. - * - * See also: vips_format_write(), vips_format_for_file(). - * - * Returns: a format on success, %NULL on error - */ -VipsFormatClass * -vips_format_for_name( const char *filename ) -{ - VipsFormatClass *format; - - if( !(format = (VipsFormatClass *) vips_format_map( - (VipsSListMap2Fn) format_for_name_sub, - (void *) filename, NULL )) ) { - vips_error( "VipsFormat", - _( "\"%s\" is not a supported image format." ), - filename ); - - return( NULL ); - } - - return( format ); -} - -/** - * vips_format_read: - * @filename: file to load - * @out: write the file to this image - * - * Searches for a format for this file, then loads the file into @out. - * - * See also: vips_format_write(). - * - * Returns: 0 on success, -1 on error - */ -int -vips_format_read( const char *filename, IMAGE *out ) -{ - VipsFormatClass *format; - - if( !(format = vips_format_for_file( filename )) || - format->load( filename, out ) ) - return( -1 ); - - return( 0 ); -} - -/** - * vips_format_write: - * @in: image to write - * @filename: file to write to - * - * Searches for a format for this name, then saves @im to it. - * - * See also: vips_format_read(). - * - * Returns: 0 on success, -1 on error - */ -int -vips_format_write( IMAGE *in, const char *filename ) -{ - VipsFormatClass *format; - - if( !(format = vips_format_for_name( filename )) || - format->save( in, filename ) ) - return( -1 ); - - return( 0 ); -} diff --git a/libvips/include/vips/foreign.h b/libvips/include/vips/foreign.h index 906a67af..f198cd3c 100644 --- a/libvips/include/vips/foreign.h +++ b/libvips/include/vips/foreign.h @@ -349,6 +349,13 @@ int vips_csvsave( VipsImage *in, const char *filename, ... ) int vips_magickload( const char *filename, VipsImage **out, ... ) __attribute__((sentinel)); +int vips_pngload( const char *filename, VipsImage **out, ... ) + __attribute__((sentinel)); +int vips_pngsave( VipsImage *in, const char *filename, ... ) + __attribute__((sentinel)); +int vips_pngsave_buffer( VipsImage *in, void **buf, size_t *len, ... ) + __attribute__((sentinel)); + #ifdef __cplusplus } #endif /*__cplusplus*/ diff --git a/libvips/include/vips/internal.h b/libvips/include/vips/internal.h index 066d1e8d..9c3700af 100644 --- a/libvips/include/vips/internal.h +++ b/libvips/include/vips/internal.h @@ -146,16 +146,6 @@ void im__analyze_register( void ); void im__exr_register( void ); void im__magick_register( void ); -typedef enum { - IM__RGB, /* 1 or 3 bands (eg. PPM) */ - IM__RGBA, /* 1, 2, 3 or 4 bands (eg. PNG) */ - IM__RGB_CMYK, /* 1, 3 or 4 bands (eg. JPEG) */ - IM__ANY /* any number of bands (eg. TIFF) */ -} im__saveable_t; - -VipsImage *im__convert_saveable( VipsImage *in, - im__saveable_t saveable, int format_table[10] ); - int im__bandup( const char *domain, VipsImage *in, VipsImage *out, int n ); int im__bandalike_vec( const char *domain, VipsImage **in, VipsImage **out, int n ); int im__bandalike( const char *domain,