Merge remote-tracking branch 'origin/master' into colour
Conflicts: ChangeLog configure.in po/vips7.pot
This commit is contained in:
commit
f8a160de44
12
ChangeLog
12
ChangeLog
@ -5,9 +5,21 @@
|
||||
im_LabS2LabQ(), im_LabQ2disp(), im_XYZ2disp(), im_disp2XYZ(),
|
||||
im_icc_import*(), im_icc_export*(), im_icc_transform*() as classes
|
||||
- added vips_colour_convert(), replaces all derived conversions
|
||||
- dzsave can write zoomify and google maps layout as well
|
||||
- tilecache supports threaded access
|
||||
- openslide2vips gets underlying tile size from openslide
|
||||
- embed has 'background' option
|
||||
- dzsave --layout google has a @background option
|
||||
|
||||
2/10/12 started 7.30.4
|
||||
- remove options from format string in .dzi (thanks Martin)
|
||||
- vipsCC.pc required the wrong version of vips (thanks Alessandro)
|
||||
|
||||
13/9/12 started 7.30.3
|
||||
- linecache sized itself too large
|
||||
- fix a compile failure if libtiff was not found (thanks Martin)
|
||||
- dzsave did not work for images with an odd number of scanlines
|
||||
(thanks Martin)
|
||||
|
||||
4/9/12 started 7.30.2
|
||||
- sequential stops all threads on error
|
||||
|
5
TODO
5
TODO
@ -1,3 +1,8 @@
|
||||
- get rid of no-threads mode, glib no longer supports this
|
||||
|
||||
- argh g_mutex_new() is now deprecated, make our own vips_mutex_new()
|
||||
compatibility function that uses g_mutex_init() internally, if available
|
||||
|
||||
- add mono as a colourspace? also rad?
|
||||
|
||||
- something to test if an image is in a supported colourspace?
|
||||
|
@ -16,8 +16,8 @@ AC_CONFIG_MACRO_DIR([m4])
|
||||
|
||||
# user-visible library versioning
|
||||
m4_define([vips_major_version], [7])
|
||||
m4_define([vips_minor_version], [30])
|
||||
m4_define([vips_micro_version], [3])
|
||||
m4_define([vips_minor_version], [31])
|
||||
m4_define([vips_micro_version], [0])
|
||||
m4_define([vips_version],
|
||||
[vips_major_version.vips_minor_version.vips_micro_version])
|
||||
|
||||
@ -37,8 +37,8 @@ VIPS_VERSION_STRING=$VIPS_VERSION-`date`
|
||||
# binary interface changes not backwards compatible?: reset age to 0
|
||||
|
||||
LIBRARY_CURRENT=33
|
||||
LIBRARY_REVISION=6
|
||||
LIBRARY_AGE=2
|
||||
LIBRARY_REVISION=8
|
||||
LIBRARY_AGE=4
|
||||
|
||||
# patched into include/vips/version.h
|
||||
AC_SUBST(VIPS_VERSION)
|
||||
|
@ -83,8 +83,9 @@
|
||||
#ifdef HAVE_LCMS
|
||||
typedef DWORD cmsUInt32Number;
|
||||
|
||||
#define cmsSigRgbData icSigRgbData:
|
||||
#define cmsSigCmykData icSigCmykData:
|
||||
/* This doesn't exist in lcms1, just set it to zero.
|
||||
*/
|
||||
#define cmsFLAGS_NOCACHE (0)
|
||||
#endif
|
||||
|
||||
/**
|
||||
@ -191,7 +192,7 @@ vips_icc_build( VipsObject *object )
|
||||
if( icc->in_profile &&
|
||||
code->in ) {
|
||||
switch( cmsGetColorSpace( icc->in_profile ) ) {
|
||||
case cmsSigRgbData:
|
||||
case icSigRgbData:
|
||||
code->input_bands = 3;
|
||||
code->input_format =
|
||||
code->in->BandFmt == VIPS_FORMAT_USHORT ?
|
||||
@ -201,7 +202,7 @@ vips_icc_build( VipsObject *object )
|
||||
TYPE_RGB_16 : TYPE_RGB_8;
|
||||
break;
|
||||
|
||||
case cmsSigCmykData:
|
||||
case icSigCmykData:
|
||||
code->input_bands = 4;
|
||||
code->input_format =
|
||||
code->in->BandFmt == VIPS_FORMAT_USHORT ?
|
||||
@ -211,7 +212,7 @@ vips_icc_build( VipsObject *object )
|
||||
TYPE_CMYK_16 : TYPE_CMYK_8;
|
||||
break;
|
||||
|
||||
case cmsSigLabData:
|
||||
case icSigLabData:
|
||||
code->input_bands = 3;
|
||||
code->input_format = VIPS_FORMAT_FLOAT;
|
||||
icc->in_icc_format = TYPE_Lab_16;
|
||||
@ -265,8 +266,8 @@ vips_icc_build( VipsObject *object )
|
||||
*/
|
||||
if( icc->in_profile &&
|
||||
icc->out_profile &&
|
||||
cmsGetColorSpace( icc->in_profile ) == cmsSigLabData &&
|
||||
cmsGetColorSpace( icc->out_profile ) == cmsSigLabData ) {
|
||||
cmsGetColorSpace( icc->in_profile ) == icSigLabData &&
|
||||
cmsGetColorSpace( icc->out_profile ) == icSigLabData ) {
|
||||
vips_error( class->nickname,
|
||||
"%s", _( "no device profile" ) );
|
||||
return( -1 );
|
||||
|
@ -23,6 +23,8 @@
|
||||
* - cleanups
|
||||
* 15/10/11
|
||||
* - rewrite as a class
|
||||
* 10/10/12
|
||||
* - add @background
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -79,11 +81,16 @@ typedef struct _VipsEmbed {
|
||||
VipsImage *in;
|
||||
|
||||
VipsExtend extend;
|
||||
VipsArea *background;
|
||||
int x;
|
||||
int y;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
/* Pixel we paint calculated from background.
|
||||
*/
|
||||
VipsPel *ink;
|
||||
|
||||
/* Geometry calculations.
|
||||
*/
|
||||
VipsRect rout; /* Whole output area */
|
||||
@ -257,6 +264,14 @@ vips_embed_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
|
||||
embed->extend == 0 ? 0 : 255 );
|
||||
break;
|
||||
|
||||
case VIPS_EXTEND_BACKGROUND:
|
||||
/* Paint the borders a solid value.
|
||||
*/
|
||||
for( i = 0; i < 8; i++ )
|
||||
vips_region_paint_pel( or, &embed->border[i],
|
||||
embed->ink );
|
||||
break;
|
||||
|
||||
case VIPS_EXTEND_COPY:
|
||||
/* Extend the borders.
|
||||
*/
|
||||
@ -327,6 +342,15 @@ vips_embed_build( VipsObject *object )
|
||||
if( vips_image_pio_input( embed->in ) )
|
||||
return( -1 );
|
||||
|
||||
if( !(embed->ink = vips__vector_to_ink(
|
||||
"VipsEmbed", embed->in,
|
||||
embed->background->data, embed->background->n )) )
|
||||
return( -1 );
|
||||
|
||||
if( !vips_object_get_argument_assigned( object, "extend" ) &&
|
||||
vips_object_get_argument_assigned( object, "background" ) )
|
||||
embed->extend = VIPS_EXTEND_BACKGROUND;
|
||||
|
||||
switch( embed->extend ) {
|
||||
case VIPS_EXTEND_REPEAT:
|
||||
{
|
||||
@ -398,6 +422,7 @@ vips_embed_build( VipsObject *object )
|
||||
|
||||
case VIPS_EXTEND_BLACK:
|
||||
case VIPS_EXTEND_WHITE:
|
||||
case VIPS_EXTEND_BACKGROUND:
|
||||
case VIPS_EXTEND_COPY:
|
||||
if( vips_image_copy_fields( conversion->out, embed->in ) )
|
||||
return( -1 );
|
||||
@ -559,13 +584,22 @@ vips_embed_class_init( VipsEmbedClass *class )
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsEmbed, extend ),
|
||||
VIPS_TYPE_EXTEND, VIPS_EXTEND_BLACK );
|
||||
|
||||
VIPS_ARG_BOXED( class, "background", 12,
|
||||
_( "Background" ),
|
||||
_( "Colour for background pixels" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsEmbed, background ),
|
||||
VIPS_TYPE_ARRAY_DOUBLE );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_embed_init( VipsEmbed *embed )
|
||||
{
|
||||
/* Init our instance fields.
|
||||
*/
|
||||
embed->extend = VIPS_EXTEND_BLACK;
|
||||
embed->background =
|
||||
vips_area_new_array( G_TYPE_DOUBLE, sizeof( double ), 1 );
|
||||
((double *) (embed->background->data))[0] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -581,6 +615,7 @@ vips_embed_init( VipsEmbed *embed )
|
||||
* Optional arguments:
|
||||
*
|
||||
* @extend: how to generate the edge pixels
|
||||
* @background: colour for edge pixels
|
||||
*
|
||||
* The opposite of vips_extract_area(): embed @in within an image of size
|
||||
* @width by @height at position @x, @y. @extend
|
||||
|
@ -155,9 +155,8 @@ vips_sequential_generate( VipsRegion *or,
|
||||
* either not read anything yet or fallen through from the stall
|
||||
* above.
|
||||
*
|
||||
* Probably the operation is something like
|
||||
* extract_area and we should skip the initial part of the image. In
|
||||
* fact we read to cache.
|
||||
* Probably the operation is something like extract_area and we should
|
||||
* skip the initial part of the image. In fact, we read to cache.
|
||||
*/
|
||||
if( r->top > sequential->y_pos ) {
|
||||
VipsRect area;
|
||||
|
@ -21,6 +21,8 @@
|
||||
* - use a hash table instead of a list
|
||||
* 13/9/12
|
||||
* - oops, linecache was oversized
|
||||
* 8/10/12
|
||||
* - make it optionally threaded
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -68,13 +70,32 @@
|
||||
|
||||
#include "conversion.h"
|
||||
|
||||
/* A tile in cache can be in one of three states:
|
||||
*
|
||||
* DATA - the tile holds valid pixels
|
||||
* CALC - some thread somewhere is calculating it
|
||||
* PEND - some thread somewhere wants it
|
||||
*/
|
||||
typedef enum VipsTileState {
|
||||
VIPS_TILE_STATE_DATA,
|
||||
VIPS_TILE_STATE_CALC,
|
||||
VIPS_TILE_STATE_PEND
|
||||
} VipsTileState;
|
||||
|
||||
/* A tile in our cache.
|
||||
*/
|
||||
typedef struct _VipsTile {
|
||||
struct _VipsBlockCache *cache;
|
||||
|
||||
VipsTileState state;
|
||||
|
||||
VipsRegion *region; /* Region with private mem for data */
|
||||
|
||||
/* We count how many threads are relying on this tile. This tile can't
|
||||
* be flushed if ref_count > 0.
|
||||
*/
|
||||
int ref_count;
|
||||
|
||||
/* Tile position. Just use left/top to calculate a hash. This is the
|
||||
* key for the hash table. Don't use region->valid in case the region
|
||||
* pointer is NULL.
|
||||
@ -92,10 +113,12 @@ typedef struct _VipsBlockCache {
|
||||
int tile_height;
|
||||
int max_tiles;
|
||||
VipsCacheStrategy strategy;
|
||||
gboolean threaded;
|
||||
|
||||
int time; /* Update ticks for LRU here */
|
||||
int ntiles; /* Current cache size */
|
||||
GMutex *lock; /* Lock everything here */
|
||||
GCond *new_tile; /* A new tile is ready */
|
||||
GHashTable *tiles; /* Tiles, hashed by coordinates */
|
||||
} VipsBlockCache;
|
||||
|
||||
@ -108,6 +131,10 @@ G_DEFINE_TYPE( VipsBlockCache, vips_block_cache, VIPS_TYPE_CONVERSION );
|
||||
static void
|
||||
vips_block_cache_drop_all( VipsBlockCache *cache )
|
||||
{
|
||||
/* FIXME this is a disaster if active threads are working on tiles. We
|
||||
* should have something to block new requests, and only dispose once
|
||||
* all tiles are unreffed.
|
||||
*/
|
||||
g_hash_table_remove_all( cache->tiles );
|
||||
}
|
||||
|
||||
@ -118,6 +145,7 @@ vips_block_cache_dispose( GObject *gobject )
|
||||
|
||||
vips_block_cache_drop_all( cache );
|
||||
VIPS_FREEF( g_mutex_free, cache->lock );
|
||||
VIPS_FREEF( g_cond_free, cache->new_tile );
|
||||
|
||||
G_OBJECT_CLASS( vips_block_cache_parent_class )->dispose( gobject );
|
||||
}
|
||||
@ -140,6 +168,10 @@ vips_tile_move( VipsTile *tile, int x, int y )
|
||||
if( vips_region_buffer( tile->region, &tile->pos ) )
|
||||
return( -1 );
|
||||
|
||||
/* No data yet, but someone must want it.
|
||||
*/
|
||||
tile->state = VIPS_TILE_STATE_PEND;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
@ -152,6 +184,8 @@ vips_tile_new( VipsBlockCache *cache, int x, int y )
|
||||
return( NULL );
|
||||
|
||||
tile->cache = cache;
|
||||
tile->state = VIPS_TILE_STATE_PEND;
|
||||
tile->ref_count = 0;
|
||||
tile->region = NULL;
|
||||
tile->time = cache->time;
|
||||
tile->pos.left = x;
|
||||
@ -213,6 +247,7 @@ vips_tile_fill( VipsTile *tile, VipsRegion *in )
|
||||
if( vips_region_prepare_to( in, tile->region,
|
||||
&tile->pos, tile->pos.left, tile->pos.top ) )
|
||||
return( -1 );
|
||||
tile->state = VIPS_TILE_STATE_DATA;
|
||||
|
||||
vips_tile_touch( tile );
|
||||
|
||||
@ -226,25 +261,31 @@ typedef struct _VipsTileSearch {
|
||||
int topmost;
|
||||
} VipsTileSearch;
|
||||
|
||||
void
|
||||
static void
|
||||
vips_tile_oldest( gpointer key, gpointer value, gpointer user_data )
|
||||
{
|
||||
VipsTile *tile = (VipsTile *) value;
|
||||
VipsTileSearch *search = (VipsTileSearch *) user_data;
|
||||
|
||||
if( tile->time < search->oldest ) {
|
||||
/* Only consider unreffed tiles for recycling.
|
||||
*/
|
||||
if( !tile->ref_count &&
|
||||
tile->time < search->oldest ) {
|
||||
search->oldest = tile->time;
|
||||
search->tile = tile;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
vips_tile_topmost( gpointer key, gpointer value, gpointer user_data )
|
||||
{
|
||||
VipsTile *tile = (VipsTile *) value;
|
||||
VipsTileSearch *search = (VipsTileSearch *) user_data;
|
||||
|
||||
if( tile->pos.top < search->topmost ) {
|
||||
/* Only consider unreffed tiles for recycling.
|
||||
*/
|
||||
if( !tile->ref_count &&
|
||||
tile->pos.top < search->topmost ) {
|
||||
search->topmost = tile->pos.top;
|
||||
search->tile = tile;
|
||||
}
|
||||
@ -254,7 +295,7 @@ vips_tile_topmost( gpointer key, gpointer value, gpointer user_data )
|
||||
* reuse LRU.
|
||||
*/
|
||||
static VipsTile *
|
||||
vips_tile_find( VipsBlockCache *cache, VipsRegion *in, int x, int y )
|
||||
vips_tile_find( VipsBlockCache *cache, int x, int y )
|
||||
{
|
||||
VipsTile *tile;
|
||||
VipsTileSearch search;
|
||||
@ -271,8 +312,7 @@ vips_tile_find( VipsBlockCache *cache, VipsRegion *in, int x, int y )
|
||||
*/
|
||||
if( cache->max_tiles == -1 ||
|
||||
cache->ntiles < cache->max_tiles ) {
|
||||
if( !(tile = vips_tile_new( cache, x, y )) ||
|
||||
vips_tile_fill( tile, in ) )
|
||||
if( !(tile = vips_tile_new( cache, x, y )) )
|
||||
return( NULL );
|
||||
|
||||
return( tile );
|
||||
@ -301,22 +341,44 @@ vips_tile_find( VipsBlockCache *cache, VipsRegion *in, int x, int y )
|
||||
g_assert( 0 );
|
||||
}
|
||||
|
||||
g_assert( tile );
|
||||
if( !tile ) {
|
||||
/* There are no tiles we can reuse -- we have to make another
|
||||
* for now. They will get culled down again next time around.
|
||||
*/
|
||||
if( !(tile = vips_tile_new( cache, x, y )) )
|
||||
return( NULL );
|
||||
|
||||
return( tile );
|
||||
}
|
||||
|
||||
VIPS_DEBUG_MSG( "tilecache: reusing tile %d x %d\n",
|
||||
tile->pos.left, tile->pos.top );
|
||||
|
||||
if( vips_tile_move( tile, x, y ) ||
|
||||
vips_tile_fill( tile, in ) )
|
||||
if( vips_tile_move( tile, x, y ) )
|
||||
return( NULL );
|
||||
|
||||
return( tile );
|
||||
}
|
||||
|
||||
static gboolean
|
||||
vips_tile_unlocked( gpointer key, gpointer value, gpointer user_data )
|
||||
{
|
||||
VipsTile *tile = (VipsTile *) value;
|
||||
|
||||
return( !tile->ref_count );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_block_cache_minimise( VipsImage *image, VipsBlockCache *cache )
|
||||
{
|
||||
vips_block_cache_drop_all( cache );
|
||||
/* We can't drop tiles that are in use.
|
||||
*/
|
||||
g_mutex_lock( cache->lock );
|
||||
|
||||
g_hash_table_foreach_remove( cache->tiles,
|
||||
vips_tile_unlocked, NULL );
|
||||
|
||||
g_mutex_unlock( cache->lock );
|
||||
}
|
||||
|
||||
static int
|
||||
@ -342,6 +404,7 @@ vips_block_cache_class_init( VipsBlockCacheClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class );
|
||||
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_block_cache_class_init\n" );
|
||||
|
||||
@ -353,20 +416,29 @@ vips_block_cache_class_init( VipsBlockCacheClass *class )
|
||||
vobject_class->description = _( "cache an image" );
|
||||
vobject_class->build = vips_block_cache_build;
|
||||
|
||||
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
|
||||
|
||||
VIPS_ARG_IMAGE( class, "in", 1,
|
||||
_( "Input" ),
|
||||
_( "Input image" ),
|
||||
VIPS_ARGUMENT_REQUIRED_INPUT,
|
||||
G_STRUCT_OFFSET( VipsBlockCache, in ) );
|
||||
|
||||
VIPS_ARG_INT( class, "tile_height", 3,
|
||||
VIPS_ARG_INT( class, "tile_height", 4,
|
||||
_( "Tile height" ),
|
||||
_( "Tile height in pixels" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsBlockCache, tile_height ),
|
||||
1, 1000000, 128 );
|
||||
|
||||
VIPS_ARG_ENUM( class, "strategy", 3,
|
||||
VIPS_ARG_BOOL( class, "threaded", 7,
|
||||
_( "Threaded" ),
|
||||
_( "Allow threaded access" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsBlockCache, threaded ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_ENUM( class, "strategy", 6,
|
||||
_( "Strategy" ),
|
||||
_( "Expected access pattern" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
@ -413,10 +485,12 @@ vips_block_cache_init( VipsBlockCache *cache )
|
||||
cache->tile_height = 128;
|
||||
cache->max_tiles = 1000;
|
||||
cache->strategy = VIPS_CACHE_RANDOM;
|
||||
cache->threaded = FALSE;
|
||||
|
||||
cache->time = 0;
|
||||
cache->ntiles = 0;
|
||||
cache->lock = g_mutex_new();
|
||||
cache->new_tile = g_cond_new();
|
||||
cache->tiles = g_hash_table_new_full(
|
||||
(GHashFunc) vips_rect_hash,
|
||||
(GEqualFunc) vips_rect_equal,
|
||||
@ -433,6 +507,73 @@ typedef VipsBlockCacheClass VipsTileCacheClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsTileCache, vips_tile_cache, VIPS_TYPE_BLOCK_CACHE );
|
||||
|
||||
static void
|
||||
vips_tile_cache_unref( GSList *work )
|
||||
{
|
||||
GSList *p;
|
||||
|
||||
for( p = work; p; p = p->next ) {
|
||||
VipsTile *tile = (VipsTile *) p->data;
|
||||
|
||||
tile->ref_count -= 1;
|
||||
}
|
||||
|
||||
g_slist_free( work );
|
||||
}
|
||||
|
||||
/* Make a set of work tiles.
|
||||
*/
|
||||
static GSList *
|
||||
vips_tile_cache_ref( VipsBlockCache *cache, VipsRect *r )
|
||||
{
|
||||
const int tw = cache->tile_width;
|
||||
const int th = cache->tile_height;
|
||||
|
||||
/* Find top left of tiles we need.
|
||||
*/
|
||||
const int xs = (r->left / tw) * tw;
|
||||
const int ys = (r->top / th) * th;
|
||||
|
||||
GSList *work;
|
||||
VipsTile *tile;
|
||||
int x, y;
|
||||
|
||||
/* Ref all the tiles we will need.
|
||||
*/
|
||||
work = NULL;
|
||||
for( y = ys; y < VIPS_RECT_BOTTOM( r ); y += th )
|
||||
for( x = xs; x < VIPS_RECT_RIGHT( r ); x += tw ) {
|
||||
if( !(tile = vips_tile_find( cache, x, y )) ) {
|
||||
vips_tile_cache_unref( work );
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
tile->ref_count += 1;
|
||||
|
||||
/* We must append, since we want to keep tile ordering
|
||||
* for sequential sources.
|
||||
*/
|
||||
work = g_slist_append( work, tile );
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_tile_cache_gen: "
|
||||
"tile %d, %d (%p)\n", x, y, tile );
|
||||
}
|
||||
|
||||
return( work );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_tile_paste( VipsTile *tile, VipsRegion *or )
|
||||
{
|
||||
VipsRect hit;
|
||||
|
||||
/* The part of the tile that we need.
|
||||
*/
|
||||
vips_rect_intersectrect( &or->valid, &tile->pos, &hit );
|
||||
if( !vips_rect_isempty( &hit ) )
|
||||
vips_region_copy( tile->region, or, &hit, hit.left, hit.top );
|
||||
}
|
||||
|
||||
/* Also called from vips_line_cache_gen(), beware.
|
||||
*/
|
||||
static int
|
||||
@ -440,58 +581,103 @@ vips_tile_cache_gen( VipsRegion *or,
|
||||
void *seq, void *a, void *b, gboolean *stop )
|
||||
{
|
||||
VipsRegion *in = (VipsRegion *) seq;
|
||||
VipsBlockCache *block_cache = (VipsBlockCache *) b;
|
||||
const int tw = block_cache->tile_width;
|
||||
const int th = block_cache->tile_height;
|
||||
VipsBlockCache *cache = (VipsBlockCache *) b;
|
||||
VipsRect *r = &or->valid;
|
||||
|
||||
/* Find top left of tiles we need.
|
||||
*/
|
||||
int xs = (r->left / tw) * tw;
|
||||
int ys = (r->top / th) * th;
|
||||
VipsTile *tile;
|
||||
GSList *work;
|
||||
GSList *p;
|
||||
|
||||
int x, y;
|
||||
|
||||
g_mutex_lock( block_cache->lock );
|
||||
|
||||
/* If the output region fits within a tile, we could save a copy by
|
||||
* routing the output region directly to the tile.
|
||||
*
|
||||
* However this would mean that tile drop on minimise could then leave
|
||||
* dangling pointers, if minimise were called on an active pipeline.
|
||||
*/
|
||||
g_mutex_lock( cache->lock );
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_tile_cache_gen: "
|
||||
"left = %d, top = %d, width = %d, height = %d\n",
|
||||
r->left, r->top, r->width, r->height );
|
||||
|
||||
for( y = ys; y < VIPS_RECT_BOTTOM( r ); y += th )
|
||||
for( x = xs; x < VIPS_RECT_RIGHT( r ); x += tw ) {
|
||||
VipsTile *tile;
|
||||
VipsRect tarea;
|
||||
VipsRect hit;
|
||||
/* Ref all the tiles we will need.
|
||||
*/
|
||||
work = vips_tile_cache_ref( cache, r );
|
||||
|
||||
if( !(tile = vips_tile_find( block_cache,
|
||||
in, x, y )) ) {
|
||||
g_mutex_unlock( block_cache->lock );
|
||||
return( -1 );
|
||||
while( work ) {
|
||||
/* Search for data tiles: easy, we can just paste those in.
|
||||
*/
|
||||
for(;;) {
|
||||
for( p = work; p; p = p->next ) {
|
||||
tile = (VipsTile *) p->data;
|
||||
|
||||
if( tile->state == VIPS_TILE_STATE_DATA )
|
||||
break;
|
||||
}
|
||||
|
||||
/* The area of the tile.
|
||||
*/
|
||||
tarea.left = x;
|
||||
tarea.top = y;
|
||||
tarea.width = tw;
|
||||
tarea.height = th;
|
||||
if( !p )
|
||||
break;
|
||||
|
||||
/* The part of the tile that we need.
|
||||
VIPS_DEBUG_MSG( "vips_tile_cache_gen: pasting %p\n",
|
||||
tile );
|
||||
|
||||
vips_tile_paste( tile, or );
|
||||
|
||||
/* We're done with this tile.
|
||||
*/
|
||||
vips_rect_intersectrect( &tarea, r, &hit );
|
||||
vips_region_copy( tile->region, or, &hit,
|
||||
hit.left, hit.top );
|
||||
work = g_slist_remove( work, tile );
|
||||
tile->ref_count -= 1;
|
||||
}
|
||||
|
||||
g_mutex_unlock( block_cache->lock );
|
||||
/* Calculate the first PEND tile we find on the work list. We
|
||||
* don't calculate all PEND tiles since after the first, more
|
||||
* DATA tiles might heve been made available by other threads
|
||||
* and we want to get them out of the way as soon as we can.
|
||||
*/
|
||||
for( p = work; p; p = p->next ) {
|
||||
tile = (VipsTile *) p->data;
|
||||
|
||||
if( tile->state == VIPS_TILE_STATE_PEND ) {
|
||||
tile->state = VIPS_TILE_STATE_CALC;
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_tile_cache_gen: "
|
||||
"calc of %p\n", tile );
|
||||
|
||||
/* In threaded mode, we let other threads run
|
||||
* while we calc this tile. In non-threaded
|
||||
* mode, we keep the lock and make 'em wait.
|
||||
*/
|
||||
if( cache->threaded )
|
||||
g_mutex_unlock( cache->lock );
|
||||
|
||||
if( vips_tile_fill( tile, in ) ) {
|
||||
tile->state = VIPS_TILE_STATE_PEND;
|
||||
vips_tile_cache_unref( work );
|
||||
if( !cache->threaded )
|
||||
g_mutex_lock( cache->lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( cache->threaded )
|
||||
g_mutex_lock( cache->lock );
|
||||
|
||||
/* Let everyone know there's a new DATA tile.
|
||||
* They need to all check their work lists.
|
||||
*/
|
||||
g_cond_broadcast( cache->new_tile );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* There are no PEND or DATA tiles, but there's srill work to
|
||||
* do. We must block until the CALC tiles we need are done.
|
||||
*/
|
||||
if( !p &&
|
||||
work ) {
|
||||
VIPS_DEBUG_MSG( "vips_tile_cache_gen: waiting\n" );
|
||||
|
||||
g_cond_wait( cache->new_tile, cache->lock );
|
||||
|
||||
VIPS_DEBUG_MSG( "vips_tile_cache_gen: awake!\n" );
|
||||
}
|
||||
}
|
||||
|
||||
g_mutex_unlock( cache->lock );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -547,7 +733,7 @@ vips_tile_cache_class_init( VipsTileCacheClass *class )
|
||||
G_STRUCT_OFFSET( VipsBlockCache, tile_width ),
|
||||
1, 1000000, 128 );
|
||||
|
||||
VIPS_ARG_INT( class, "max_tiles", 3,
|
||||
VIPS_ARG_INT( class, "max_tiles", 5,
|
||||
_( "Max tiles" ),
|
||||
_( "Maximum number of tiles to cache" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
@ -573,6 +759,7 @@ vips_tile_cache_init( VipsTileCache *cache )
|
||||
* @tile_height: height of tiles in cache
|
||||
* @max_tiles: maximum number of tiles to cache
|
||||
* @strategy: hint expected access pattern #VipsCacheStrategy
|
||||
* @threaded: allow many threads
|
||||
*
|
||||
* This operation behaves rather like vips_copy() between images
|
||||
* @in and @out, except that it keeps a cache of computed pixels.
|
||||
@ -590,9 +777,9 @@ vips_tile_cache_init( VipsTileCache *cache )
|
||||
* By default, @tile_width and @tile_height are 128 pixels, and the operation
|
||||
* will cache up to 1,000 tiles. @strategy defaults to #VIPS_CACHE_RANDOM.
|
||||
*
|
||||
* This is a lower-level operation than vips_image_cache() since it does no
|
||||
* subdivision and it single-threads its callee. It is suitable for caching
|
||||
* the output of operations like exr2vips() on tiled images.
|
||||
* Normally, only a single thread at once is allowed to calculate tiles. If
|
||||
* you set @threaded to %TRUE, vips_tilecache() will allow many threads to
|
||||
* calculate tiles at once, and share the cache between them.
|
||||
*
|
||||
* See also: vips_cache(), vips_linecache().
|
||||
*
|
||||
@ -723,6 +910,7 @@ vips_line_cache_init( VipsLineCache *cache )
|
||||
*
|
||||
* @strategy: hint expected access pattern #VipsCacheStrategy
|
||||
* @tile_height: height of tiles in cache
|
||||
* @threaded: allow many threads
|
||||
*
|
||||
* This operation behaves rather like vips_copy() between images
|
||||
* @in and @out, except that it keeps a cache of computed pixels.
|
||||
@ -740,9 +928,9 @@ vips_line_cache_init( VipsLineCache *cache )
|
||||
* @tile_height can be used to set the size of the strips that
|
||||
* vips_linecache() uses. The default is 1 (a single scanline).
|
||||
*
|
||||
* This is a lower-level operation than vips_image_cache() since it does no
|
||||
* subdivision and it single-threads its callee. It is suitable for caching
|
||||
* the output of operations like png load.
|
||||
* Normally, only a single thread at once is allowed to calculate tiles. If
|
||||
* you set @threaded to %TRUE, vips_tilecache() will allow many threads to
|
||||
* calculate tiles at once, and share the cache between them.
|
||||
*
|
||||
* See also: vips_cache(), vips_tilecache().
|
||||
*
|
||||
|
@ -83,6 +83,7 @@ tiff2vips( const char *name, IMAGE *out, gboolean header_only )
|
||||
* malloc if all we are doing is looking at fields.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_TIFF
|
||||
if( !header_only &&
|
||||
!seq &&
|
||||
!vips__istifftiled( filename ) &&
|
||||
@ -91,7 +92,6 @@ tiff2vips( const char *name, IMAGE *out, gboolean header_only )
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
#ifdef HAVE_TIFF
|
||||
if( header_only ) {
|
||||
if( vips__tiff_read_header( filename, out, page ) )
|
||||
return( -1 );
|
||||
|
@ -17,6 +17,15 @@
|
||||
* - reorganise the directory structure
|
||||
* - rename to basename and tile_size
|
||||
* - deprecate tile_width/_height and dirname
|
||||
* 1/10/12
|
||||
* - did not write low pyramid layers for images with an odd number of
|
||||
* scan lines (thanks Martin)
|
||||
* 2/10/12
|
||||
* - remove filename options from format string in .dzi (thanks Martin)
|
||||
* 3/10/12
|
||||
* - add zoomify and google maps output
|
||||
* 10/10/12
|
||||
* - add @background option
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -46,10 +55,10 @@
|
||||
*/
|
||||
|
||||
/*
|
||||
*/
|
||||
#define DEBUG_VERBOSE
|
||||
#define DEBUG
|
||||
#define VIPS_DEBUG
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
@ -61,6 +70,15 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
#include <vips/internal.h>
|
||||
|
||||
/* Round N down to P boundary.
|
||||
*/
|
||||
#define ROUND_DOWN(N,P) ((N) - ((N) % P))
|
||||
|
||||
/* Round N up to P boundary.
|
||||
*/
|
||||
#define ROUND_UP(N,P) (ROUND_DOWN( (N) + (P) - 1, (P) ))
|
||||
|
||||
typedef struct _VipsForeignSaveDz VipsForeignSaveDz;
|
||||
typedef struct _Layer Layer;
|
||||
@ -77,6 +95,12 @@ struct _Layer {
|
||||
int width;
|
||||
int height;
|
||||
|
||||
/* Number of tiles across and down in this layer. Zoomify needs this
|
||||
* to calculate the directory to put each tile in.
|
||||
*/
|
||||
int tiles_across;
|
||||
int tiles_down;
|
||||
|
||||
VipsImage *image; /* The image we build */
|
||||
|
||||
/* The top of this strip of tiles, excluding the overlap. Go up from
|
||||
@ -108,8 +132,14 @@ struct _VipsForeignSaveDz {
|
||||
char *suffix;
|
||||
int overlap;
|
||||
int tile_size;
|
||||
VipsForeignDzLayout layout;
|
||||
VipsArea *background;
|
||||
|
||||
Layer *layer; /* x2 shrink pyr layer */
|
||||
|
||||
/* Count zoomify tiles we write.
|
||||
*/
|
||||
int tile_count;
|
||||
};
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveDzClass;
|
||||
@ -148,11 +178,15 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int width, int height )
|
||||
Layer *layer = VIPS_NEW( dz, Layer );
|
||||
|
||||
VipsRect strip;
|
||||
int limit;
|
||||
|
||||
layer->dz = dz;
|
||||
layer->width = width;
|
||||
layer->height = height;
|
||||
|
||||
layer->tiles_across = ROUND_UP( width, dz->tile_size ) / dz->tile_size;
|
||||
layer->tiles_down = ROUND_UP( height, dz->tile_size ) / dz->tile_size;
|
||||
|
||||
layer->image = NULL;
|
||||
layer->strip = NULL;
|
||||
layer->copy = NULL;
|
||||
@ -204,8 +238,16 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int width, int height )
|
||||
return( NULL );
|
||||
}
|
||||
|
||||
if( width > 1 ||
|
||||
height > 1 ) {
|
||||
/* DeepZoom stops at 1x1 pixels, others when the image fits within a
|
||||
* tile.
|
||||
*/
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_DZ )
|
||||
limit = 1;
|
||||
else
|
||||
limit = dz->tile_size;
|
||||
|
||||
if( width > limit ||
|
||||
height > limit ) {
|
||||
/* Round up, so eg. a 5 pixel wide image becomes 3 a layer
|
||||
* down.
|
||||
*/
|
||||
@ -219,24 +261,34 @@ pyramid_build( VipsForeignSaveDz *dz, Layer *above, int width, int height )
|
||||
else
|
||||
layer->n = 0;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "pyramid_build:\n" );
|
||||
printf( "\tn = %d\n", layer->n );
|
||||
printf( "\twidth = %d, height = %d\n", width, height );
|
||||
printf( "\tXsize = %d, Ysize = %d\n",
|
||||
layer->image->Xsize, layer->image->Ysize );
|
||||
#endif
|
||||
|
||||
return( layer );
|
||||
}
|
||||
|
||||
static int
|
||||
pyramid_mkdir( VipsForeignSaveDz *dz )
|
||||
{
|
||||
Layer *layer;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if( vips_existsf( "%s_files", dz->basename ) ) {
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_DZ )
|
||||
vips_snprintf( buf, PATH_MAX, "%s_files", dz->basename );
|
||||
else
|
||||
vips_snprintf( buf, PATH_MAX, "%s", dz->basename );
|
||||
|
||||
if( vips_existsf( "%s", buf ) ) {
|
||||
vips_error( "dzsave",
|
||||
_( "Directory \"%s_files\" exists" ), dz->basename );
|
||||
_( "Directory \"%s\" exists" ), buf );
|
||||
return( -1 );
|
||||
}
|
||||
if( vips_mkdirf( "%s_files", dz->basename ) )
|
||||
if( vips_mkdirf( "%s", buf ) )
|
||||
return( -1 );
|
||||
for( layer = dz->layer; layer; layer = layer->below )
|
||||
if( vips_mkdirf( "%s_files/%d", dz->basename, layer->n ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -246,15 +298,20 @@ write_dzi( VipsForeignSaveDz *dz )
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[PATH_MAX];
|
||||
char *p;
|
||||
|
||||
vips_snprintf( buf, PATH_MAX, "%s.dzi", dz->basename );
|
||||
if( !(fp = vips__file_open_write( buf, TRUE )) )
|
||||
return( -1 );
|
||||
|
||||
vips_snprintf( buf, PATH_MAX, "%s", dz->suffix + 1 );
|
||||
if( (p = (char *) vips__find_rightmost_brackets( buf )) )
|
||||
*p = '\0';
|
||||
|
||||
fprintf( fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" );
|
||||
fprintf( fp, "<Image "
|
||||
"xmlns=\"http://schemas.microsoft.com/deepzoom/2008\"\n" );
|
||||
fprintf( fp, " Format=\"%s\"\n", dz->suffix + 1 );
|
||||
fprintf( fp, " Format=\"%s\"\n", buf );
|
||||
fprintf( fp, " Overlap=\"%d\"\n", dz->overlap );
|
||||
fprintf( fp, " TileSize=\"%d\"\n", dz->tile_size );
|
||||
fprintf( fp, " >\n" );
|
||||
@ -269,6 +326,49 @@ write_dzi( VipsForeignSaveDz *dz )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
write_properties( VipsForeignSaveDz *dz )
|
||||
{
|
||||
FILE *fp;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
vips_snprintf( buf, PATH_MAX, "%s/ImageProperties.xml", dz->basename );
|
||||
if( !(fp = vips__file_open_write( buf, TRUE )) )
|
||||
return( -1 );
|
||||
|
||||
fprintf( fp, "<IMAGE_PROPERTIES "
|
||||
"WIDTH=\"%d\" HEIGHT=\"%d\" NUMTILES=\"%d\" "
|
||||
"NUMIMAGES=\"1\" VERSION=\"1.8\" TILESIZE=\"%d\" />\n",
|
||||
dz->layer->width,
|
||||
dz->layer->height,
|
||||
dz->tile_count,
|
||||
dz->tile_size );
|
||||
|
||||
fclose( fp );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
write_blank( VipsForeignSaveDz *dz )
|
||||
{
|
||||
char buf[PATH_MAX];
|
||||
VipsImage *black;
|
||||
|
||||
vips_snprintf( buf, PATH_MAX, "%s/blank.png", dz->basename );
|
||||
if( vips_black( &black, dz->tile_size, dz->tile_size,
|
||||
"bands", 3,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
if( vips_image_write_to_file( black, buf ) ) {
|
||||
g_object_unref( black );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( black );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Generate area @target in @to using pixels in @from. VIPS_CODING_LABQ only.
|
||||
*/
|
||||
static void
|
||||
@ -481,6 +581,104 @@ strip_allocate( VipsThreadState *state, void *a, gboolean *stop )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/* Create a tile name in the current layout.
|
||||
*/
|
||||
static int
|
||||
tile_name( Layer *layer, char *buf, int x, int y )
|
||||
{
|
||||
VipsForeignSaveDz *dz = layer->dz;
|
||||
|
||||
char dirname[PATH_MAX];
|
||||
char dirname2[PATH_MAX];
|
||||
Layer *p;
|
||||
int n;
|
||||
|
||||
/* We have to lock or there's a race between exists() and mkdir().
|
||||
*/
|
||||
g_mutex_lock( vips__global_lock );
|
||||
|
||||
switch( dz->layout ) {
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_DZ:
|
||||
vips_snprintf( dirname, PATH_MAX, "%s_files/%d",
|
||||
dz->basename, layer->n );
|
||||
vips_snprintf( buf, PATH_MAX, "%s/%d_%d%s",
|
||||
dirname, x, y, dz->suffix );
|
||||
|
||||
if( !vips_existsf( "%s", dirname ) &&
|
||||
vips_mkdirf( "%s", dirname ) ) {
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY:
|
||||
/* We need to work out the tile number so we can calculate the
|
||||
* directory to put this tile in.
|
||||
*
|
||||
* Tiles are numbered from 0 for the most-zoomed-out tile.
|
||||
*/
|
||||
n = 0;
|
||||
|
||||
/* Count all tiles in layers below this one.
|
||||
*/
|
||||
for( p = layer->below; p; p = p->below )
|
||||
n += p->tiles_across * p->tiles_down;
|
||||
|
||||
/* And count tiles so far in this layer.
|
||||
*/
|
||||
n += y * layer->tiles_across + x;
|
||||
|
||||
vips_snprintf( dirname, PATH_MAX, "%s/TileGroup%d",
|
||||
dz->basename, n / 256 );
|
||||
vips_snprintf( buf, PATH_MAX, "%s/%d-%d-%d%s",
|
||||
dirname, layer->n, x, y, dz->suffix );
|
||||
|
||||
/* Used at the end in ImageProperties.xml
|
||||
*/
|
||||
dz->tile_count += 1;
|
||||
|
||||
if( !vips_existsf( "%s", dirname ) &&
|
||||
vips_mkdirf( "%s", dirname ) ) {
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_GOOGLE:
|
||||
vips_snprintf( dirname, PATH_MAX, "%s/%d",
|
||||
dz->basename, layer->n );
|
||||
vips_snprintf( dirname2, PATH_MAX, "%s/%d",
|
||||
dirname, y );
|
||||
vips_snprintf( buf, PATH_MAX, "%s/%d%s",
|
||||
dirname2, x, dz->suffix );
|
||||
|
||||
if( !vips_existsf( "%s", dirname ) &&
|
||||
vips_mkdirf( "%s", dirname ) ) {
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( !vips_existsf( "%s", dirname2 ) &&
|
||||
vips_mkdirf( "%s", dirname2 ) ) {
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert( 0 );
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
strip_work( VipsThreadState *state, void *a )
|
||||
{
|
||||
@ -488,29 +686,43 @@ strip_work( VipsThreadState *state, void *a )
|
||||
Layer *layer = strip->layer;
|
||||
VipsForeignSaveDz *dz = layer->dz;
|
||||
|
||||
VipsImage *extr;
|
||||
char str[1000];
|
||||
VipsBuf buf = VIPS_BUF_STATIC( str );
|
||||
VipsImage *x;
|
||||
char buf[PATH_MAX];
|
||||
|
||||
if( tile_name( layer, buf,
|
||||
state->x / dz->tile_size, state->y / dz->tile_size ) )
|
||||
return( -1 );
|
||||
|
||||
vips_object_sanity( VIPS_OBJECT( strip->image ) );
|
||||
|
||||
/* Extract relative to the strip top-left corner.
|
||||
*/
|
||||
if( vips_extract_area( state->im, &extr,
|
||||
if( vips_extract_area( strip->image, &x,
|
||||
state->pos.left, 0,
|
||||
state->pos.width, state->pos.height, NULL ) )
|
||||
return( -1 );
|
||||
|
||||
vips_buf_appendf( &buf, "%s_files/%d/%d_%d%s",
|
||||
dz->basename, layer->n,
|
||||
state->x / dz->tile_size,
|
||||
state->y / dz->tile_size,
|
||||
dz->suffix );
|
||||
/* Google tiles need to be padded up to tilesize.
|
||||
*/
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE ) {
|
||||
VipsImage *z;
|
||||
|
||||
if( vips_image_write_to_file( extr, vips_buf_all( &buf ) ) ) {
|
||||
g_object_unref( extr );
|
||||
return( -1 );
|
||||
if( vips_embed( x, &z, 0, 0, dz->tile_size, dz->tile_size,
|
||||
"background", dz->background,
|
||||
NULL ) ) {
|
||||
g_object_unref( x );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( x );
|
||||
|
||||
x = z;
|
||||
}
|
||||
|
||||
g_object_unref( extr );
|
||||
if( vips_image_write_to_file( x, buf ) ) {
|
||||
g_object_unref( x );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( x );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
@ -522,6 +734,10 @@ strip_save( Layer *layer )
|
||||
{
|
||||
Strip strip;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "strip_save: n = %d, y = %d\n", layer->n, layer->y );
|
||||
#endif /*DEBUG*/
|
||||
|
||||
strip_init( &strip, layer );
|
||||
if( vips_threadpool_run( strip.image,
|
||||
vips_thread_state_new, strip_allocate, strip_work, NULL,
|
||||
@ -535,9 +751,9 @@ strip_save( Layer *layer )
|
||||
}
|
||||
|
||||
/* A strip has filled, but the rightmost column and the bottom-most row may
|
||||
* not have if we've rounded the size up!
|
||||
* not have been if we've rounded the size up.
|
||||
*
|
||||
* Fill them, if necessary, by copyping the previous row/column.
|
||||
* Fill them, if necessary, by copying the previous row/column.
|
||||
*/
|
||||
static void
|
||||
layer_generate_extras( Layer *layer )
|
||||
@ -729,7 +945,13 @@ pyramid_strip( VipsRegion *region, VipsRect *area, void *a )
|
||||
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) a;
|
||||
Layer *layer = dz->layer;
|
||||
|
||||
#ifdef DEBUG
|
||||
printf( "pyramid_strip: strip at %d, height %d\n",
|
||||
area->top, area->height );
|
||||
#endif/*DEBUG*/
|
||||
|
||||
for(;;) {
|
||||
VipsRect *to = &layer->strip->valid;
|
||||
VipsRect target;
|
||||
|
||||
/* The bit of strip that needs filling.
|
||||
@ -737,9 +959,8 @@ pyramid_strip( VipsRegion *region, VipsRect *area, void *a )
|
||||
target.left = 0;
|
||||
target.top = layer->write_y;
|
||||
target.width = layer->image->Xsize;
|
||||
target.height = layer->strip->valid.height;
|
||||
vips_rect_intersectrect( &target,
|
||||
&layer->strip->valid, &target );
|
||||
target.height = to->height;
|
||||
vips_rect_intersectrect( &target, to, &target );
|
||||
|
||||
/* Clip against what we have available.
|
||||
*/
|
||||
@ -761,12 +982,16 @@ pyramid_strip( VipsRegion *region, VipsRect *area, void *a )
|
||||
|
||||
layer->write_y += target.height;
|
||||
|
||||
/* If we've filled the strip, let it know.
|
||||
/* If we've filled the strip of the layer below, let it know.
|
||||
* We can either fill the region, if it's somewhere half-way
|
||||
* down the image, or, if it's at the bottom, get to the last
|
||||
* writeable line.
|
||||
*/
|
||||
if( layer->write_y ==
|
||||
VIPS_RECT_BOTTOM( &layer->strip->valid ) &&
|
||||
strip_arrived( layer ) )
|
||||
return( -1 );
|
||||
if( layer->write_y == VIPS_RECT_BOTTOM( to ) ||
|
||||
layer->write_y == layer->height ) {
|
||||
if( strip_arrived( layer ) )
|
||||
return( -1 );
|
||||
}
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
@ -778,6 +1003,16 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
VipsForeignSave *save = (VipsForeignSave *) object;
|
||||
VipsForeignSaveDz *dz = (VipsForeignSaveDz *) object;
|
||||
|
||||
/* Google and zoomify default to zero overlap, ".jpg".
|
||||
*/
|
||||
if( dz->layout == VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY ||
|
||||
dz->layout == VIPS_FOREIGN_DZ_LAYOUT_GOOGLE ) {
|
||||
if( !vips_object_get_argument_assigned( object, "overlap" ) )
|
||||
dz->overlap = 0;
|
||||
if( !vips_object_get_argument_assigned( object, "suffix" ) )
|
||||
VIPS_SETSTR( dz->suffix, ".jpg" );
|
||||
}
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_foreign_save_dz_parent_class )->
|
||||
build( object ) )
|
||||
return( -1 );
|
||||
@ -795,13 +1030,33 @@ vips_foreign_save_dz_build( VipsObject *object )
|
||||
if( !(dz->layer = pyramid_build( dz,
|
||||
NULL, save->ready->Xsize, save->ready->Ysize )) )
|
||||
return( -1 );
|
||||
if( pyramid_mkdir( dz ) ||
|
||||
write_dzi( dz ) )
|
||||
if( pyramid_mkdir( dz ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_sink_disc( save->ready, pyramid_strip, dz ) )
|
||||
return( -1 );
|
||||
|
||||
switch( dz->layout ) {
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_DZ:
|
||||
if( write_dzi( dz ) )
|
||||
return( -1 );
|
||||
break;
|
||||
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY:
|
||||
if( write_properties( dz ) )
|
||||
return( -1 );
|
||||
break;
|
||||
|
||||
case VIPS_FOREIGN_DZ_LAYOUT_GOOGLE:
|
||||
if( write_blank( dz ) )
|
||||
return( -1 );
|
||||
break;
|
||||
|
||||
default:
|
||||
g_assert( 0 );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
@ -854,6 +1109,14 @@ vips_foreign_save_dz_class_init( VipsForeignSaveDzClass *class )
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, basename ),
|
||||
NULL );
|
||||
|
||||
VIPS_ARG_ENUM( class, "layout", 8,
|
||||
_( "Layout" ),
|
||||
_( "Directory layout" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, layout ),
|
||||
VIPS_TYPE_FOREIGN_DZ_LAYOUT,
|
||||
VIPS_FOREIGN_DZ_LAYOUT_DZ );
|
||||
|
||||
VIPS_ARG_STRING( class, "suffix", 9,
|
||||
_( "suffix" ),
|
||||
_( "Filename suffix for tiles" ),
|
||||
@ -875,6 +1138,13 @@ vips_foreign_save_dz_class_init( VipsForeignSaveDzClass *class )
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, tile_size ),
|
||||
1, 1024, 256 );
|
||||
|
||||
VIPS_ARG_BOXED( class, "background", 12,
|
||||
_( "Background" ),
|
||||
_( "Colour for background pixels" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveDz, background ),
|
||||
VIPS_TYPE_ARRAY_DOUBLE );
|
||||
|
||||
/* How annoying. We stupidly had these in earlier versions.
|
||||
*/
|
||||
|
||||
@ -905,8 +1175,13 @@ static void
|
||||
vips_foreign_save_dz_init( VipsForeignSaveDz *dz )
|
||||
{
|
||||
VIPS_SETSTR( dz->suffix, ".jpeg" );
|
||||
dz->layout = VIPS_FOREIGN_DZ_LAYOUT_DZ;
|
||||
dz->overlap = 1;
|
||||
dz->tile_size = 256;
|
||||
dz->tile_count = 0;
|
||||
dz->background =
|
||||
vips_area_new_array( G_TYPE_DOUBLE, sizeof( double ), 1 );
|
||||
((double *) (dz->background->data))[0] = 255;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -917,23 +1192,31 @@ vips_foreign_save_dz_init( VipsForeignSaveDz *dz )
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* @suffix: suffix for tile tiles (default ".jpg")
|
||||
* @overlap; set tile overlap (default 1)
|
||||
* @tile_size; set tile size (default 256)
|
||||
* @layout; directory layout convention
|
||||
* @suffix: suffix for tile tiles
|
||||
* @overlap; set tile overlap
|
||||
* @tile_size; set tile size
|
||||
* @background: background colour
|
||||
*
|
||||
* Save an image to a deep zoom - style directory tree. A directory called
|
||||
* Save an image as a set of tiles at various resolutions. By default dzsave
|
||||
* uses DeepZoom layout -- use @layout to pick other conventions.
|
||||
*
|
||||
* In DeepZoom layout a directory called
|
||||
* "@basename_files" is created to hold the tiles, and an XML file called
|
||||
* "@basename.dzi" is written with the image metadata,
|
||||
* "@basename.dzi" is written with the image metadata,
|
||||
*
|
||||
* The image is shrunk in a series of x2 reductions until it fits within a
|
||||
* single pixel. Each layer is written out to a separate subdirectory of
|
||||
* @dirname_files, with directory "0" holding the smallest, single pixel image.
|
||||
*
|
||||
* Each tile is written as a separate file named as "@x_@y@suffix", where @x
|
||||
* and @y are the tile coordinates, with (0, 0) as the top-left tile.
|
||||
* In Zoomify and Google layout, a directory called @basename is created to
|
||||
* hold the tile structure.
|
||||
*
|
||||
* You can set @suffix to something like ".jpg[Q=85]" to set the tile write
|
||||
* options.
|
||||
*
|
||||
* In Google layout mode, edge tiles are expanded to @tile_size by @tile_size
|
||||
* pixels. Normally they are filled with white, but you can set another colour
|
||||
* with @background.
|
||||
*
|
||||
* You can set the size and overlap of tiles with @tile_size and @overlap.
|
||||
* They default to the correct settings for the selected @layout.
|
||||
*
|
||||
* See also: vips_tiffsave().
|
||||
*
|
||||
|
@ -34,6 +34,9 @@
|
||||
* 20/9/12
|
||||
* - update openslide_open error handling for 3.3.0 semantics
|
||||
* - switch from deprecated _layer_ functions
|
||||
* 11/10/12
|
||||
* - look for tile-width and tile-height properties
|
||||
* - use threaded tile cache
|
||||
*/
|
||||
|
||||
/*
|
||||
@ -79,14 +82,9 @@
|
||||
|
||||
#include "openslide2vips.h"
|
||||
|
||||
/* We run our own tile cache. The OpenSlide one can't always keep enough for a
|
||||
* complete lines of pixels.
|
||||
*/
|
||||
#define TILE_WIDTH (256)
|
||||
#define TILE_HEIGHT (256)
|
||||
|
||||
typedef struct {
|
||||
openslide_t *osr;
|
||||
|
||||
char *associated;
|
||||
|
||||
/* Only valid if associated == NULL.
|
||||
@ -94,6 +92,11 @@ typedef struct {
|
||||
int32_t level;
|
||||
double downsample;
|
||||
uint32_t bg;
|
||||
|
||||
/* Try to get these from openslide properties.
|
||||
*/
|
||||
int tile_width;
|
||||
int tile_height;
|
||||
} ReadSlide;
|
||||
|
||||
int
|
||||
@ -117,6 +120,7 @@ vips__openslide_isslide( const char *filename )
|
||||
if( vendor &&
|
||||
strcmp( vendor, "generic-tiff" ) != 0 )
|
||||
ok = 1;
|
||||
|
||||
openslide_close( osr );
|
||||
}
|
||||
|
||||
@ -173,6 +177,11 @@ readslide_new( const char *filename, VipsImage *out,
|
||||
rslide->level = level;
|
||||
rslide->associated = g_strdup( associated );
|
||||
|
||||
/* Non-crazy defaults, override below if we can.
|
||||
*/
|
||||
rslide->tile_width = 256;
|
||||
rslide->tile_height = 256;
|
||||
|
||||
rslide->osr = openslide_open( filename );
|
||||
if( rslide->osr == NULL ) {
|
||||
vips_error( "openslide2vips",
|
||||
@ -206,12 +215,29 @@ readslide_new( const char *filename, VipsImage *out,
|
||||
vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
|
||||
}
|
||||
else {
|
||||
char buf[256];
|
||||
const char *value;
|
||||
|
||||
openslide_get_level_dimensions( rslide->osr,
|
||||
level, &w, &h );
|
||||
rslide->downsample = openslide_get_level_downsample(
|
||||
rslide->osr, level );
|
||||
vips_image_set_int( out, "slide-level", level );
|
||||
vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );
|
||||
|
||||
/* Try to get tile width/height. An undocumented, experimental
|
||||
* feature.
|
||||
*/
|
||||
vips_snprintf( buf, 256,
|
||||
"openslide.level[%d].tile-width", level );
|
||||
if( (value = openslide_get_property_value( rslide->osr, buf )) )
|
||||
rslide->tile_width = atoi( value );
|
||||
vips_snprintf( buf, 256,
|
||||
"openslide.level[%d].tile-height", level );
|
||||
if( (value = openslide_get_property_value( rslide->osr, buf )) )
|
||||
rslide->tile_height = atoi( value );
|
||||
if( value )
|
||||
VIPS_DEBUG_MSG( "readslide_new: found tile-size\n" );
|
||||
}
|
||||
|
||||
rslide->bg = 0xffffff;
|
||||
@ -261,7 +287,7 @@ vips__openslide_read_header( const char *filename, VipsImage *out,
|
||||
|
||||
static int
|
||||
vips__openslide_generate( VipsRegion *out,
|
||||
void *seq, void *_rslide, void *unused, gboolean *stop )
|
||||
void *_seq, void *_rslide, void *unused, gboolean *stop )
|
||||
{
|
||||
ReadSlide *rslide = _rslide;
|
||||
uint32_t bg = rslide->bg;
|
||||
@ -275,13 +301,18 @@ vips__openslide_generate( VipsRegion *out,
|
||||
VIPS_DEBUG_MSG( "vips__openslide_generate: %dx%d @ %dx%d\n",
|
||||
r->width, r->height, r->left, r->top );
|
||||
|
||||
/* We're inside a cache, so requests should always be TILE_WIDTH by
|
||||
* TILE_HEIGHT pixels and on a tile boundary.
|
||||
/* We're inside a cache, so requests should always be
|
||||
* tile_width by tile_height pixels and on a tile boundary.
|
||||
*/
|
||||
g_assert( (r->left % TILE_WIDTH) == 0 );
|
||||
g_assert( (r->height % TILE_HEIGHT) == 0 );
|
||||
g_assert( r->width <= TILE_WIDTH );
|
||||
g_assert( r->height <= TILE_HEIGHT );
|
||||
g_assert( (r->left % rslide->tile_width) == 0 );
|
||||
g_assert( (r->top % rslide->tile_height) == 0 );
|
||||
g_assert( r->width <= rslide->tile_width );
|
||||
g_assert( r->height <= rslide->tile_height );
|
||||
|
||||
/* The memory on the region should be contiguous for our ARGB->RGBA
|
||||
* loop below.
|
||||
*/
|
||||
g_assert( VIPS_REGION_LSKIP( out ) == r->width * 4 );
|
||||
|
||||
openslide_read_region( rslide->osr,
|
||||
buf,
|
||||
@ -298,12 +329,14 @@ vips__openslide_generate( VipsRegion *out,
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
/* Convert from ARGB to RGBA and undo premultiplication.
|
||||
/* Convert from ARGB to RGBA and undo premultiplication. Since we are
|
||||
* inside a cache, we know buf must be continuous.
|
||||
*/
|
||||
for( i = 0; i < n; i++ ) {
|
||||
uint32_t x = buf[i];
|
||||
uint32_t *p = buf + i;
|
||||
uint32_t x = *p;
|
||||
uint8_t a = x >> 24;
|
||||
VipsPel *out = (VipsPel *) (buf + i);
|
||||
VipsPel *out = (VipsPel *) p;
|
||||
|
||||
if( a != 0 ) {
|
||||
out[0] = 255 * ((x >> 16) & 255) / a;
|
||||
@ -334,10 +367,6 @@ vips__openslide_read( const char *filename, VipsImage *out, int level )
|
||||
VIPS_DEBUG_MSG( "vips__openslide_read: %s %d\n",
|
||||
filename, level );
|
||||
|
||||
/* Tile cache: keep enough for two complete rows of tiles. OpenSlide
|
||||
* has its own tile cache, but it's not large enough for a complete
|
||||
* scan line.
|
||||
*/
|
||||
raw = vips_image_new();
|
||||
vips_object_local( out, raw );
|
||||
|
||||
@ -352,9 +381,11 @@ vips__openslide_read( const char *filename, VipsImage *out, int level )
|
||||
* 50%.
|
||||
*/
|
||||
if( vips_tilecache( raw, &t,
|
||||
"tile_width", TILE_WIDTH,
|
||||
"tile_height", TILE_WIDTH,
|
||||
"max_tiles", (int) (1.5 * (1 + raw->Xsize / TILE_WIDTH)),
|
||||
"tile_width", rslide->tile_width,
|
||||
"tile_height", rslide->tile_height,
|
||||
"max_tiles",
|
||||
(int) (1.5 * (1 + raw->Xsize / rslide->tile_width)),
|
||||
"threaded", TRUE,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
if( vips_image_write( t, out ) ) {
|
||||
|
@ -44,6 +44,7 @@ extern "C" {
|
||||
* @VIPS_EXTEND_REPEAT: repeat the whole image
|
||||
* @VIPS_EXTEND_MIRROR: mirror the whole image
|
||||
* @VIPS_EXTEND_WHITE: extend with white (all bits set) pixels
|
||||
* @VIPS_EXTEND_BACKGROUND: extend with colour from the @background property
|
||||
*
|
||||
* See vips_embed(), vips_conv(), vips_affine() and so on.
|
||||
*
|
||||
@ -61,6 +62,8 @@ extern "C" {
|
||||
* edges
|
||||
*
|
||||
* #VIPS_EXTEND_WHITE --- new pixels are white, ie. all bits are set
|
||||
|
||||
* #VIPS_EXTEND_BACKGROUND --- colour set from the @background property
|
||||
*
|
||||
* We have to specify the exact value of each enum member since we have to
|
||||
* keep these frozen for back compat with vips7.
|
||||
@ -68,12 +71,13 @@ extern "C" {
|
||||
* See also: vips_embed().
|
||||
*/
|
||||
typedef enum {
|
||||
VIPS_EXTEND_BLACK = 0,
|
||||
VIPS_EXTEND_COPY = 1,
|
||||
VIPS_EXTEND_REPEAT = 2,
|
||||
VIPS_EXTEND_MIRROR = 3,
|
||||
VIPS_EXTEND_WHITE = 4,
|
||||
VIPS_EXTEND_LAST = 5
|
||||
VIPS_EXTEND_BLACK,
|
||||
VIPS_EXTEND_COPY,
|
||||
VIPS_EXTEND_REPEAT,
|
||||
VIPS_EXTEND_MIRROR,
|
||||
VIPS_EXTEND_WHITE,
|
||||
VIPS_EXTEND_BACKGROUND,
|
||||
VIPS_EXTEND_LAST
|
||||
} VipsExtend;
|
||||
|
||||
/**
|
||||
|
@ -17,6 +17,8 @@ GType vips_foreign_tiff_predictor_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_FOREIGN_TIFF_PREDICTOR (vips_foreign_tiff_predictor_get_type())
|
||||
GType vips_foreign_tiff_resunit_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_FOREIGN_TIFF_RESUNIT (vips_foreign_tiff_resunit_get_type())
|
||||
GType vips_foreign_dz_layout_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_FOREIGN_DZ_LAYOUT (vips_foreign_dz_layout_get_type())
|
||||
/* enumerations from "../../../libvips/include/vips/arithmetic.h" */
|
||||
GType vips_operation_math_get_type (void) G_GNUC_CONST;
|
||||
#define VIPS_TYPE_OPERATION_MATH (vips_operation_math_get_type())
|
||||
|
@ -424,6 +424,21 @@ int vips_radload( const char *filename, VipsImage **out, ... )
|
||||
int vips_radsave( VipsImage *in, const char *filename, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
/**
|
||||
* VipsForeignDzLayout:
|
||||
* @VIPS_FOREIGN_DZ_LAYOUT_DZ: use DeepZoom directory layout
|
||||
* @VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY: use Zoomify directory layout
|
||||
* @VIPS_FOREIGN_DZ_LAYOUT_GOOGLE: use Google maps directory layout
|
||||
*
|
||||
* Use inches or centimeters as the resolution unit for a tiff file.
|
||||
*/
|
||||
typedef enum {
|
||||
VIPS_FOREIGN_DZ_LAYOUT_DZ,
|
||||
VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY,
|
||||
VIPS_FOREIGN_DZ_LAYOUT_GOOGLE,
|
||||
VIPS_FOREIGN_DZ_LAYOUT_LAST
|
||||
} VipsForeignDzLayout;
|
||||
|
||||
int vips_dzsave( VipsImage *in, const char *basename, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
|
@ -105,6 +105,25 @@ vips_foreign_tiff_resunit_get_type( void )
|
||||
|
||||
return( etype );
|
||||
}
|
||||
GType
|
||||
vips_foreign_dz_layout_get_type( void )
|
||||
{
|
||||
static GType etype = 0;
|
||||
|
||||
if( etype == 0 ) {
|
||||
static const GEnumValue values[] = {
|
||||
{VIPS_FOREIGN_DZ_LAYOUT_DZ, "VIPS_FOREIGN_DZ_LAYOUT_DZ", "dz"},
|
||||
{VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY, "VIPS_FOREIGN_DZ_LAYOUT_ZOOMIFY", "zoomify"},
|
||||
{VIPS_FOREIGN_DZ_LAYOUT_GOOGLE, "VIPS_FOREIGN_DZ_LAYOUT_GOOGLE", "google"},
|
||||
{VIPS_FOREIGN_DZ_LAYOUT_LAST, "VIPS_FOREIGN_DZ_LAYOUT_LAST", "last"},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
||||
etype = g_enum_register_static( "VipsForeignDzLayout", values );
|
||||
}
|
||||
|
||||
return( etype );
|
||||
}
|
||||
/* enumerations from "../../libvips/include/vips/conversion.h" */
|
||||
GType
|
||||
vips_extend_get_type( void )
|
||||
@ -118,6 +137,7 @@ vips_extend_get_type( void )
|
||||
{VIPS_EXTEND_REPEAT, "VIPS_EXTEND_REPEAT", "repeat"},
|
||||
{VIPS_EXTEND_MIRROR, "VIPS_EXTEND_MIRROR", "mirror"},
|
||||
{VIPS_EXTEND_WHITE, "VIPS_EXTEND_WHITE", "white"},
|
||||
{VIPS_EXTEND_BACKGROUND, "VIPS_EXTEND_BACKGROUND", "background"},
|
||||
{VIPS_EXTEND_LAST, "VIPS_EXTEND_LAST", "last"},
|
||||
{0, NULL, NULL}
|
||||
};
|
||||
|
@ -122,7 +122,7 @@
|
||||
*/
|
||||
#define MAX_IMAGES (1000)
|
||||
|
||||
/* Make a upstream/downstream link. upstream is one of downstream's inputs.
|
||||
/* Make an upstream/downstream link. upstream is one of downstream's inputs.
|
||||
*/
|
||||
static void
|
||||
vips__link_make( VipsImage *image_up, VipsImage *image_down )
|
||||
@ -183,10 +183,10 @@ vips__link_break_all( VipsImage *image )
|
||||
vips_slist_map2( image->downstream,
|
||||
(VipsSListMap2Fn) vips__link_break_rev, image, NULL );
|
||||
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
|
||||
g_assert( !image->upstream );
|
||||
g_assert( !image->downstream );
|
||||
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
}
|
||||
|
||||
typedef struct _LinkMap {
|
||||
@ -264,6 +264,7 @@ vips__link_map( VipsImage *image, gboolean upstream,
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
|
||||
result = vips_slist_map2( images, fn, a, b );
|
||||
|
||||
for( p = images; p; p = p->next )
|
||||
g_object_unref( p->data );
|
||||
g_slist_free( images );
|
||||
|
@ -465,6 +465,10 @@ vips_image_sanity( VipsObject *object, VipsBuf *buf )
|
||||
vips_buf_appends( buf, "bad resolution\n" );
|
||||
}
|
||||
|
||||
/* Must lock around inter-image links.
|
||||
*/
|
||||
g_mutex_lock( vips__global_lock );
|
||||
|
||||
if( vips_slist_map2( image->upstream,
|
||||
(VipsSListMap2Fn) vips_image_sanity_upstream, image, NULL ) )
|
||||
vips_buf_appends( buf, "upstream broken\n" );
|
||||
@ -472,6 +476,8 @@ vips_image_sanity( VipsObject *object, VipsBuf *buf )
|
||||
(VipsSListMap2Fn) vips_image_sanity_downstream, image, NULL ) )
|
||||
vips_buf_appends( buf, "downstream broken\n" );
|
||||
|
||||
g_mutex_unlock( vips__global_lock );
|
||||
|
||||
VIPS_OBJECT_CLASS( vips_image_parent_class )->sanity( object, buf );
|
||||
}
|
||||
|
||||
|
84
po/vips7.pot
84
po/vips7.pot
@ -9,7 +9,11 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: http://bugzilla.gnome.org/enter_bug.cgi?"
|
||||
"product=glib&keywords=I18N+L10N&component=general\n"
|
||||
<<<<<<< HEAD
|
||||
"POT-Creation-Date: 2012-09-08 14:01+0100\n"
|
||||
=======
|
||||
"POT-Creation-Date: 2012-10-01 14:17+0100\n"
|
||||
>>>>>>> origin/master
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -395,7 +399,11 @@ msgstr ""
|
||||
#: ../libvips/conversion/extract.c:197 ../libvips/conversion/extract.c:356
|
||||
#: ../libvips/conversion/bandjoin.c:171 ../libvips/conversion/copy.c:321
|
||||
#: ../libvips/conversion/rot.c:359 ../libvips/conversion/replicate.c:196
|
||||
<<<<<<< HEAD
|
||||
#: ../libvips/conversion/tilecache.c:355 ../libvips/conversion/embed.c:523
|
||||
=======
|
||||
#: ../libvips/conversion/tilecache.c:357 ../libvips/conversion/embed.c:523
|
||||
>>>>>>> origin/master
|
||||
#: ../libvips/conversion/cache.c:100 ../libvips/conversion/recomb.c:203
|
||||
#: ../libvips/conversion/sequential.c:273 ../libvips/foreign/foreign.c:1413
|
||||
#: ../libvips/resample/resample.c:89
|
||||
@ -567,7 +575,11 @@ msgstr ""
|
||||
#: ../libvips/conversion/cast.c:480 ../libvips/conversion/flatten.c:376
|
||||
#: ../libvips/conversion/extract.c:198 ../libvips/conversion/extract.c:357
|
||||
#: ../libvips/conversion/copy.c:322 ../libvips/conversion/rot.c:360
|
||||
<<<<<<< HEAD
|
||||
#: ../libvips/conversion/replicate.c:197 ../libvips/conversion/tilecache.c:356
|
||||
=======
|
||||
#: ../libvips/conversion/replicate.c:197 ../libvips/conversion/tilecache.c:358
|
||||
>>>>>>> origin/master
|
||||
#: ../libvips/conversion/embed.c:524 ../libvips/conversion/cache.c:101
|
||||
#: ../libvips/conversion/sequential.c:274
|
||||
msgid "Input image"
|
||||
@ -1104,6 +1116,7 @@ msgstr ""
|
||||
msgid "Top edge of sub in main"
|
||||
msgstr ""
|
||||
|
||||
<<<<<<< HEAD
|
||||
#: ../libvips/conversion/tilecache.c:351 ../libvips/conversion/cache.c:96
|
||||
msgid "cache an image"
|
||||
msgstr ""
|
||||
@ -1151,6 +1164,55 @@ msgid "Maximum number of tiles to cache"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:699
|
||||
=======
|
||||
#: ../libvips/conversion/tilecache.c:353 ../libvips/conversion/cache.c:96
|
||||
msgid "cache an image"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:363 ../libvips/conversion/cache.c:113
|
||||
#: ../libvips/conversion/sequential.c:286 ../libvips/foreign/tiffsave.c:222
|
||||
#: ../libvips/foreign/dzsave.c:920
|
||||
msgid "Tile height"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:364 ../libvips/conversion/cache.c:114
|
||||
#: ../libvips/conversion/sequential.c:287 ../libvips/foreign/tiffsave.c:223
|
||||
#: ../libvips/foreign/dzsave.c:921
|
||||
msgid "Tile height in pixels"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:370
|
||||
msgid "Strategy"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:371
|
||||
msgid "Expected access pattern"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:540
|
||||
msgid "cache an image as a set of tiles"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:544 ../libvips/conversion/cache.c:106
|
||||
#: ../libvips/foreign/tiffsave.c:215 ../libvips/foreign/dzsave.c:913
|
||||
msgid "Tile width"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:545 ../libvips/conversion/cache.c:107
|
||||
#: ../libvips/foreign/tiffsave.c:216 ../libvips/foreign/dzsave.c:914
|
||||
msgid "Tile width in pixels"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:551 ../libvips/conversion/cache.c:120
|
||||
msgid "Max tiles"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:552 ../libvips/conversion/cache.c:121
|
||||
msgid "Maximum number of tiles to cache"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/conversion/tilecache.c:706
|
||||
>>>>>>> origin/master
|
||||
msgid "cache an image as a set of lines"
|
||||
msgstr ""
|
||||
|
||||
@ -1527,48 +1589,48 @@ msgstr ""
|
||||
msgid "Write a bigtiff image"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:232
|
||||
#: ../libvips/foreign/dzsave.c:243
|
||||
#, c-format
|
||||
msgid "Directory \"%s_files\" exists"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:788
|
||||
#: ../libvips/foreign/dzsave.c:812
|
||||
msgid "overlap must be less than tile width and height"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:841
|
||||
#: ../libvips/foreign/dzsave.c:865
|
||||
msgid "save image to deep zoom format"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:851 ../libvips/foreign/dzsave.c:882
|
||||
#: ../libvips/foreign/dzsave.c:875 ../libvips/foreign/dzsave.c:906
|
||||
msgid "Base name"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:852 ../libvips/foreign/dzsave.c:883
|
||||
#: ../libvips/foreign/dzsave.c:876 ../libvips/foreign/dzsave.c:907
|
||||
msgid "Base name to save to"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:858
|
||||
#: ../libvips/foreign/dzsave.c:882
|
||||
msgid "suffix"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:859
|
||||
#: ../libvips/foreign/dzsave.c:883
|
||||
msgid "Filename suffix for tiles"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:865
|
||||
#: ../libvips/foreign/dzsave.c:889
|
||||
msgid "Overlap"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:866
|
||||
#: ../libvips/foreign/dzsave.c:890
|
||||
msgid "Tile overlap in pixels"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:872
|
||||
#: ../libvips/foreign/dzsave.c:896
|
||||
msgid "Tile size"
|
||||
msgstr ""
|
||||
|
||||
#: ../libvips/foreign/dzsave.c:873
|
||||
#: ../libvips/foreign/dzsave.c:897
|
||||
msgid "Tile size in pixels"
|
||||
msgstr ""
|
||||
|
||||
|
@ -3,7 +3,7 @@ exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: vips-@VIPS_MAJOR_VERSION@.@VIPS_MINOR_VERSION@
|
||||
Name: vips
|
||||
Description: Image processing library
|
||||
Version: @VERSION@
|
||||
Requires: @PACKAGES_USED@
|
||||
|
@ -3,8 +3,8 @@ exec_prefix=@exec_prefix@
|
||||
libdir=@libdir@
|
||||
includedir=@includedir@
|
||||
|
||||
Name: vipsCC-@VIPS_MAJOR_VERSION@.@VIPS_MINOR_VERSION@
|
||||
Name: vipsCC
|
||||
Description: C++ API for vips image processing library
|
||||
Version: @VERSION@
|
||||
Requires: vips-@VIPS_MAJOR_VERSION@.@VIPS_MINOR_VERSION@ = @VERSION@
|
||||
Requires: vips = @VERSION@
|
||||
Libs: -L${libdir} -lvipsCC
|
||||
|
Loading…
Reference in New Issue
Block a user