mvoe png load/save over
This commit is contained in:
parent
3015d7d7b8
commit
0377920764
@ -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
|
||||
|
16
TODO
16
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
|
||||
|
29
configure.in
29
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)]))
|
||||
|
@ -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 \
|
||||
|
@ -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 );
|
||||
}
|
||||
|
136
libvips/foreign/pngload.c
Normal file
136
libvips/foreign/pngload.c
Normal file
@ -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 <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/buf.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
#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 )
|
||||
{
|
||||
}
|
236
libvips/foreign/pngsave.c
Normal file
236
libvips/foreign/pngsave.c
Normal file
@ -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 <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#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 )
|
||||
{
|
||||
}
|
685
libvips/foreign/vipspng.c
Normal file
685
libvips/foreign/vipspng.c
Normal file
@ -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 <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
#include <vips/debug.h>
|
||||
|
||||
#include <png.h>
|
||||
|
||||
#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 );
|
||||
}
|
||||
|
51
libvips/foreign/vipspng.h
Normal file
51
libvips/foreign/vipspng.h
Normal file
@ -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*/
|
@ -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 </libexif/ prefix if required
|
||||
* 11/2/08
|
||||
* - spot CMYK jpegs and set Type
|
||||
* - spot Adobe CMYK JPEG and invert ink density
|
||||
* 15/2/08
|
||||
* - added "shrink" parameter
|
||||
* 16/6/09
|
||||
* - added "fail" option ... fail on any warnings
|
||||
* 12/10/09
|
||||
* - also set scale_num on shrink (thanks Guido)
|
||||
* 4/2/10
|
||||
* - gtkdoc
|
||||
* 4/12/10
|
||||
* - attach the jpeg thumbnail and multiscan fields (thanks Mike)
|
||||
* 21/2/10
|
||||
* - only accept the first APP1 block which starts "Exif..." as exif
|
||||
* data, some jpegs seem to have several APP1s, argh
|
||||
* 20/4/2011
|
||||
* - added im_bufjpeg2vips()
|
||||
* 12/10/2011
|
||||
* - read XMP data
|
||||
* 3/11/11
|
||||
* - attach exif tags as coded values
|
||||
* 30/11/11
|
||||
* - now just a stub
|
||||
*/
|
||||
|
@ -1,27 +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
|
||||
* 8/5/06
|
||||
* - set RGB16/GREY16 if appropriate
|
||||
* 28/2/09
|
||||
* - small cleanups
|
||||
* 4/2/10
|
||||
* - gtkdoc
|
||||
* 8/1/11
|
||||
* - get 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
|
||||
* 19/12/11
|
||||
* - just a stub
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -59,361 +39,24 @@
|
||||
#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>
|
||||
|
||||
#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*/
|
||||
|
@ -47,222 +47,6 @@
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
|
@ -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 <vips/intl.h>
|
||||
|
||||
#ifndef HAVE_PNG
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
int
|
||||
im_vips2png( IMAGE *in, const char *filename )
|
||||
{
|
||||
im_error( "im_vips2png", "%s",
|
||||
_( "PNG support disabled" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#else /*HAVE_PNG*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
#include <vips/debug.h>
|
||||
|
||||
#include <png.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 )
|
||||
{
|
||||
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:<emphasis>compression</emphasis>,<emphasis>interlace</emphasis>
|
||||
* ]|
|
||||
*
|
||||
* <itemizedlist>
|
||||
* <listitem>
|
||||
* <para>
|
||||
* <emphasis>compression</emphasis>
|
||||
* Compress with this much effort (0 - 9). Default 6.
|
||||
* </para>
|
||||
* </listitem>
|
||||
* <listitem>
|
||||
* <para>
|
||||
* <emphasis>interlace</emphasis>
|
||||
* 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.
|
||||
* </para>
|
||||
* </listitem>
|
||||
* </itemizedlist>
|
||||
*
|
||||
* 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*/
|
||||
|
@ -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 <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
#include <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
/**
|
||||
* SECTION: format
|
||||
* @short_description: load and save in a variety of formats
|
||||
* @stability: Stable
|
||||
* @see_also: <link linkend="libvips-image">image</link>
|
||||
* @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:
|
||||
*
|
||||
* <itemizedlist>
|
||||
* <listitem>
|
||||
* <para>
|
||||
* 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.
|
||||
* </para>
|
||||
* </listitem>
|
||||
* <listitem>
|
||||
* <para>
|
||||
* 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().
|
||||
* </para>
|
||||
* </listitem>
|
||||
* <listitem>
|
||||
* <para>
|
||||
* 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().
|
||||
* </para>
|
||||
* </listitem>
|
||||
* <listitem>
|
||||
* <para>
|
||||
* 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().
|
||||
* </para>
|
||||
* </listitem>
|
||||
* <listitem>
|
||||
* <para>
|
||||
* 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.
|
||||
* </para>
|
||||
* </listitem>
|
||||
* <listitem>
|
||||
* <para>
|
||||
* <structfield>priority</structfield> 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.
|
||||
* </para>
|
||||
* </listitem>
|
||||
* <listitem>
|
||||
* <para>
|
||||
* <structfield>suffs</structfield> 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().
|
||||
* </para>
|
||||
* </listitem>
|
||||
* </itemizedlist>
|
||||
*
|
||||
* You should also define <structfield>nickname</structfield> and
|
||||
* <structfield>description</structfield> 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 );
|
||||
}
|
@ -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*/
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user