Merge branch 'master' of github.com:jcupitt/libvips
This commit is contained in:
commit
f5200dd4a0
@ -37,6 +37,8 @@
|
||||
im_*mosaic1(), im_*merge1() redone as classes
|
||||
- better filename tracking for globalbalance
|
||||
- revised vips8 image load/save API, now simpler and more logical
|
||||
- operations emit "invalidate" if any of their input images invalidate
|
||||
- operation cache drops ops on invalidate
|
||||
|
||||
6/3/14 started 7.38.6
|
||||
- grey ramp minimum was wrong
|
||||
|
57
TODO
57
TODO
@ -8,6 +8,7 @@
|
||||
|
||||
|
||||
|
||||
|
||||
- can we use postbuild elsewhere? look at use of "preclose" / "written", etc.
|
||||
|
||||
|
||||
@ -32,42 +33,6 @@
|
||||
|
||||
|
||||
|
||||
- think of a better way to support skipahead
|
||||
|
||||
extract needs to hint to it's image sources what line it will start reading
|
||||
at
|
||||
|
||||
in vips_extract_build(), call vips_image_set_start(), somehow sends a signal
|
||||
the wrong way down the pipe to the vips_sequential() (if there is one)
|
||||
telling it not to stall the first request if it's for that line
|
||||
|
||||
test --crop in vipsthumbnail on portrait images cropped to landscape
|
||||
|
||||
alternative: if extract sees a seq image, it deliberately reads and discards
|
||||
the N scanlines
|
||||
|
||||
could be very slow :-(
|
||||
|
||||
the first vips_extract_area_gen() needs to somehow signal in
|
||||
vips_region_prepare() that this is the first request
|
||||
|
||||
how do we pass the signal down? not clear
|
||||
|
||||
run the first tile single-threaded
|
||||
|
||||
so sinkdisc runs the first request, waits for it to come back, then
|
||||
starts all the workers going
|
||||
|
||||
this is quite a good idea! we'd not slow down much, and there's a huge
|
||||
amount of locking on the first tile anyway
|
||||
|
||||
now seq can support skipahead again
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
- now vips_linear() has uchar output, can we do something with orc?
|
||||
|
||||
- do restrict on some more packages, we've just done arithmetic so far
|
||||
@ -107,6 +72,7 @@
|
||||
- use g_log() instead of vips_info()?
|
||||
|
||||
|
||||
|
||||
- quadratic doesn't work for order 3
|
||||
|
||||
start to get jaggies on lines --- the 3rd differential isn't being
|
||||
@ -117,18 +83,6 @@
|
||||
because we step across tiles left to right: y doesn't change, only x does
|
||||
|
||||
|
||||
|
||||
- the operation cache needs to detect invalidate
|
||||
|
||||
tricky!
|
||||
|
||||
perhaps have operations always watching all of their inputs and resignalling
|
||||
"invalidate" themselves
|
||||
|
||||
cache then just needs to watch for "invalidate" on operations it tracks
|
||||
|
||||
need to add an "invalidate" signal to operation
|
||||
|
||||
mosaic
|
||||
======
|
||||
|
||||
@ -194,9 +148,6 @@ packaging
|
||||
|
||||
- test _O_TEMPORARY thing on Windows
|
||||
|
||||
- do we bundle "convert" in the OS X / win32 builds? if we don't we
|
||||
should
|
||||
|
||||
|
||||
convolution
|
||||
===========
|
||||
@ -233,10 +184,6 @@ convolution
|
||||
arithmetic
|
||||
==========
|
||||
|
||||
- avg/dev etc. should uncode images? eg. labq2lab etc.
|
||||
|
||||
how about ifthenelse?
|
||||
|
||||
- HAVE_HYPOT could define a hypot() macro?
|
||||
|
||||
- fix a better NaN policy
|
||||
|
@ -283,6 +283,11 @@ typedef struct _VipsArgumentInstance {
|
||||
* here.
|
||||
*/
|
||||
gulong close_id;
|
||||
|
||||
/* We need to listen for "invalidate" on input images and send our own
|
||||
* "invalidate" out. If we go, we need to disconnect.
|
||||
*/
|
||||
gulong invalidate_id;
|
||||
} VipsArgumentInstance;
|
||||
|
||||
/* Need to look up our VipsArgument structs from a pspec. Just hash the
|
||||
|
@ -65,11 +65,6 @@ typedef gboolean (*VipsOperationBuildFn)( VipsObject * );
|
||||
typedef struct _VipsOperation {
|
||||
VipsObject parent_instance;
|
||||
|
||||
/* When we added this operation to cache .. used to find LRU for
|
||||
* flush.
|
||||
*/
|
||||
int time;
|
||||
|
||||
/* Keep the hash here.
|
||||
*/
|
||||
guint hash;
|
||||
@ -92,12 +87,18 @@ typedef struct _VipsOperationClass {
|
||||
*/
|
||||
VipsOperationFlags (*get_flags)( VipsOperation * );
|
||||
VipsOperationFlags flags;
|
||||
|
||||
/* One of our input images has signalled "invalidate". The cache uses
|
||||
* VipsOperation::invalidate to drop dirty ops.
|
||||
*/
|
||||
void (*invalidate)( VipsOperation * );
|
||||
} VipsOperationClass;
|
||||
|
||||
GType vips_operation_get_type( void );
|
||||
|
||||
VipsOperationFlags vips_operation_get_flags( VipsOperation *operation );
|
||||
void vips_operation_class_print_usage( VipsOperationClass *operation_class );
|
||||
void vips_operation_invalidate( VipsOperation *operation );
|
||||
|
||||
int vips_operation_call_valist( VipsOperation *operation, va_list ap );
|
||||
VipsOperation *vips_operation_new( const char *name );
|
||||
|
@ -37,8 +37,6 @@
|
||||
|
||||
TODO
|
||||
|
||||
listen for invalidate
|
||||
|
||||
will we need to drop all on exit? unclear
|
||||
|
||||
what about delayed writes ... do we ever write in close? we shouldn't,
|
||||
@ -123,6 +121,22 @@ static GMutex *vips_cache_lock = NULL;
|
||||
#define VIPS_VALUE_GET_CHAR g_value_get_char
|
||||
#endif
|
||||
|
||||
/* A cache entry.
|
||||
*/
|
||||
typedef struct _VipsOperationCacheEntry {
|
||||
VipsOperation *operation;
|
||||
|
||||
/* When we added this operation to cache .. used to find LRU for
|
||||
* flush.
|
||||
*/
|
||||
int time;
|
||||
|
||||
/* We listen for "invalidate" from the operation. Track the id here so
|
||||
* we can disconnect when we drop an operation.
|
||||
*/
|
||||
gulong invalidate_id;
|
||||
} VipsOperationCacheEntry;
|
||||
|
||||
/* Pass in the pspec so we can get the generic type. For example, a
|
||||
* held in a GParamSpec allowing OBJECT, but the value could be of type
|
||||
* VipsImage. generics are much faster to compare.
|
||||
@ -450,10 +464,12 @@ vips__cache_init( void )
|
||||
static void *
|
||||
vips_cache_print_fn( void *value, void *a, void *b )
|
||||
{
|
||||
VipsOperationCacheEntry *entry = value;
|
||||
|
||||
char str[32768];
|
||||
VipsBuf buf = VIPS_BUF_STATIC( str );
|
||||
|
||||
vips_object_to_string( VIPS_OBJECT( value ), &buf );
|
||||
vips_object_to_string( VIPS_OBJECT( entry->operation ), &buf );
|
||||
|
||||
printf( "%p - %s\n", value, vips_buf_all( &buf ) );
|
||||
|
||||
@ -515,113 +531,25 @@ vips_cache_unref( VipsOperation *operation )
|
||||
g_object_unref( operation );
|
||||
}
|
||||
|
||||
/* Drop an operation from the cache.
|
||||
/* Remove an operation from the cache.
|
||||
*/
|
||||
static void
|
||||
vips_cache_drop( VipsOperation *operation )
|
||||
vips_cache_remove( VipsOperation *operation )
|
||||
{
|
||||
/* It must be in cache.
|
||||
*/
|
||||
g_assert( g_hash_table_lookup( vips_cache_table, operation ) );
|
||||
VipsOperationCacheEntry *entry = (VipsOperationCacheEntry *)
|
||||
g_hash_table_lookup( vips_cache_table, operation );
|
||||
|
||||
g_assert( entry );
|
||||
|
||||
if( entry->invalidate_id ) {
|
||||
g_signal_handler_disconnect( operation, entry->invalidate_id );
|
||||
entry->invalidate_id = 0;
|
||||
}
|
||||
|
||||
g_hash_table_remove( vips_cache_table, operation );
|
||||
vips_cache_unref( operation );
|
||||
}
|
||||
|
||||
static void *
|
||||
vips_cache_get_first_fn( void *value, void *a, void *b )
|
||||
{
|
||||
return( value );
|
||||
}
|
||||
|
||||
/* Return the first item.
|
||||
*/
|
||||
static VipsOperation *
|
||||
vips_cache_get_first( void )
|
||||
{
|
||||
if( vips_cache_table )
|
||||
return( VIPS_OPERATION( vips_hash_table_map( vips_cache_table,
|
||||
vips_cache_get_first_fn, NULL, NULL ) ) );
|
||||
else
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_cache_drop_all:
|
||||
*
|
||||
* Drop the whole operation cache, handy for leak tracking.
|
||||
*/
|
||||
void
|
||||
vips_cache_drop_all( void )
|
||||
{
|
||||
g_mutex_lock( vips_cache_lock );
|
||||
|
||||
if( vips_cache_table ) {
|
||||
VipsOperation *operation;
|
||||
|
||||
if( vips__cache_dump )
|
||||
vips_cache_print();
|
||||
|
||||
/* We can't modify the hash in the callback from
|
||||
* g_hash_table_foreach() and friends. Repeatedly drop the
|
||||
* first item instead.
|
||||
*/
|
||||
while( (operation = vips_cache_get_first()) )
|
||||
vips_cache_drop( operation );
|
||||
|
||||
VIPS_FREEF( g_hash_table_unref, vips_cache_table );
|
||||
}
|
||||
|
||||
g_mutex_unlock( vips_cache_lock );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_cache_get_lru_cb( VipsOperation *key, VipsOperation *value,
|
||||
VipsOperation **best )
|
||||
{
|
||||
if( !*best ||
|
||||
(*best)->time > value->time )
|
||||
*best = value;
|
||||
}
|
||||
|
||||
/* Get the least-recently-used cache item.
|
||||
*
|
||||
* TODO ... will this be too expensive? probably not
|
||||
*/
|
||||
static VipsOperation *
|
||||
vips_cache_get_lru( void )
|
||||
{
|
||||
VipsOperation *operation;
|
||||
|
||||
operation = NULL;
|
||||
g_hash_table_foreach( vips_cache_table,
|
||||
(GHFunc) vips_cache_get_lru_cb, &operation );
|
||||
|
||||
return( operation );
|
||||
}
|
||||
|
||||
/* Is the cache full? Drop until it's not.
|
||||
*/
|
||||
static void
|
||||
vips_cache_trim( void )
|
||||
{
|
||||
VipsOperation *operation;
|
||||
|
||||
g_mutex_lock( vips_cache_lock );
|
||||
|
||||
while( vips_cache_table &&
|
||||
(g_hash_table_size( vips_cache_table ) > vips_cache_max ||
|
||||
vips_tracked_get_files() > vips_cache_max_files ||
|
||||
vips_tracked_get_mem() > vips_cache_max_mem) &&
|
||||
(operation = vips_cache_get_lru()) ) {
|
||||
#ifdef DEBUG
|
||||
printf( "vips_cache_trim: trimming %p\n", operation );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
vips_cache_drop( operation );
|
||||
}
|
||||
|
||||
g_mutex_unlock( vips_cache_lock );
|
||||
g_free( entry );
|
||||
}
|
||||
|
||||
static void *
|
||||
@ -649,8 +577,11 @@ vips_object_ref_arg( VipsObject *object,
|
||||
static void
|
||||
vips_operation_touch( VipsOperation *operation )
|
||||
{
|
||||
VipsOperationCacheEntry *entry = (VipsOperationCacheEntry *)
|
||||
g_hash_table_lookup( vips_cache_table, operation );
|
||||
|
||||
vips_cache_time += 1;
|
||||
operation->time = vips_cache_time;
|
||||
entry->time = vips_cache_time;
|
||||
}
|
||||
|
||||
/* Ref an operation for the cache. The operation itself, plus all the output
|
||||
@ -665,6 +596,130 @@ vips_cache_ref( VipsOperation *operation )
|
||||
vips_operation_touch( operation );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_cache_insert( VipsOperation *operation )
|
||||
{
|
||||
VipsOperationCacheEntry *entry = g_new( VipsOperationCacheEntry, 1 );
|
||||
|
||||
/* It must not be in cache.
|
||||
*/
|
||||
g_assert( !g_hash_table_lookup( vips_cache_table, operation ) );
|
||||
|
||||
entry->operation = operation;
|
||||
entry->time = 0;
|
||||
entry->invalidate_id = 0;
|
||||
|
||||
g_hash_table_insert( vips_cache_table, operation, entry );
|
||||
vips_cache_ref( operation );
|
||||
|
||||
/* If the operation signals "invalidate", we must drop it.
|
||||
*/
|
||||
entry->invalidate_id = g_signal_connect( operation, "invalidate",
|
||||
G_CALLBACK( vips_cache_remove ), NULL );
|
||||
}
|
||||
|
||||
static void *
|
||||
vips_cache_get_first_fn( void *value, void *a, void *b )
|
||||
{
|
||||
return( value );
|
||||
}
|
||||
|
||||
/* Return the first item.
|
||||
*/
|
||||
static VipsOperation *
|
||||
vips_cache_get_first( void )
|
||||
{
|
||||
VipsOperationCacheEntry *entry;
|
||||
|
||||
if( vips_cache_table &&
|
||||
(entry = vips_hash_table_map( vips_cache_table,
|
||||
vips_cache_get_first_fn, NULL, NULL )) )
|
||||
return( VIPS_OPERATION( entry->operation ) );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_cache_drop_all:
|
||||
*
|
||||
* Drop the whole operation cache, handy for leak tracking.
|
||||
*/
|
||||
void
|
||||
vips_cache_drop_all( void )
|
||||
{
|
||||
g_mutex_lock( vips_cache_lock );
|
||||
|
||||
if( vips_cache_table ) {
|
||||
VipsOperation *operation;
|
||||
|
||||
if( vips__cache_dump )
|
||||
vips_cache_print();
|
||||
|
||||
/* We can't modify the hash in the callback from
|
||||
* g_hash_table_foreach() and friends. Repeatedly drop the
|
||||
* first item instead.
|
||||
*/
|
||||
while( (operation = vips_cache_get_first()) )
|
||||
vips_cache_remove( operation );
|
||||
|
||||
VIPS_FREEF( g_hash_table_unref, vips_cache_table );
|
||||
}
|
||||
|
||||
g_mutex_unlock( vips_cache_lock );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_cache_get_lru_cb( VipsOperation *key, VipsOperationCacheEntry *value,
|
||||
VipsOperationCacheEntry **best )
|
||||
{
|
||||
if( !*best ||
|
||||
(*best)->time > value->time )
|
||||
*best = value;
|
||||
}
|
||||
|
||||
/* Get the least-recently-used cache item.
|
||||
*
|
||||
* TODO ... will this be too expensive? probably not
|
||||
*/
|
||||
static VipsOperation *
|
||||
vips_cache_get_lru( void )
|
||||
{
|
||||
VipsOperationCacheEntry *entry;
|
||||
|
||||
entry = NULL;
|
||||
g_hash_table_foreach( vips_cache_table,
|
||||
(GHFunc) vips_cache_get_lru_cb, &entry );
|
||||
|
||||
if( entry )
|
||||
return( entry->operation );
|
||||
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
/* Is the cache full? Drop until it's not.
|
||||
*/
|
||||
static void
|
||||
vips_cache_trim( void )
|
||||
{
|
||||
VipsOperation *operation;
|
||||
|
||||
g_mutex_lock( vips_cache_lock );
|
||||
|
||||
while( vips_cache_table &&
|
||||
(g_hash_table_size( vips_cache_table ) > vips_cache_max ||
|
||||
vips_tracked_get_files() > vips_cache_max_files ||
|
||||
vips_tracked_get_mem() > vips_cache_max_mem) &&
|
||||
(operation = vips_cache_get_lru()) ) {
|
||||
#ifdef DEBUG
|
||||
printf( "vips_cache_trim: trimming %p\n", operation );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
vips_cache_remove( operation );
|
||||
}
|
||||
|
||||
g_mutex_unlock( vips_cache_lock );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_cache_operation_buildp: (skip)
|
||||
* @operation: pointer to operation to lookup
|
||||
@ -679,7 +734,7 @@ vips_cache_ref( VipsOperation *operation )
|
||||
int
|
||||
vips_cache_operation_buildp( VipsOperation **operation )
|
||||
{
|
||||
VipsOperation *hit;
|
||||
VipsOperationCacheEntry *hit;
|
||||
|
||||
g_assert( VIPS_IS_OPERATION( *operation ) );
|
||||
|
||||
@ -693,15 +748,15 @@ vips_cache_operation_buildp( VipsOperation **operation )
|
||||
if( (hit = g_hash_table_lookup( vips_cache_table, *operation )) ) {
|
||||
if( vips__cache_trace ) {
|
||||
printf( "vips cache-: " );
|
||||
vips_object_print_summary( VIPS_OBJECT( hit ) );
|
||||
vips_object_print_summary( VIPS_OBJECT( *operation ) );
|
||||
}
|
||||
|
||||
/* Ref before unref in case *operation == hit.
|
||||
*/
|
||||
vips_cache_ref( hit );
|
||||
vips_cache_ref( hit->operation );
|
||||
g_object_unref( *operation );
|
||||
|
||||
*operation = hit;
|
||||
*operation = hit->operation;
|
||||
}
|
||||
|
||||
g_mutex_unlock( vips_cache_lock );
|
||||
@ -724,11 +779,8 @@ vips_cache_operation_buildp( VipsOperation **operation )
|
||||
g_mutex_lock( vips_cache_lock );
|
||||
|
||||
if( !(vips_operation_get_flags( *operation ) &
|
||||
VIPS_OPERATION_NOCACHE) ) {
|
||||
vips_cache_ref( *operation );
|
||||
g_hash_table_insert( vips_cache_table,
|
||||
*operation, *operation );
|
||||
}
|
||||
VIPS_OPERATION_NOCACHE) )
|
||||
vips_cache_insert( *operation );
|
||||
|
||||
g_mutex_unlock( vips_cache_lock );
|
||||
}
|
||||
|
@ -2747,6 +2747,11 @@ vips_image_inplace( VipsImage *image )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* This image is about to be changed (probably). Make sure it's not
|
||||
* in cache.
|
||||
*/
|
||||
vips_image_invalidate_all( image );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
@ -392,16 +392,37 @@ vips_object_rewind( VipsObject *object )
|
||||
/* Extra stuff we track for properties to do our argument handling.
|
||||
*/
|
||||
|
||||
static void
|
||||
vips_argument_instance_detach( VipsArgumentInstance *argument_instance )
|
||||
{
|
||||
VipsObject *object = argument_instance->object;
|
||||
VipsArgumentClass *argument_class = argument_instance->argument_class;
|
||||
GObject *member = G_STRUCT_MEMBER( GObject *, object,
|
||||
argument_class->offset );
|
||||
|
||||
if( argument_instance->close_id ) {
|
||||
if( g_signal_handler_is_connected( member,
|
||||
argument_instance->close_id ) )
|
||||
g_signal_handler_disconnect( member,
|
||||
argument_instance->close_id );
|
||||
argument_instance->close_id = 0;
|
||||
}
|
||||
|
||||
if( argument_instance->invalidate_id ) {
|
||||
if( g_signal_handler_is_connected( member,
|
||||
argument_instance->invalidate_id ) )
|
||||
g_signal_handler_disconnect( member,
|
||||
argument_instance->invalidate_id );
|
||||
argument_instance->invalidate_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* Free a VipsArgumentInstance ... VipsArgumentClass can just be g_free()d.
|
||||
*/
|
||||
static void
|
||||
vips_argument_instance_free( VipsArgumentInstance *argument_instance )
|
||||
{
|
||||
if( argument_instance->close_id ) {
|
||||
g_signal_handler_disconnect( argument_instance->object,
|
||||
argument_instance->close_id );
|
||||
argument_instance->close_id = 0;
|
||||
}
|
||||
vips_argument_instance_detach( argument_instance );
|
||||
g_free( argument_instance );
|
||||
}
|
||||
|
||||
@ -580,6 +601,7 @@ vips_argument_init( VipsObject *object )
|
||||
argument_class->flags &
|
||||
VIPS_ARGUMENT_SET_ALWAYS;
|
||||
argument_instance->close_id = 0;
|
||||
argument_instance->invalidate_id = 0;
|
||||
|
||||
vips_argument_table_replace( object->argument_table,
|
||||
(VipsArgument *) argument_instance );
|
||||
@ -720,14 +742,14 @@ vips_object_get_argument_priority( VipsObject *object, const char *name )
|
||||
}
|
||||
|
||||
static void
|
||||
vips_object_clear_member( VipsObject *object, GParamSpec *pspec,
|
||||
GObject **member )
|
||||
vips_object_clear_member( VipsArgumentInstance *argument_instance )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsArgumentClass *argument_class = (VipsArgumentClass *)
|
||||
vips__argument_table_lookup( class->argument_table, pspec );
|
||||
VipsArgumentInstance *argument_instance =
|
||||
vips__argument_get_instance( argument_class, object );
|
||||
VipsObject *object = argument_instance->object;
|
||||
VipsArgumentClass *argument_class = argument_instance->argument_class;
|
||||
GObject **member = &G_STRUCT_MEMBER( GObject *, object,
|
||||
argument_class->offset );
|
||||
|
||||
vips_argument_instance_detach( argument_instance );
|
||||
|
||||
if( *member ) {
|
||||
if( argument_class->flags & VIPS_ARGUMENT_INPUT ) {
|
||||
@ -754,17 +776,6 @@ vips_object_clear_member( VipsObject *object, GParamSpec *pspec,
|
||||
G_OBJECT( object )->ref_count - 1 );
|
||||
#endif /*DEBUG_REF*/
|
||||
|
||||
/* The object reffed us. Stop listening link to the
|
||||
* object's "close" signal. We can come here from
|
||||
* object being closed, in which case the handler
|
||||
* will already have been disconnected for us.
|
||||
*/
|
||||
if( g_signal_handler_is_connected( object,
|
||||
argument_instance->close_id ) )
|
||||
g_signal_handler_disconnect( object,
|
||||
argument_instance->close_id );
|
||||
argument_instance->close_id = 0;
|
||||
|
||||
g_object_unref( object );
|
||||
}
|
||||
|
||||
@ -919,6 +930,18 @@ vips_object_finalize( GObject *gobject )
|
||||
G_OBJECT_CLASS( vips_object_parent_class )->finalize( gobject );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_object_arg_invalidate( GObject *argument,
|
||||
VipsArgumentInstance *argument_instance )
|
||||
{
|
||||
/* Image @argument has signalled "invalidate" ... resignal on our
|
||||
* operation.
|
||||
*/
|
||||
if( VIPS_IS_OPERATION( argument_instance->object ) )
|
||||
vips_operation_invalidate(
|
||||
VIPS_OPERATION( argument_instance->object ) );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_object_arg_close( GObject *argument,
|
||||
VipsArgumentInstance *argument_instance )
|
||||
@ -946,10 +969,11 @@ vips__object_set_member( VipsObject *object, GParamSpec *pspec,
|
||||
vips__argument_table_lookup( class->argument_table, pspec );
|
||||
VipsArgumentInstance *argument_instance =
|
||||
vips__argument_get_instance( argument_class, object );
|
||||
GType otype = G_PARAM_SPEC_VALUE_TYPE( pspec );
|
||||
|
||||
g_assert( argument_instance );
|
||||
|
||||
vips_object_clear_member( object, pspec, member );
|
||||
vips_object_clear_member( argument_instance );
|
||||
|
||||
g_assert( !*member );
|
||||
*member = argument;
|
||||
@ -957,7 +981,7 @@ vips__object_set_member( VipsObject *object, GParamSpec *pspec,
|
||||
if( *member ) {
|
||||
if( argument_class->flags & VIPS_ARGUMENT_INPUT ) {
|
||||
#ifdef DEBUG_REF
|
||||
printf( "vips_object_set_member: vips object: " );
|
||||
printf( "vips__object_set_member: vips object: " );
|
||||
vips_object_print_name( object );
|
||||
printf( " refers to gobject %s (%p)\n",
|
||||
G_OBJECT_TYPE_NAME( *member ), *member );
|
||||
@ -971,7 +995,7 @@ vips__object_set_member( VipsObject *object, GParamSpec *pspec,
|
||||
}
|
||||
else if( argument_class->flags & VIPS_ARGUMENT_OUTPUT ) {
|
||||
#ifdef DEBUG_REF
|
||||
printf( "vips_object_set_member: gobject %s (%p)\n",
|
||||
printf( "vips__object_set_member: gobject %s (%p)\n",
|
||||
G_OBJECT_TYPE_NAME( *member ), *member );
|
||||
printf( " refers to vips object: " );
|
||||
vips_object_print_name( object );
|
||||
@ -982,10 +1006,23 @@ vips__object_set_member( VipsObject *object, GParamSpec *pspec,
|
||||
/* The argument reffs us.
|
||||
*/
|
||||
g_object_ref( object );
|
||||
}
|
||||
}
|
||||
|
||||
/* FIXME ... could use a NULLing weakref
|
||||
*/
|
||||
if( *member &&
|
||||
g_type_is_a( otype, VIPS_TYPE_IMAGE ) ) {
|
||||
if( argument_class->flags & VIPS_ARGUMENT_INPUT ) {
|
||||
g_assert( !argument_instance->invalidate_id );
|
||||
|
||||
argument_instance->invalidate_id =
|
||||
g_signal_connect( *member, "invalidate",
|
||||
G_CALLBACK(
|
||||
vips_object_arg_invalidate ),
|
||||
argument_instance );
|
||||
}
|
||||
else if( argument_class->flags & VIPS_ARGUMENT_OUTPUT ) {
|
||||
g_assert( !argument_instance->close_id );
|
||||
|
||||
argument_instance->close_id =
|
||||
g_signal_connect( *member, "close",
|
||||
G_CALLBACK( vips_object_arg_close ),
|
||||
@ -1098,7 +1135,7 @@ vips_object_set_property( GObject *gobject,
|
||||
GObject **member = &G_STRUCT_MEMBER( GObject *, object,
|
||||
argument_class->offset );
|
||||
|
||||
vips__object_set_member( object, pspec, member,
|
||||
vips__object_set_member( object, pspec, member,
|
||||
g_value_get_object( value ) );
|
||||
}
|
||||
else if( G_IS_PARAM_SPEC_INT( pspec ) ) {
|
||||
|
@ -92,6 +92,15 @@
|
||||
/* Abstract base class for operations.
|
||||
*/
|
||||
|
||||
/* Our signals.
|
||||
*/
|
||||
enum {
|
||||
SIG_INVALIDATE,
|
||||
SIG_LAST
|
||||
};
|
||||
|
||||
static guint vips_operation_signals[SIG_LAST] = { 0 };
|
||||
|
||||
G_DEFINE_ABSTRACT_TYPE( VipsOperation, vips_operation, VIPS_TYPE_OBJECT );
|
||||
|
||||
static void
|
||||
@ -380,6 +389,14 @@ vips_operation_class_init( VipsOperationClass *class )
|
||||
|
||||
class->usage = vips_operation_usage;
|
||||
class->get_flags = vips_operation_real_get_flags;
|
||||
|
||||
vips_operation_signals[SIG_INVALIDATE] = g_signal_new( "invalidate",
|
||||
G_TYPE_FROM_CLASS( class ),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET( VipsOperationClass, invalidate ),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
@ -419,6 +436,17 @@ vips_operation_class_print_usage( VipsOperationClass *operation_class )
|
||||
printf( "%s", vips_buf_all( &buf ) );
|
||||
}
|
||||
|
||||
void
|
||||
vips_operation_invalidate( VipsOperation *operation )
|
||||
{
|
||||
/*
|
||||
printf( "vips_operation_invalidate: %p\n", operation );
|
||||
vips_object_print_summary( VIPS_OBJECT( operation ) );
|
||||
*/
|
||||
|
||||
g_signal_emit( operation, vips_operation_signals[SIG_INVALIDATE], 0 );
|
||||
}
|
||||
|
||||
VipsOperation *
|
||||
vips_operation_new( const char *name )
|
||||
{
|
||||
|
@ -60,11 +60,11 @@ static int
|
||||
vips_labelregions_build( VipsObject *object )
|
||||
{
|
||||
VipsMorphology *morphology = VIPS_MORPHOLOGY( object );
|
||||
VipsLabelregions *labelregions = (VipsLabelregions *) object;
|
||||
VipsImage *in = morphology->in;
|
||||
VipsImage **t = (VipsImage **) vips_object_local_array( object, 7 );
|
||||
VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 );
|
||||
VipsImage *mask;
|
||||
|
||||
int serial;
|
||||
int segments;
|
||||
int *m;
|
||||
int x, y;
|
||||
|
||||
@ -72,43 +72,39 @@ vips_labelregions_build( VipsObject *object )
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
|
||||
/* Create the zero mask image.
|
||||
/* Create the zero mask image in memory.
|
||||
*/
|
||||
mask = vips_image_new_memory();
|
||||
g_object_set( object,
|
||||
"mask", mask,
|
||||
NULL );
|
||||
if( vips_black( &t[0], in->Xsize, in->Ysize, NULL ) ||
|
||||
vips_cast( t[0], &t[1], VIPS_FORMAT_INT, NULL ) )
|
||||
vips_cast( t[0], &t[1], VIPS_FORMAT_INT, NULL ) ||
|
||||
vips_image_write( t[1], mask ) )
|
||||
return( -1 );
|
||||
|
||||
/* Search the mask image, flooding as we find zero pixels.
|
||||
*/
|
||||
if( vips_image_inplace( t[1] ) )
|
||||
return( -1 );
|
||||
|
||||
serial = 1;
|
||||
m = (int *) t[1]->data;
|
||||
for( y = 0; y < t[1]->Ysize; y++ ) {
|
||||
for( x = 0; x < t[1]->Xsize; x++ ) {
|
||||
segments = 1;
|
||||
m = (int *) mask->data;
|
||||
for( y = 0; y < mask->Ysize; y++ ) {
|
||||
for( x = 0; x < mask->Xsize; x++ ) {
|
||||
if( !m[x] ) {
|
||||
/* Use a direct path for speed.
|
||||
*/
|
||||
if( vips__draw_flood_direct( t[1], in,
|
||||
serial, x, y ) )
|
||||
if( vips__draw_flood_direct( mask, in,
|
||||
segments, x, y ) )
|
||||
return( -1 );
|
||||
|
||||
serial += 1;
|
||||
segments += 1;
|
||||
}
|
||||
}
|
||||
|
||||
m += t[1]->Xsize;
|
||||
m += mask->Xsize;
|
||||
}
|
||||
|
||||
g_object_set( object,
|
||||
"mask", vips_image_new(),
|
||||
"segments", serial,
|
||||
"segments", segments,
|
||||
NULL );
|
||||
|
||||
if( vips_image_write( t[1], labelregions->mask ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user