libvips/libvips/deprecated/lazy.c

342 lines
7.7 KiB
C

/* lazy open/save ... compat funcs for old im_open() behaviour
*
* 30/11/11
* - cut from old image.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., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301 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 <glib/gi18n-lib.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <vips/vips.h>
#include <vips/vips7compat.h>
#include <vips/debug.h>
#include <vips/internal.h>
static gboolean
vips_format_is_vips( VipsFormatClass *format )
{
return( strcmp( VIPS_OBJECT_CLASS( format )->nickname, "vips" ) == 0 );
}
/* Lazy open.
*/
/* What we track during a delayed open.
*/
typedef struct {
VipsImage *image;
VipsFormatClass *format;/* Read in pixels with this */
char *filename; /* Get pixels from here */
gboolean sequential; /* Sequential read requested */
VipsImage *real; /* The real decompressed image */
} Lazy;
static void
lazy_free_cb( VipsImage *image, Lazy *lazy )
{
VIPS_DEBUG_MSG( "lazy_free: %p \"%s\"\n", lazy, lazy->filename );
g_free( lazy->filename );
VIPS_UNREF( lazy->real );
g_free( lazy );
}
static Lazy *
lazy_new( VipsImage *image,
VipsFormatClass *format, const char *filename, gboolean sequential )
{
Lazy *lazy;
lazy = g_new( Lazy, 1 );
VIPS_DEBUG_MSG( "lazy_new: %p \"%s\"\n", lazy, filename );
lazy->image = image;
lazy->format = format;
lazy->filename = g_strdup( filename );
lazy->sequential = sequential;
lazy->real = NULL;
g_signal_connect( image, "close", G_CALLBACK( lazy_free_cb ), lazy );
return( lazy );
}
static size_t
disc_threshold( void )
{
static gboolean done = FALSE;
static size_t threshold;
if( !done ) {
const char *env;
done = TRUE;
/* 100mb default.
*/
threshold = 100 * 1024 * 1024;
if( (env = g_getenv( "IM_DISC_THRESHOLD" )) )
threshold = vips__parse_size( env );
if( vips__disc_threshold )
threshold = vips__parse_size( vips__disc_threshold );
VIPS_DEBUG_MSG( "disc_threshold: %zd bytes\n", threshold );
}
return( threshold );
}
/* Make the real underlying image: either a direct disc file, or a temp file
* somewhere.
*/
static VipsImage *
lazy_real_image( Lazy *lazy )
{
VipsImage *real;
/* We open via disc if:
* - 'sequential' is not set
* - disc_threshold() has not been set to zero
* - the format does not support lazy read
* - the uncompressed image will be larger than disc_threshold()
*/
real = NULL;
if( !lazy->sequential &&
disc_threshold() &&
!(vips_format_get_flags( lazy->format, lazy->filename ) &
VIPS_FORMAT_PARTIAL) &&
VIPS_IMAGE_SIZEOF_IMAGE( lazy->image ) > disc_threshold() )
if( !(real = vips_image_new_temp_file( "%s.v" )) )
return( NULL );
/* Otherwise, fall back to a "p".
*/
if( !real &&
!(real = vips_image_new()) )
return( NULL );
return( real );
}
/* Our start function ... do the lazy open, if necessary, and return a region
* on the new image.
*/
static void *
open_lazy_start( VipsImage *out, void *a, void *dummy )
{
Lazy *lazy = (Lazy *) a;
if( !lazy->real ) {
if( !(lazy->real = lazy_real_image( lazy )) ||
lazy->format->load( lazy->filename, lazy->real ) ||
vips_image_pio_input( lazy->real ) ) {
VIPS_UNREF( lazy->real );
return( NULL );
}
}
return( vips_region_new( lazy->real ) );
}
/* Just copy.
*/
static int
open_lazy_generate( VipsRegion *or,
void *seq, void *a, void *b, gboolean *stop )
{
VipsRegion *ir = (VipsRegion *) seq;
VipsRect *r = &or->valid;
/* 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 );
return( 0 );
}
/* Lazy open ... init the header with the first OpenLazyFn, delay actually
* decoding pixels with the second OpenLazyFn until the first generate().
*/
static int
vips_image_open_lazy( VipsImage *image,
VipsFormatClass *format, const char *filename, gboolean sequential )
{
Lazy *lazy;
lazy = lazy_new( image, format, filename, sequential );
/* Is there a ->header() function? We need to do a lazy load.
*/
if( format->header ) {
/* Read header fields to init the return image.
*/
if( format->header( filename, image ) )
return( -1 );
/* Then 'start' creates the real image and 'gen' paints 'image'
* with pixels from the real image on demand.
*/
if( vips_image_pipelinev( image, image->dhint, NULL ) ||
vips_image_generate( image,
open_lazy_start, open_lazy_generate,
vips_stop_one, lazy, NULL ) )
return( -1 );
}
else if( format->load ) {
if( format->load( filename, image ) )
return( -1 );
}
else
g_assert( 0 );
return( 0 );
}
/* Lazy save.
*/
/* If we write to (eg.) TIFF, actually do the write
* to a "p" and on "written" do im_vips2tiff() or whatever. Track save
* parameters here.
*/
typedef int (*VipsSaveFn)( VipsImage *image, const char *filename );
typedef struct {
VipsSaveFn save_fn; /* Save function */
char *filename; /* Save args */
} SaveBlock;
/* From "written" callback: invoke a delayed save.
*/
static void
vips_image_save_cb( VipsImage *image, int *result, SaveBlock *sb )
{
if( sb->save_fn( image, sb->filename ) )
*result = -1;
g_free( sb->filename );
g_free( sb );
}
static void
vips_attach_save( VipsImage *image, VipsSaveFn save_fn, const char *filename )
{
SaveBlock *sb;
sb = g_new( SaveBlock, 1 );
sb->save_fn = save_fn;
sb->filename = g_strdup( filename );
g_signal_connect( image, "written",
G_CALLBACK( vips_image_save_cb ), sb );
}
IMAGE *
vips__deprecated_open_read( const char *filename, gboolean sequential )
{
VipsFormatClass *format;
if( !(format = vips_format_for_file( filename )) )
return( NULL );
if( vips_format_is_vips( format ) ) {
/* For vips format, we can just the main vips path.
*/
return( vips_image_new_mode( filename, "rd" ) );
}
else {
/* For non-vips formats we must go via the old VipsFormat
* system to make sure we support the "filename:options"
* syntax.
*/
IMAGE *image;
image = vips_image_new();
if( vips_image_open_lazy( image, format,
filename, sequential ) ) {
g_object_unref( image );
return( NULL );
}
/* Yuk. Can't g_object_set() filename since it's after
* construct. Just zap the new filename in.
*/
VIPS_SETSTR( image->filename, filename );
return( image );
}
}
IMAGE *
vips__deprecated_open_write( const char *filename )
{
VipsFormatClass *format;
if( !(format = vips_format_for_name( filename )) )
return( NULL );
if( vips_format_is_vips( format ) )
/* For vips format, we can just the main vips path.
*/
return( vips_image_new_mode( filename, "w" ) );
else {
/* For non-vips formats we must go via the old VipsFormat
* system to make sure we support the "filename:options"
* syntax.
*/
IMAGE *image;
if( !(image = vips_image_new()) )
return( NULL );
vips_attach_save( image,
format->save, filename );
return( image );
}
}