break vips_sequential() out
This commit is contained in:
parent
13a6c02c5c
commit
7f94fb3597
@ -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
2
TODO
@ -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
|
||||
|
@ -4,6 +4,7 @@ libconversion_la_SOURCES = \
|
||||
conversion.c \
|
||||
conversion.h \
|
||||
tilecache.c \
|
||||
sequential.c \
|
||||
cache.c \
|
||||
copy.c \
|
||||
embed.c \
|
||||
|
@ -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:
|
||||
*
|
||||
|
186
libvips/conversion/sequential.c
Normal file
186
libvips/conversion/sequential.c
Normal 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 );
|
||||
}
|
@ -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" );
|
||||
|
@ -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 );
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user