break vips_sequential() out

This commit is contained in:
John Cupitt 2012-02-15 15:47:43 +00:00
parent 13a6c02c5c
commit 7f94fb3597
8 changed files with 259 additions and 188 deletions

View File

@ -5,6 +5,7 @@
- support operations with many returns in Python
- sequential read mode
- better im_shrink()
- added vips_sequential()
20/8/11 started 7.27.0
- version bump for new dev cycle

2
TODO
View File

@ -2,6 +2,8 @@
tiff, jpg are the obvious ones
test progressive png, non-sequential png load
- argh
(nip2:11576): GLib-GObject-WARNING **: value "-0.000000" of type

View File

@ -4,6 +4,7 @@ libconversion_la_SOURCES = \
conversion.c \
conversion.h \
tilecache.c \
sequential.c \
cache.c \
copy.c \
embed.c \

View File

@ -105,6 +105,7 @@ vips_conversion_operation_init( void )
{
extern GType vips_copy_get_type( void );
extern GType vips_tile_cache_get_type( void );
extern GType vips_sequential_get_type( void );
extern GType vips_cache_get_type( void );
extern GType vips_embed_get_type( void );
extern GType vips_flip_get_type( void );
@ -123,6 +124,7 @@ vips_conversion_operation_init( void )
vips_copy_get_type();
vips_tile_cache_get_type();
vips_sequential_get_type();
vips_cache_get_type();
vips_embed_get_type();
vips_flip_get_type();
@ -140,7 +142,6 @@ vips_conversion_operation_init( void )
vips_bandmean_get_type();
}
/* The common part of most binary conversion
* operators. We:
*

View File

@ -0,0 +1,186 @@
/* Like copy, but ensure sequential access.
*
* Handy with sequential for loading files formats which are strictly
* top-to-bottom, like PNG.
*
* 15/2/12
* - from VipsForeignLoad
*/
/*
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 cache 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 VIPS_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/internal.h>
#include <vips/debug.h>
#include "conversion.h"
typedef struct _VipsSequential {
VipsConversion parent_instance;
VipsImage *in;
int y_pos;
} VipsSequential;
typedef VipsConversionClass VipsSequentialClass;
G_DEFINE_TYPE( VipsSequential, vips_sequential, VIPS_TYPE_CONVERSION );
static int
vips_sequential_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
VipsSequential *sequential = (VipsSequential *) b;
VipsRect *r = &or->valid;
VipsRegion *ir = (VipsRegion *) seq;
/* The y pos of the request must be the same as our current file
* position.
*/
if( r->top != sequential->y_pos ) {
vips_error( "VipsSequential",
_( "non-sequential read --- "
"at position %d in file, but position %d requested" ),
sequential->y_pos, r->top );
return( -1 );
}
/* We're inside a tilecache where tiles are the full 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 );
/* Pointer copy.
*/
if( vips_region_prepare( ir, r ) ||
vips_region_region( or, ir, r, r->left, r->top ) )
return( -1 );
sequential->y_pos += r->height;
return( 0 );
}
static int
vips_sequential_build( VipsObject *object )
{
VipsConversion *conversion = VIPS_CONVERSION( object );
VipsSequential *sequential = (VipsSequential *) object;
VIPS_DEBUG_MSG( "vips_sequential_build\n" );
if( VIPS_OBJECT_CLASS( vips_sequential_parent_class )->build( object ) )
return( -1 );
if( vips_image_pio_input( sequential->in ) )
return( -1 );
if( vips_image_copy_fields( conversion->out, sequential->in ) )
return( -1 );
vips_demand_hint( conversion->out,
VIPS_DEMAND_STYLE_FATSTRIP, sequential->in, NULL );
if( vips_image_generate( conversion->out,
vips_start_one, vips_sequential_generate, vips_stop_one,
sequential->in, sequential ) )
return( -1 );
return( 0 );
}
static void
vips_sequential_class_init( VipsSequentialClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
VIPS_DEBUG_MSG( "vips_sequential_class_init\n" );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
vobject_class->nickname = "sequential";
vobject_class->description = _( "check sequential access" );
vobject_class->build = vips_sequential_build;
VIPS_ARG_IMAGE( class, "in", 1,
_( "Input" ),
_( "Input image" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsSequential, in ) );
}
static void
vips_sequential_init( VipsSequential *sequential )
{
}
/**
* vips_sequential:
* @in: input image
* @out: output image
* @...: %NULL-terminated list of optional named arguments
*
* This operation behaves rather like vips_copy() between images
* @in and @out, except that it checks that pixels are only requested
* top-to-bottom. If an out of order request is made, it throws an exception.
*
* This operation is handy with tilecache for loading file formats which are
* strictly top-to-bottom, like PNG.
*
* See also: vips_image_cache().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_sequential( VipsImage *in, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_call_split( "sequential", ap, in, out );
va_end( ap );
return( result );
}

View File

@ -678,6 +678,43 @@ vips_get_disc_threshold( void )
return( threshold );
}
static VipsImage *
vips_foreign_load_temp( VipsForeignLoad *load )
{
const guint64 disc_threshold = vips_get_disc_threshold();
const guint64 image_size = VIPS_IMAGE_SIZEOF_IMAGE( load->out );
VipsImage *temp;
/* We open via disc if:
* - 'disc' is set
* - disc-threshold has not been set to zero
* - the uncompressed image will be larger than
* vips_get_disc_threshold()
*/
if( load->disc &&
disc_threshold &&
image_size > disc_threshold ) {
#ifdef DEBUG
printf( "vips_foreign_load_temp: making disc temp\n" );
#endif /*DEBUG*/
if( !(temp = vips_image_new_disc_temp( "%s.v" )) )
return( NULL );
}
else {
#ifdef DEBUG
printf( "vips_foreign_load_start: making 'p' temp\n" );
#endif /*DEBUG*/
/* Otherwise, fall back to a "p".
*/
temp = vips_image_new();
}
return( temp );
}
/* Check two images for compatibility: their geometries need to match.
*/
static gboolean
@ -700,43 +737,14 @@ vips_foreign_load_iscompat( VipsImage *a, VipsImage *b )
* on the new image.
*/
static void *
vips_foreign_load_start( VipsImage *out, void *a, void *dummy )
vips_foreign_load_start( VipsImage *out, void *a, void *b )
{
VipsForeignLoad *load = VIPS_FOREIGN_LOAD( a );
VipsForeignLoadClass *class = VIPS_FOREIGN_LOAD_GET_CLASS( a );
VipsForeignLoad *load = VIPS_FOREIGN_LOAD( b );
VipsForeignLoadClass *class = VIPS_FOREIGN_LOAD_GET_CLASS( load );
if( !load->real ) {
const guint64 disc_threshold = vips_get_disc_threshold();
const guint64 image_size = VIPS_IMAGE_SIZEOF_IMAGE( load->out );
/* We open via disc if:
* - 'disc' is set
* - disc-threshold has not been set to zero
* - the format does not support lazy read
* - the uncompressed image will be larger than
* vips_get_disc_threshold()
*/
if( load->disc &&
disc_threshold &&
!(load->flags & VIPS_FOREIGN_PARTIAL) &&
image_size > disc_threshold ) {
#ifdef DEBUG
printf( "vips_foreign_load_start: making disc temp\n" );
#endif /*DEBUG*/
if( !(load->real = vips_image_new_disc_temp( "%s.v" )) )
return( NULL );
}
/* Otherwise, fall back to a "p".
*/
if( !load->real ) {
#ifdef DEBUG
printf( "vips_foreign_load_start: making 'p' temp\n" );
#endif /*DEBUG*/
load->real = vips_image_new();
}
if( !(load->real = vips_foreign_load_temp( load )) )
return( NULL );
#ifdef DEBUG
printf( "vips_foreign_load_start: triggering ->load()\n" );
@ -785,73 +793,6 @@ vips_foreign_load_generate( VipsRegion *or,
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
vips_foreign_load_build( VipsObject *object )
{
@ -898,19 +839,16 @@ vips_foreign_load_build( VipsObject *object )
* everything. Otherwise, it's just set fields and we must also
* load pixels.
*
* Three modes:
* If it's a partial loader, or if it's a sequiential loader and
* sequential load has been requested, we can load here.
*
* 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.
* Otherwise, it's a whole-image read and we delay until first pixel
* access.
*/
if( class->load &&
(load->flags & VIPS_FOREIGN_PARTIAL) ) {
((load->flags & VIPS_FOREIGN_PARTIAL) ||
((load->flags & VIPS_FOREIGN_SEQUENTIAL) &&
load->sequential)) ) {
#ifdef DEBUG
printf( "vips_foreign_load_build: partial read\n" );
#endif /*DEBUG*/
@ -940,54 +878,6 @@ vips_foreign_load_build( VipsObject *object )
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" );

View File

@ -65,8 +65,8 @@
*/
/*
#define DEBUG
*/
#define DEBUG
#ifdef HAVE_CONFIG_H
#include <config.h>
@ -352,13 +352,6 @@ png2vips_generate( VipsRegion *or,
if( setjmp( png_jmpbuf( read->pPng ) ) )
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 );
for( y = 0; y < r->height; y++ ) {
png_bytep q = (png_bytep) VIPS_REGION_ADDR( or, 0, r->top + y );
@ -368,24 +361,6 @@ png2vips_generate( VipsRegion *or,
return( 0 );
}
/* Build with vips_image_generate(), but fail if out-of-order tiles are
* requested.
*
* 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
* assume we are single-threaded. And that we will be called sequentially.
*/
static int
png2vips_sequential( Read *read, VipsImage *out )
{
if( vips_image_generate( out,
NULL, png2vips_generate, NULL,
read, NULL ) )
return( -1 );
return( 0 );
}
/* Interlaced PNGs need to be entirely decompressed into memory then can be
* served partially from there. Non-interlaced PNGs may be read sequentially.
*/
@ -425,8 +400,21 @@ vips__png_read( const char *name, VipsImage *out )
return( -1 );
}
else {
if( png2vips_header( read, out ) ||
png2vips_sequential( read, out ) )
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 3 );
t[0] = vips_image_new();
if( png2vips_header( read, t[0] ) ||
vips_image_generate( t[0],
NULL, png2vips_generate, NULL,
read, NULL ) ||
vips_sequential( t[0], &t[1], NULL ) ||
vips_tilecache( t[1], &t[2],
"tile_width", t[0]->Xsize,
"tile_height", VIPS__TILE_HEIGHT,
"max_tiles", 2,
NULL ) ||
vips_image_write( t[2], out ) )
return( -1 );
}

View File

@ -139,6 +139,8 @@ int vips_copy( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));
int vips_tilecache( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));
int vips_sequential( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));
int vips_cache( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));