generic sequential read mode
moved sequential read stuff into VipsForeignLoad
This commit is contained in:
parent
71d55c4554
commit
aee2999259
@ -3,7 +3,7 @@
|
|||||||
- added vips_foreign_find_save_options()/vips_foreign_find_load_options()
|
- added vips_foreign_find_save_options()/vips_foreign_find_load_options()
|
||||||
- delayed write to foreign via a "w" image was not working
|
- delayed write to foreign via a "w" image was not working
|
||||||
- support operations with many returns in Python
|
- support operations with many returns in Python
|
||||||
- sequential read mode for pngload
|
- sequential read mode
|
||||||
- better im_shrink()
|
- better im_shrink()
|
||||||
|
|
||||||
20/8/11 started 7.27.0
|
20/8/11 started 7.27.0
|
||||||
|
3
TODO
3
TODO
@ -1,7 +1,6 @@
|
|||||||
- add a sequential mode to all readers
|
- add a sequential mode to all readers
|
||||||
|
|
||||||
we can't do this neatly in the base load class until we move all loaders to
|
tiff, jpg are the obvious ones
|
||||||
classes ... do an ad-hoc hack for now
|
|
||||||
|
|
||||||
- argh
|
- argh
|
||||||
|
|
||||||
|
@ -1,4 +1,7 @@
|
|||||||
/* VIPS function dispatch tables for image file load/save.
|
/* VIPS function dispatch tables for image file load/save.
|
||||||
|
*
|
||||||
|
* 7/2/12
|
||||||
|
* - add support for sequential reads
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -28,8 +31,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#define DEBUG
|
|
||||||
*/
|
*/
|
||||||
|
#define DEBUG
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include <config.h>
|
#include <config.h>
|
||||||
@ -62,7 +65,7 @@
|
|||||||
* process, loading an image from a file or saving an image to a file. These
|
* process, loading an image from a file or saving an image to a file. These
|
||||||
* functions let you give load or save options as name - value pairs in the C
|
* functions let you give load or save options as name - value pairs in the C
|
||||||
* argument list. You can use vips_foreign_load_options() and
|
* argument list. You can use vips_foreign_load_options() and
|
||||||
* vips_foreign_save_options() to include option in the file name.
|
* vips_foreign_save_options() to include options in the file name.
|
||||||
*
|
*
|
||||||
* For example:
|
* For example:
|
||||||
*
|
*
|
||||||
@ -75,6 +78,12 @@
|
|||||||
* Will save an image to the file "frank.tiff" in TIFF format (selected by
|
* Will save an image to the file "frank.tiff" in TIFF format (selected by
|
||||||
* the file name extension) with JPEG compression.
|
* the file name extension) with JPEG compression.
|
||||||
*
|
*
|
||||||
|
* |[
|
||||||
|
* vips_foreign_save_options (my_image, "frank.tiff[compression=jpeg]");
|
||||||
|
* ]|
|
||||||
|
*
|
||||||
|
* Is the same thing, but with the option in the filename.
|
||||||
|
*
|
||||||
* You can also invoke the operations directly, for example:
|
* You can also invoke the operations directly, for example:
|
||||||
*
|
*
|
||||||
* |[
|
* |[
|
||||||
@ -100,6 +109,7 @@
|
|||||||
* VipsForeignFlags:
|
* VipsForeignFlags:
|
||||||
* @VIPS_FOREIGN_NONE: no flags set
|
* @VIPS_FOREIGN_NONE: no flags set
|
||||||
* @VIPS_FOREIGN_PARTIAL: the image may be read lazilly
|
* @VIPS_FOREIGN_PARTIAL: the image may be read lazilly
|
||||||
|
* @VIPS_FOREIGN_SEQUENTIAL: top-to-bottom lazy reading
|
||||||
* @VIPS_FOREIGN_BIGENDIAN: image pixels are most-significant byte first
|
* @VIPS_FOREIGN_BIGENDIAN: image pixels are most-significant byte first
|
||||||
*
|
*
|
||||||
* Some hints about the image loader.
|
* Some hints about the image loader.
|
||||||
@ -107,6 +117,13 @@
|
|||||||
* @VIPS_FOREIGN_PARTIAL means that the image can be read directly from the
|
* @VIPS_FOREIGN_PARTIAL means that the image can be read directly from the
|
||||||
* file without needing to be unpacked to a temporary image first.
|
* file without needing to be unpacked to a temporary image first.
|
||||||
*
|
*
|
||||||
|
* @VIPS_FOREIGN_SEQUENTIAL means that the loader supports lazy reading, but
|
||||||
|
* only top-to-bottom (sequential) access. Formats like PNG can read sets of
|
||||||
|
* scanlines, for example, but only in order.
|
||||||
|
*
|
||||||
|
* If neither PARTIAL or SEQUENTIAL is set, the loader only supports whole
|
||||||
|
* image read. Setting both PARTIAL and SEQUENTIAL is an error.
|
||||||
|
*
|
||||||
* @VIPS_FOREIGN_BIGENDIAN means that image pixels are most-significant byte
|
* @VIPS_FOREIGN_BIGENDIAN means that image pixels are most-significant byte
|
||||||
* first. Depending on the native byte order of the host machine, you may
|
* first. Depending on the native byte order of the host machine, you may
|
||||||
* need to swap bytes. See copy_swap().
|
* need to swap bytes. See copy_swap().
|
||||||
@ -124,7 +141,8 @@
|
|||||||
/**
|
/**
|
||||||
* VipsForeignLoad:
|
* VipsForeignLoad:
|
||||||
*
|
*
|
||||||
* @out must be set by @header(). @load(), if defined, must set @real.
|
* @header() must set at least the header fields of @out. @laod(), if defined,
|
||||||
|
* must load the pixels to @real.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -151,7 +169,15 @@ G_DEFINE_TYPE( VipsForeignLoadPng, vips_foreign_load_png,
|
|||||||
static VipsForeignFlags
|
static VipsForeignFlags
|
||||||
vips_foreign_load_png_get_flags_filename( const char *filename )
|
vips_foreign_load_png_get_flags_filename( const char *filename )
|
||||||
{
|
{
|
||||||
return( 0 );
|
VipsForeignFlags flags;
|
||||||
|
|
||||||
|
flags = 0;
|
||||||
|
if( vips__png_isinterlaced( filename ) )
|
||||||
|
flags = VIPS_FOREIGN_PARTIAL;
|
||||||
|
else
|
||||||
|
flags = VIPS_FOREIGN_SEQUENTIAL;
|
||||||
|
|
||||||
|
return( flags );
|
||||||
}
|
}
|
||||||
|
|
||||||
static VipsForeignFlags
|
static VipsForeignFlags
|
||||||
@ -644,13 +670,32 @@ vips_get_disc_threshold( void )
|
|||||||
if( vips__disc_threshold )
|
if( vips__disc_threshold )
|
||||||
threshold = vips__parse_size( vips__disc_threshold );
|
threshold = vips__parse_size( vips__disc_threshold );
|
||||||
|
|
||||||
VIPS_DEBUG_MSG( "vips_get_disc_threshold: "
|
#ifdef DEBUG
|
||||||
"%zd bytes\n", threshold );
|
printf( "vips_get_disc_threshold: %zd bytes\n", threshold );
|
||||||
|
#endif /*DEBUG*/
|
||||||
}
|
}
|
||||||
|
|
||||||
return( threshold );
|
return( threshold );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check two images for compatibility: their geometries need to match.
|
||||||
|
*/
|
||||||
|
static gboolean
|
||||||
|
vips_foreign_load_iscompat( VipsImage *a, VipsImage *b )
|
||||||
|
{
|
||||||
|
if( a->Xsize != b->Xsize ||
|
||||||
|
a->Ysize != b->Ysize ||
|
||||||
|
a->Bands != b->Bands ||
|
||||||
|
a->Coding != b->Coding ||
|
||||||
|
a->BandFmt != b->BandFmt ) {
|
||||||
|
vips_error( "VipsForeignLoad",
|
||||||
|
"%s", _( "images do not match" ) );
|
||||||
|
return( FALSE );
|
||||||
|
}
|
||||||
|
|
||||||
|
return( TRUE );
|
||||||
|
}
|
||||||
|
|
||||||
/* Our start function ... do the lazy open, if necessary, and return a region
|
/* Our start function ... do the lazy open, if necessary, and return a region
|
||||||
* on the new image.
|
* on the new image.
|
||||||
*/
|
*/
|
||||||
@ -658,7 +703,6 @@ static void *
|
|||||||
vips_foreign_load_start( VipsImage *out, void *a, void *dummy )
|
vips_foreign_load_start( VipsImage *out, void *a, void *dummy )
|
||||||
{
|
{
|
||||||
VipsForeignLoad *load = VIPS_FOREIGN_LOAD( a );
|
VipsForeignLoad *load = VIPS_FOREIGN_LOAD( a );
|
||||||
VipsObjectClass *object_class = VIPS_OBJECT_GET_CLASS( a );
|
|
||||||
VipsForeignLoadClass *class = VIPS_FOREIGN_LOAD_GET_CLASS( a );
|
VipsForeignLoadClass *class = VIPS_FOREIGN_LOAD_GET_CLASS( a );
|
||||||
|
|
||||||
if( !load->real ) {
|
if( !load->real ) {
|
||||||
@ -691,8 +735,7 @@ vips_foreign_load_start( VipsImage *out, void *a, void *dummy )
|
|||||||
printf( "vips_foreign_load_start: making 'p' temp\n" );
|
printf( "vips_foreign_load_start: making 'p' temp\n" );
|
||||||
#endif /*DEBUG*/
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
if( !(load->real = vips_image_new()) )
|
load->real = vips_image_new();
|
||||||
return( NULL );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
@ -712,16 +755,8 @@ vips_foreign_load_start( VipsImage *out, void *a, void *dummy )
|
|||||||
* Some versions of ImageMagick give different results between
|
* Some versions of ImageMagick give different results between
|
||||||
* Ping and Load for some formats, for example.
|
* Ping and Load for some formats, for example.
|
||||||
*/
|
*/
|
||||||
if( load->real->Xsize != out->Xsize ||
|
if( !vips_foreign_load_iscompat( load->real, out ) )
|
||||||
load->real->Ysize != out->Ysize ||
|
|
||||||
load->real->Bands != out->Bands ||
|
|
||||||
load->real->Coding != out->Coding ||
|
|
||||||
load->real->BandFmt != out->BandFmt ) {
|
|
||||||
vips_error( object_class->nickname,
|
|
||||||
"%s", _( "header() and load() report "
|
|
||||||
"different dimensions" ) );
|
|
||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return( vips_region_new( load->real ) );
|
return( vips_region_new( load->real ) );
|
||||||
@ -750,6 +785,73 @@ vips_foreign_load_generate( VipsRegion *or,
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
vips_foreign_load_seq_generate( VipsRegion *or,
|
||||||
|
void *seq, void *a, void *b, gboolean *stop )
|
||||||
|
{
|
||||||
|
VipsForeignLoad *load = (VipsForeignLoad *) b;
|
||||||
|
VipsRegion *ir = (VipsRegion *) seq;
|
||||||
|
VipsRect *r = &or->valid;
|
||||||
|
|
||||||
|
/* The y pos of the request must be the same as our current file
|
||||||
|
* position.
|
||||||
|
*/
|
||||||
|
if( r->top != load->y_pos ) {
|
||||||
|
vips_error( "VipsForeignLoad",
|
||||||
|
_( "non-sequential read --- "
|
||||||
|
"at position %d in file, but position %d requested" ),
|
||||||
|
load->y_pos, r->top );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're inside a tilecache where tiles are the fill image width, so
|
||||||
|
* this should always be true.
|
||||||
|
*/
|
||||||
|
g_assert( r->left == 0 );
|
||||||
|
g_assert( r->width == or->im->Xsize );
|
||||||
|
g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize );
|
||||||
|
|
||||||
|
/* Ask for input we need.
|
||||||
|
*/
|
||||||
|
if( vips_region_prepare( ir, r ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Attach output region to that.
|
||||||
|
*/
|
||||||
|
if( vips_region_region( or, ir, r, r->left, r->top ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
load->y_pos += r->height;
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Like vips_copy(), but check sequentiality of access.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
vips_foreign_load_seq( VipsForeignLoad *load, VipsImage *in, VipsImage **out )
|
||||||
|
{
|
||||||
|
VipsImage *seq;
|
||||||
|
|
||||||
|
seq = vips_image_new();
|
||||||
|
vips_demand_hint( seq, VIPS_DEMAND_STYLE_FATSTRIP,
|
||||||
|
in, NULL );
|
||||||
|
if( vips_image_pio_input( in ) ||
|
||||||
|
vips_image_copy_fields( seq, in ) ||
|
||||||
|
vips_image_generate( seq,
|
||||||
|
vips_start_one,
|
||||||
|
vips_foreign_load_seq_generate,
|
||||||
|
vips_stop_one,
|
||||||
|
in, load ) ) {
|
||||||
|
g_object_unref( seq );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = seq;
|
||||||
|
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
vips_foreign_load_build( VipsObject *object )
|
vips_foreign_load_build( VipsObject *object )
|
||||||
{
|
{
|
||||||
@ -765,6 +867,15 @@ vips_foreign_load_build( VipsObject *object )
|
|||||||
flags = 0;
|
flags = 0;
|
||||||
if( class->get_flags )
|
if( class->get_flags )
|
||||||
flags |= class->get_flags( load );
|
flags |= class->get_flags( load );
|
||||||
|
|
||||||
|
if( (flags & VIPS_FOREIGN_PARTIAL) &&
|
||||||
|
(flags & VIPS_FOREIGN_SEQUENTIAL) ) {
|
||||||
|
vips_warn( "VipsForeign", "%s",
|
||||||
|
_( "VIPS_FOREIGN_PARTIAL and VIPS_FOREIGN_SEQUENTIAL "
|
||||||
|
"both set -- using SEQUENTIAL" ) );
|
||||||
|
flags ^= VIPS_FOREIGN_PARTIAL;
|
||||||
|
}
|
||||||
|
|
||||||
g_object_set( load, "flags", flags, NULL );
|
g_object_set( load, "flags", flags, NULL );
|
||||||
|
|
||||||
if( VIPS_OBJECT_CLASS( vips_foreign_load_parent_class )->
|
if( VIPS_OBJECT_CLASS( vips_foreign_load_parent_class )->
|
||||||
@ -784,12 +895,102 @@ vips_foreign_load_build( VipsObject *object )
|
|||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
/* If there's no ->load() method then the header read has done
|
/* If there's no ->load() method then the header read has done
|
||||||
* everything. Otherwise, it's just set fields and we now must
|
* everything. Otherwise, it's just set fields and we must also
|
||||||
* convert pixels on demand.
|
* load pixels.
|
||||||
|
*
|
||||||
|
* Three modes:
|
||||||
|
*
|
||||||
|
* PARTIAL: just load and copy to @out. This is rather unlikely, most
|
||||||
|
* partial readers would just define a ->header() method I suppose.
|
||||||
|
*
|
||||||
|
* SEQUENTIAL: we need a tile cache plus a thing
|
||||||
|
* to check sequentiality.
|
||||||
|
*
|
||||||
|
* ELSE: it's a write-line thing. We delay the load until the
|
||||||
|
* first read, and allocate a memory/disk buffer as required.
|
||||||
*/
|
*/
|
||||||
if( class->load ) {
|
if( class->load &&
|
||||||
|
(load->flags & VIPS_FOREIGN_PARTIAL) ) {
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf( "vips_foreign_load_build: triggering ->load()\n" );
|
printf( "vips_foreign_load_build: partial read\n" );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
/* Read the image to @real.
|
||||||
|
*/
|
||||||
|
load->real = vips_image_new();
|
||||||
|
if( class->load( load ) ||
|
||||||
|
vips_image_pio_input( load->real ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Must match ->header().
|
||||||
|
*/
|
||||||
|
if( !vips_foreign_load_iscompat( load->real, load->out ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* ->header() should set the dhint. It'll default to the safe
|
||||||
|
* SMALLTILE if ->header() did not set it.
|
||||||
|
*/
|
||||||
|
vips_demand_hint( load->out, load->out->dhint,
|
||||||
|
load->real, NULL );
|
||||||
|
|
||||||
|
if( vips_image_generate( load->out,
|
||||||
|
vips_start_one,
|
||||||
|
vips_foreign_load_generate,
|
||||||
|
vips_stop_one,
|
||||||
|
load->real, load ) )
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
else if( class->load &&
|
||||||
|
(load->flags & VIPS_FOREIGN_SEQUENTIAL) ) {
|
||||||
|
VipsImage **t = (VipsImage **)
|
||||||
|
vips_object_local_array( VIPS_OBJECT( load ), 2 );
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "vips_foreign_load_build: sequential read\n" );
|
||||||
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
|
/* Load to @real.
|
||||||
|
*/
|
||||||
|
load->real = vips_image_new();
|
||||||
|
if( class->load( load ) ||
|
||||||
|
vips_image_pio_input( load->real ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Must match ->header().
|
||||||
|
*/
|
||||||
|
if( !vips_foreign_load_iscompat( load->real, load->out ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Copy to seq, checking sequentiality of accesses.
|
||||||
|
*/
|
||||||
|
if( vips_foreign_load_seq( load, load->real, &t[0] ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Copy again, with a cache. Enough tiles for two complete
|
||||||
|
* rows.
|
||||||
|
*/
|
||||||
|
if( vips_tilecache( t[0], &t[1],
|
||||||
|
"tile_width", load->real->Xsize,
|
||||||
|
"tile_height", VIPS__TILE_HEIGHT,
|
||||||
|
"max_tiles", 2,
|
||||||
|
NULL ) )
|
||||||
|
return( -1 );
|
||||||
|
|
||||||
|
/* Finally, copy to out.
|
||||||
|
*/
|
||||||
|
vips_demand_hint( load->out, load->out->dhint, t[1], NULL );
|
||||||
|
|
||||||
|
if( vips_image_pio_input( load->real ) ||
|
||||||
|
vips_image_generate( load->out,
|
||||||
|
vips_start_one,
|
||||||
|
vips_foreign_load_generate,
|
||||||
|
vips_stop_one,
|
||||||
|
t[1], load ) )
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
else if( class->load ) {
|
||||||
|
#ifdef DEBUG
|
||||||
|
printf( "vips_foreign_load_build: whole-image read\n" );
|
||||||
#endif /*DEBUG*/
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
/* ->header() should set the dhint. It'll default to the safe
|
/* ->header() should set the dhint. It'll default to the safe
|
||||||
@ -804,7 +1005,7 @@ vips_foreign_load_build( VipsObject *object )
|
|||||||
vips_foreign_load_start,
|
vips_foreign_load_start,
|
||||||
vips_foreign_load_generate,
|
vips_foreign_load_generate,
|
||||||
vips_stop_one,
|
vips_stop_one,
|
||||||
load, NULL ) )
|
NULL, load ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -846,6 +1047,13 @@ vips_foreign_load_class_init( VipsForeignLoadClass *class )
|
|||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
G_STRUCT_OFFSET( VipsForeignLoad, disc ),
|
G_STRUCT_OFFSET( VipsForeignLoad, disc ),
|
||||||
TRUE );
|
TRUE );
|
||||||
|
|
||||||
|
VIPS_ARG_BOOL( class, "sequential", 10,
|
||||||
|
_( "Sequential" ),
|
||||||
|
_( "Sequential read only" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsForeignLoad, sequential ),
|
||||||
|
FALSE );
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -58,10 +58,6 @@ typedef struct _VipsForeignLoadPng {
|
|||||||
*/
|
*/
|
||||||
char *filename;
|
char *filename;
|
||||||
|
|
||||||
/* Setting this means "I promise to only read sequentially from this
|
|
||||||
* image".
|
|
||||||
*/
|
|
||||||
gboolean sequential;
|
|
||||||
} VipsForeignLoadPng;
|
} VipsForeignLoadPng;
|
||||||
|
|
||||||
typedef VipsForeignLoadClass VipsForeignLoadPngClass;
|
typedef VipsForeignLoadClass VipsForeignLoadPngClass;
|
||||||
@ -72,24 +68,23 @@ G_DEFINE_TYPE( VipsForeignLoadPng, vips_foreign_load_png,
|
|||||||
static VipsForeignFlags
|
static VipsForeignFlags
|
||||||
vips_foreign_load_png_get_flags_filename( const char *filename )
|
vips_foreign_load_png_get_flags_filename( const char *filename )
|
||||||
{
|
{
|
||||||
/* We can't tell just from the file. Always refuse.
|
VipsForeignFlags flags;
|
||||||
*/
|
|
||||||
return( 0 );
|
flags = 0;
|
||||||
|
if( vips__png_isinterlaced( filename ) )
|
||||||
|
flags = VIPS_FOREIGN_PARTIAL;
|
||||||
|
else
|
||||||
|
flags = VIPS_FOREIGN_SEQUENTIAL;
|
||||||
|
|
||||||
|
return( flags );
|
||||||
}
|
}
|
||||||
|
|
||||||
static VipsForeignFlags
|
static VipsForeignFlags
|
||||||
vips_foreign_load_png_get_flags( VipsForeignLoad *load )
|
vips_foreign_load_png_get_flags( VipsForeignLoad *load )
|
||||||
{
|
{
|
||||||
VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
|
VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
|
||||||
VipsForeignFlags flags;
|
|
||||||
|
|
||||||
flags = 0;
|
return( vips_foreign_load_png_get_flags_filename( png->filename ) );
|
||||||
/* If sequential is set, try to read partially.
|
|
||||||
*/
|
|
||||||
if( png->sequential )
|
|
||||||
flags |= VIPS_FOREIGN_PARTIAL;
|
|
||||||
|
|
||||||
return( flags );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -108,7 +103,7 @@ vips_foreign_load_png_load( VipsForeignLoad *load )
|
|||||||
{
|
{
|
||||||
VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
|
VipsForeignLoadPng *png = (VipsForeignLoadPng *) load;
|
||||||
|
|
||||||
if( vips__png_read( png->filename, load->real, png->sequential ) )
|
if( vips__png_read( png->filename, load->real ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
@ -143,13 +138,6 @@ vips_foreign_load_png_class_init( VipsForeignLoadPngClass *class )
|
|||||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||||
G_STRUCT_OFFSET( VipsForeignLoadPng, filename ),
|
G_STRUCT_OFFSET( VipsForeignLoadPng, filename ),
|
||||||
NULL );
|
NULL );
|
||||||
|
|
||||||
VIPS_ARG_BOOL( class, "sequential", 10,
|
|
||||||
_( "Sequential" ),
|
|
||||||
_( "Sequential read only" ),
|
|
||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
|
||||||
G_STRUCT_OFFSET( VipsForeignLoadPng, sequential ),
|
|
||||||
FALSE );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -113,11 +113,6 @@ typedef struct {
|
|||||||
png_infop pInfo;
|
png_infop pInfo;
|
||||||
png_bytep *row_pointer;
|
png_bytep *row_pointer;
|
||||||
png_bytep data;
|
png_bytep data;
|
||||||
|
|
||||||
/* During sequential read we keep track of the current y position in
|
|
||||||
* the file to check sequentiality.
|
|
||||||
*/
|
|
||||||
int y_pos;
|
|
||||||
} Read;
|
} Read;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -145,7 +140,6 @@ read_new( const char *name, VipsImage *out )
|
|||||||
read->pInfo = NULL;
|
read->pInfo = NULL;
|
||||||
read->row_pointer = NULL;
|
read->row_pointer = NULL;
|
||||||
read->data = NULL;
|
read->data = NULL;
|
||||||
read->y_pos = 0;
|
|
||||||
|
|
||||||
g_signal_connect( out, "close",
|
g_signal_connect( out, "close",
|
||||||
G_CALLBACK( read_destroy ), read );
|
G_CALLBACK( read_destroy ), read );
|
||||||
@ -342,52 +336,35 @@ png2vips_interlace( Read *read, VipsImage *out )
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
png2vips_generate( VipsRegion *out,
|
png2vips_generate( VipsRegion *or,
|
||||||
void *seq, void *a, void *b, gboolean *stop )
|
void *seq, void *a, void *b, gboolean *stop )
|
||||||
{
|
{
|
||||||
|
VipsRect *r = &or->valid;
|
||||||
Read *read = (Read *) a;
|
Read *read = (Read *) a;
|
||||||
|
|
||||||
int y;
|
int y;
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
printf( "png2vips_generate: line %d, %d rows\n",
|
printf( "png2vips_generate: line %d, %d rows\n",
|
||||||
out->valid.top, out->valid.height );
|
r->top, r->height );
|
||||||
printf( "png2vips_generate: thread %p\n", g_thread_self() );
|
|
||||||
#endif /*DEBUG*/
|
#endif /*DEBUG*/
|
||||||
|
|
||||||
/* The y pos of the request must be the same as our current file
|
|
||||||
* position.
|
|
||||||
*/
|
|
||||||
if( out->valid.top != read->y_pos ) {
|
|
||||||
vips_error( "png2vips", _( "non-sequential read --- "
|
|
||||||
"at position %d in file, but position %d requested" ),
|
|
||||||
read->y_pos, out->valid.top );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
if( setjmp( png_jmpbuf( read->pPng ) ) )
|
if( setjmp( png_jmpbuf( read->pPng ) ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
|
||||||
/* We're inside a tilecache where tiles are the fill image width, so
|
/* We're inside a tilecache where tiles are the fill image width, so
|
||||||
* this should always be true.
|
* this should always be true.
|
||||||
*/
|
*/
|
||||||
g_assert( out->valid.left == 0 );
|
g_assert( r->left == 0 );
|
||||||
g_assert( out->valid.width == out->im->Xsize );
|
g_assert( r->width == or->im->Xsize );
|
||||||
g_assert( VIPS_RECT_BOTTOM( &out->valid ) <= out->im->Ysize );
|
g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize );
|
||||||
|
|
||||||
for( y = 0; y < out->valid.height; y++ ) {
|
for( y = 0; y < r->height; y++ ) {
|
||||||
png_bytep q = (png_bytep)
|
png_bytep q = (png_bytep) VIPS_REGION_ADDR( or, 0, r->top + y );
|
||||||
VIPS_REGION_ADDR( out, 0, out->valid.top + y );
|
|
||||||
|
|
||||||
png_read_row( read->pPng, q, NULL );
|
png_read_row( read->pPng, q, NULL );
|
||||||
}
|
}
|
||||||
|
|
||||||
read->y_pos += out->valid.height;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
printf( "png2vips_generate: done for thread %p\n", g_thread_self() );
|
|
||||||
#endif /*DEBUG*/
|
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,7 +373,7 @@ png2vips_generate( VipsRegion *out,
|
|||||||
*
|
*
|
||||||
* We are behind a tile cache, so we can assume that vips_image_generate()
|
* We are behind a tile cache, so we can assume that vips_image_generate()
|
||||||
* will always be asked for tiles which are the full image width. We can also
|
* will always be asked for tiles which are the full image width. We can also
|
||||||
* assume we are single-threaded.
|
* assume we are single-threaded. And that we will be called sequentially.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
png2vips_sequential( Read *read, VipsImage *out )
|
png2vips_sequential( Read *read, VipsImage *out )
|
||||||
@ -409,34 +386,29 @@ png2vips_sequential( Read *read, VipsImage *out )
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Noninterlaced images can be read without needing enough RAM for the whole
|
/* Interlaced PNGs need to be entirely decompressed into memory then can be
|
||||||
* image.
|
* served partially from there. Non-interlaced PNGs may be read sequentially.
|
||||||
*/
|
*/
|
||||||
static int
|
gboolean
|
||||||
png2vips_noninterlace( Read *read, VipsImage *out )
|
vips__png_isinterlaced( const char *filename )
|
||||||
{
|
{
|
||||||
const int rowbytes = VIPS_IMAGE_SIZEOF_LINE( out );
|
VipsImage *image;
|
||||||
const int height = png_get_image_height( read->pPng, read->pInfo );
|
Read *read;
|
||||||
|
gboolean interlaced;
|
||||||
|
|
||||||
int y;
|
image = vips_image_new();
|
||||||
|
if( !(read = read_new( filename, image )) ) {
|
||||||
if( !(read->data = (png_bytep) vips_malloc( NULL, rowbytes )) )
|
g_object_unref( image );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
if( 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( out, y, read->data ) )
|
|
||||||
return( -1 );
|
|
||||||
}
|
}
|
||||||
|
interlaced = png_set_interlace_handling( read->pPng ) > 1;
|
||||||
|
g_object_unref( image );
|
||||||
|
|
||||||
return( 0 );
|
return( interlaced );
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
vips__png_read( const char *name, VipsImage *out, int sequential )
|
vips__png_read( const char *name, VipsImage *out )
|
||||||
{
|
{
|
||||||
Read *read;
|
Read *read;
|
||||||
|
|
||||||
@ -452,38 +424,9 @@ vips__png_read( const char *name, VipsImage *out, int sequential )
|
|||||||
png2vips_interlace( read, out ) )
|
png2vips_interlace( read, out ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
else if( sequential ) {
|
|
||||||
VipsImage *raw;
|
|
||||||
VipsImage *t;
|
|
||||||
|
|
||||||
/* Read to this image, then cache to out.
|
|
||||||
*/
|
|
||||||
raw = vips_image_new();
|
|
||||||
vips_object_local( out, raw );
|
|
||||||
|
|
||||||
if( png2vips_header( read, raw ) ||
|
|
||||||
png2vips_sequential( read, raw ) )
|
|
||||||
return( -1 );
|
|
||||||
|
|
||||||
/* Copy to out, adding a cache. Enough tiles for two complete
|
|
||||||
* rows.
|
|
||||||
*/
|
|
||||||
if( vips_tilecache( raw, &t,
|
|
||||||
"tile_width", raw->Xsize,
|
|
||||||
"tile_height", VIPS__TILE_HEIGHT,
|
|
||||||
"max_tiles", 2,
|
|
||||||
NULL ) )
|
|
||||||
return( -1 );
|
|
||||||
if( vips_image_write( t, out ) ) {
|
|
||||||
g_object_unref( t );
|
|
||||||
return( -1 );
|
|
||||||
}
|
|
||||||
g_object_unref( t );
|
|
||||||
|
|
||||||
}
|
|
||||||
else {
|
else {
|
||||||
if( png2vips_header( read, out ) ||
|
if( png2vips_header( read, out ) ||
|
||||||
png2vips_noninterlace( read, out ) )
|
png2vips_sequential( read, out ) )
|
||||||
return( -1 );
|
return( -1 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,8 +35,9 @@ extern "C" {
|
|||||||
#endif /*__cplusplus*/
|
#endif /*__cplusplus*/
|
||||||
|
|
||||||
int vips__png_header( const char *name, VipsImage *out );
|
int vips__png_header( const char *name, VipsImage *out );
|
||||||
int vips__png_read( const char *name, VipsImage *out, int sequential );
|
int vips__png_read( const char *name, VipsImage *out );
|
||||||
int vips__png_ispng( const char *filename );
|
int vips__png_ispng( const char *filename );
|
||||||
|
gboolean vips__png_isinterlaced( const char *filename );
|
||||||
extern const char *vips__png_suffs[];
|
extern const char *vips__png_suffs[];
|
||||||
|
|
||||||
int vips__png_write( VipsImage *in, const char *filename,
|
int vips__png_write( VipsImage *in, const char *filename,
|
||||||
|
@ -89,8 +89,9 @@ void *vips_foreign_map( const char *base,
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
VIPS_FOREIGN_NONE = 0, /* No flags set */
|
VIPS_FOREIGN_NONE = 0, /* No flags set */
|
||||||
VIPS_FOREIGN_PARTIAL = 1, /* Lazy read OK (eg. tiled tiff) */
|
VIPS_FOREIGN_PARTIAL = 1, /* Lazy read OK (eg. tiled tiff) */
|
||||||
VIPS_FOREIGN_BIGENDIAN = 2, /* Most-significant byte first */
|
VIPS_FOREIGN_SEQUENTIAL = 2, /* Top-to-bottom lazy read OK */
|
||||||
VIPS_FOREIGN_ALL = 3 /* All flags set */
|
VIPS_FOREIGN_BIGENDIAN = 3, /* Most-significant byte first */
|
||||||
|
VIPS_FOREIGN_ALL = 4 /* All flags set */
|
||||||
} VipsForeignFlags;
|
} VipsForeignFlags;
|
||||||
|
|
||||||
#define VIPS_TYPE_FOREIGN_LOAD (vips_foreign_load_get_type())
|
#define VIPS_TYPE_FOREIGN_LOAD (vips_foreign_load_get_type())
|
||||||
@ -116,10 +117,20 @@ typedef struct _VipsForeignLoad {
|
|||||||
*/
|
*/
|
||||||
gboolean disc;
|
gboolean disc;
|
||||||
|
|
||||||
/* Flags read from the foreign.
|
/* Setting this means "I promise to only read sequentially from this
|
||||||
|
* image".
|
||||||
|
*/
|
||||||
|
gboolean sequential;
|
||||||
|
|
||||||
|
/* Flags for this load operation.
|
||||||
*/
|
*/
|
||||||
VipsForeignFlags flags;
|
VipsForeignFlags flags;
|
||||||
|
|
||||||
|
/* In sequential mode we need to track the y position so we can
|
||||||
|
* ensure top-to-bottom-ness.
|
||||||
|
*/
|
||||||
|
int y_pos;
|
||||||
|
|
||||||
/*< public >*/
|
/*< public >*/
|
||||||
|
|
||||||
/* The image we generate. This must be set by ->header().
|
/* The image we generate. This must be set by ->header().
|
||||||
@ -130,7 +141,6 @@ typedef struct _VipsForeignLoad {
|
|||||||
* disc foreign or a memory buffer. This must be set by ->load().
|
* disc foreign or a memory buffer. This must be set by ->load().
|
||||||
*/
|
*/
|
||||||
VipsImage *real;
|
VipsImage *real;
|
||||||
|
|
||||||
} VipsForeignLoad;
|
} VipsForeignLoad;
|
||||||
|
|
||||||
typedef struct _VipsForeignLoadClass {
|
typedef struct _VipsForeignLoadClass {
|
||||||
@ -139,9 +149,8 @@ typedef struct _VipsForeignLoadClass {
|
|||||||
|
|
||||||
/* Is a file in this format.
|
/* Is a file in this format.
|
||||||
*
|
*
|
||||||
* This function should return %TRUE if
|
* This function should return %TRUE if the file contains an image of
|
||||||
* the file contains an image of this type. If you don't define this
|
* this type. If you don't define this function, #VipsForeignLoad
|
||||||
* function, #VipsForeignLoad
|
|
||||||
* will use @suffs instead.
|
* will use @suffs instead.
|
||||||
*/
|
*/
|
||||||
gboolean (*is_a)( const char * );
|
gboolean (*is_a)( const char * );
|
||||||
@ -156,13 +165,15 @@ typedef struct _VipsForeignLoadClass {
|
|||||||
*/
|
*/
|
||||||
VipsForeignFlags (*get_flags_filename)( const char * );
|
VipsForeignFlags (*get_flags_filename)( const char * );
|
||||||
|
|
||||||
/* Get the flags for this image. Images can be loaded from (for
|
/* Get the flags for this load operation. Images can be loaded from
|
||||||
* example) memory areas rather than files, so you can't just use
|
* (for example) memory areas rather than files, so you can't just use
|
||||||
* @get_flags_filename().
|
* @get_flags_filename().
|
||||||
*/
|
*/
|
||||||
VipsForeignFlags (*get_flags)( VipsForeignLoad * );
|
VipsForeignFlags (*get_flags)( VipsForeignLoad * );
|
||||||
|
|
||||||
/* Set the header fields in @out from @filename. If you can read the
|
/* Do the minimum read we can.
|
||||||
|
*
|
||||||
|
* Set the header fields in @out from @filename. If you can read the
|
||||||
* whole image as well with no performance cost (as with vipsload),
|
* whole image as well with no performance cost (as with vipsload),
|
||||||
* or if your loader does not support reading only the header, read
|
* or if your loader does not support reading only the header, read
|
||||||
* the entire image in this method and leave @load() NULL.
|
* the entire image in this method and leave @load() NULL.
|
||||||
@ -175,7 +186,8 @@ typedef struct _VipsForeignLoadClass {
|
|||||||
*/
|
*/
|
||||||
int (*header)( VipsForeignLoad * );
|
int (*header)( VipsForeignLoad * );
|
||||||
|
|
||||||
/* Read the whole image into @real. It gets copied to @out later.
|
/* Read the whole image into @real. The pixels will get copied to @out
|
||||||
|
* later.
|
||||||
*
|
*
|
||||||
* You can omit this method if you define a @header() method which
|
* You can omit this method if you define a @header() method which
|
||||||
* loads the whole file.
|
* loads the whole file.
|
||||||
|
@ -14,6 +14,7 @@ vips_foreign_flags_get_type( void )
|
|||||||
static const GEnumValue values[] = {
|
static const GEnumValue values[] = {
|
||||||
{VIPS_FOREIGN_NONE, "VIPS_FOREIGN_NONE", "none"},
|
{VIPS_FOREIGN_NONE, "VIPS_FOREIGN_NONE", "none"},
|
||||||
{VIPS_FOREIGN_PARTIAL, "VIPS_FOREIGN_PARTIAL", "partial"},
|
{VIPS_FOREIGN_PARTIAL, "VIPS_FOREIGN_PARTIAL", "partial"},
|
||||||
|
{VIPS_FOREIGN_SEQUENTIAL, "VIPS_FOREIGN_SEQUENTIAL", "sequential"},
|
||||||
{VIPS_FOREIGN_BIGENDIAN, "VIPS_FOREIGN_BIGENDIAN", "bigendian"},
|
{VIPS_FOREIGN_BIGENDIAN, "VIPS_FOREIGN_BIGENDIAN", "bigendian"},
|
||||||
{VIPS_FOREIGN_ALL, "VIPS_FOREIGN_ALL", "all"},
|
{VIPS_FOREIGN_ALL, "VIPS_FOREIGN_ALL", "all"},
|
||||||
{0, NULL, NULL}
|
{0, NULL, NULL}
|
||||||
|
@ -67,9 +67,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
#define DEBUG_FATAL
|
|
||||||
#define DEBUG
|
#define DEBUG
|
||||||
*/
|
*/
|
||||||
|
#define DEBUG_FATAL
|
||||||
|
|
||||||
/* Need to disable these sometimes.
|
/* Need to disable these sometimes.
|
||||||
#undef DEBUG_FATAL
|
#undef DEBUG_FATAL
|
||||||
@ -1010,6 +1010,7 @@ main( int argc, char **argv )
|
|||||||
G_LOG_LEVEL_ERROR |
|
G_LOG_LEVEL_ERROR |
|
||||||
G_LOG_LEVEL_CRITICAL |
|
G_LOG_LEVEL_CRITICAL |
|
||||||
G_LOG_LEVEL_WARNING );
|
G_LOG_LEVEL_WARNING );
|
||||||
|
fprintf( stderr, "** DEBUG_FATAL\n" );
|
||||||
#endif /*!DEBUG_FATAL*/
|
#endif /*!DEBUG_FATAL*/
|
||||||
|
|
||||||
/* Try to find our action.
|
/* Try to find our action.
|
||||||
|
Loading…
Reference in New Issue
Block a user