Beispiel #1
0
/* Copy a small area.
 */
static int
vips_copy_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
{
    VipsRegion *ir = (VipsRegion *) seq;
    VipsRect *r = &or->valid;
    VipsCopy *copy = (VipsCopy *) b;
    SwapFn swap = vips_copy_swap_fn[copy->in->BandFmt];

    /* Ask for input we need.
     */
    if( vips_region_prepare( ir, r ) )
        return( -1 );

    if( copy->swap && swap ) {
        int y;

        for( y = 0; y < r->height; y++ ) {
            VipsPel *p = VIPS_REGION_ADDR( ir,
                                           r->left, r->top + y );
            VipsPel *q = VIPS_REGION_ADDR( or,
                                           r->left, r->top + y );

            swap( p, q, r->width, copy->in );
        }
    }
    else
        /* Nothing to do, just copy with pointers.
         */
        if( vips_region_region( or, ir, r, r->left, r->top ) )
            return( -1 );

    return( 0 );
}
Beispiel #2
0
static int
vips_copy_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
{
	VipsRegion *ir = (VipsRegion *) seq;
	VipsRect *r = &or->valid;

	if( vips_region_prepare( ir, r ) ||
		vips_region_region( or, ir, r, r->left, r->top ) )
		return( -1 );

	return( 0 );
}
Beispiel #3
0
static int
vips_sequential_generate( VipsRegion *or, 
	void *seq, void *a, void *b, gboolean *stop )
{
	VipsSequential *sequential = (VipsSequential *) b;
        VipsRect *r = &or->valid;
	VipsRegion *ir = (VipsRegion *) seq;

	VIPS_DEBUG_MSG( "vips_sequential_generate %d\n", r->top );

	/* The y pos of the request must be the same as our current file
	 * position.
	 */
	if( r->top != sequential->y_pos ) {
		printf( "vips_sequential_generate: error -- "
			"at position %d in file, but position %d requested",
			sequential->y_pos, r->top );

		vips_error( "VipsSequential", 
			_( "non-sequential read --- "
			"at position %d in file, but position %d requested" ),
			sequential->y_pos, r->top );
		return( -1 );
	}

	/* We're inside a tilecache where tiles are the full image width, so
	 * this should always be true.
	 */
	g_assert( r->left == 0 );
	g_assert( r->width == or->im->Xsize );
	g_assert( VIPS_RECT_BOTTOM( r ) <= or->im->Ysize );

	/* Pointer copy.
	 */
        if( vips_region_prepare( ir, r ) ||
		vips_region_region( or, ir, r, r->left, r->top ) )
                return( -1 );

	sequential->y_pos += r->height;

	return( 0 );
}
Beispiel #4
0
/* Trivial case: we just need pels from one of the inputs.
 */
static int
vips_insert_just_one( VipsRegion *or, VipsRegion *ir, int x, int y )
{
	VipsRect need;

	/* Find the part of pos we need.
	 */
	need = or->valid;
	need.left -= x;
	need.top -= y;
	if( vips_region_prepare( ir, &need ) )
		return( -1 );

	/* Attach our output to it.
	 */
	if( vips_region_region( or, ir, &or->valid, need.left, need.top ) )
		return( -1 );

	return( 0 );
}
Beispiel #5
0
/* Extract an area. Can just use pointers.
 */
static int
vips_extract_area_gen( VipsRegion *or, void *seq, void *a, void *b, 
	gboolean *stop )
{
	VipsRegion *ir = (VipsRegion *) seq;
	VipsExtractArea *extract = (VipsExtractArea *) b;
	VipsRect iarea;

	/* Ask for input we need. Translate from demand in or's space to
	 * demand in ir's space.
	 */
	iarea = or->valid;
	iarea.left += extract->left;
	iarea.top += extract->top;
	if( vips_region_prepare( ir, &iarea ) )
		return( -1 );

	/* Attach or to ir.
	 */
	if( vips_region_region( or, ir, &or->valid, iarea.left, iarea.top ) )
		return( -1 );
	
	return( 0 );
}
Beispiel #6
0
static int
vips_replicate_gen( VipsRegion *or, void *seq, void *a, void *b, 
	gboolean *stop )
{
	VipsRegion *ir = (VipsRegion *) seq;
	VipsImage *in = (VipsImage *) a;
	VipsRect *r = &or->valid;
	int twidth = in->Xsize;
	int theight = in->Ysize;

	int x, y;
	VipsRect tile;

	/* Find top left of tiles we need.
	 */
	int xs = (r->left / twidth) * twidth;
	int ys = (r->top / theight) * theight;

	/* The tile enclosing the top-left corner of the requested area.
	 */
	tile.left = xs;
	tile.top = ys;
	tile.width = twidth;
	tile.height = theight;

	/* If the request fits inside a single tile, we can just pointer-copy.
	 */
	if( vips_rect_includesrect( &tile, r ) ) {
		VipsRect irect;

		/* Translate request to input space.
		 */
		irect = *r;
		irect.left -= xs;
		irect.top -= ys;
		if( vips_region_prepare( ir, &irect ) )
			return( -1 );

		if( vips_region_region( or, ir, r, irect.left, irect.top ) )
			return( -1 );

		return( 0 );
	}

	for( y = ys; y < VIPS_RECT_BOTTOM( r ); y += theight )
		for( x = xs; x < VIPS_RECT_RIGHT( r ); x += twidth ) {
			VipsRect paint;

			/* Whole tile at x, y
			 */
			tile.left = x;
			tile.top = y;
			tile.width = twidth;
			tile.height = theight;

			/* Which parts touch the area of the output we are
			 * building.
			 */
			vips_rect_intersectrect( &tile, r, &paint );

			/* Translate back to ir coordinates.
			 */
			paint.left -= x;
			paint.top -= y;

			g_assert( !vips_rect_isempty( &paint ) );

			/* Render into or.
			 */
			if( vips_region_prepare_to( ir, or, &paint,
				paint.left + x,
				paint.top + y ) )
				return( -1 );
		}

	return( 0 );
}
Beispiel #7
0
static int
vips_embed_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
{
	VipsRegion *ir = (VipsRegion *) seq;
	VipsEmbed *embed = (VipsEmbed *) b;
	VipsRect *r = &or->valid;

	Rect ovl;
	int i;
	VipsPel *p;
	int plsk;

	/* Entirely within the input image? Generate the subimage and copy
	 * pointers.
	 */
	if( vips_rect_includesrect( &embed->rsub, r ) ) {
		VipsRect need;

		need = *r;
		need.left -= embed->x;
		need.top -= embed->y;
		if( vips_region_prepare( ir, &need ) ||
			vips_region_region( or, ir, r, need.left, need.top ) )
			return( -1 );

		return( 0 );
	}

	/* Does any of the input image appear in the area we have been asked 
	 * to make? Paste it in.
	 */
	vips_rect_intersectrect( r, &embed->rsub, &ovl );
	if( !vips_rect_isempty( &ovl ) ) {
		/* Paint the bits coming from the input image.
		 */
		ovl.left -= embed->x;
		ovl.top -= embed->y;
		if( vips_region_prepare_to( ir, or, &ovl, 
			ovl.left + embed->x, ovl.top + embed->y ) )
			return( -1 );
		ovl.left += embed->x;
		ovl.top += embed->y;
	}

	switch( embed->extend ) {
	case VIPS_EXTEND_BLACK:
	case VIPS_EXTEND_WHITE:
		VIPS_GATE_START( "vips_embed_gen: work1" );

		/* Paint the borders a solid value.
		 */
		for( i = 0; i < 8; i++ )
			vips_region_paint( or, &embed->border[i], 
				embed->extend == 0 ? 0 : 255 );

		VIPS_GATE_STOP( "vips_embed_gen: work1" );

		break;

	case VIPS_EXTEND_BACKGROUND:
		VIPS_GATE_START( "vips_embed_gen: work2" );

		/* Paint the borders a solid value.
		 */
		for( i = 0; i < 8; i++ )
			vips_region_paint_pel( or, &embed->border[i], 
				embed->ink ); 

		VIPS_GATE_STOP( "vips_embed_gen: work2" );

		break;

	case VIPS_EXTEND_COPY:
		/* Extend the borders.
		 */
		for( i = 0; i < 8; i++ ) {
			VipsRect todo;
			VipsRect edge;

			vips_rect_intersectrect( r, &embed->border[i], &todo );
			if( !vips_rect_isempty( &todo ) ) {
				vips_embed_find_edge( embed, &todo, i, &edge );

				/* Did we paint any of the input image? If we
				 * did, we can fetch the edge pixels from
				 * that.
				 */
				if( !vips_rect_isempty( &ovl ) ) {
					p = VIPS_REGION_ADDR( or, 
						edge.left, edge.top );
					plsk = VIPS_REGION_LSKIP( or );
				}
				else {
					/* No pixels painted ... fetch
					 * directly from the input image.
					 */
					edge.left -= embed->x;
					edge.top -= embed->y;
					if( vips_region_prepare( ir, &edge ) )
						return( -1 );
					p = VIPS_REGION_ADDR( ir,
						 edge.left, edge.top );
					plsk = VIPS_REGION_LSKIP( ir );
				}

				vips_embed_paint_edge( embed, 
					or, i, &todo, p, plsk );
			}
		}

		break;

	default:	
		g_assert( 0 );
	}

	return( 0 );
}
static int
vips_sequential_generate( VipsRegion *or, 
	void *seq, void *a, void *b, gboolean *stop )
{
	VipsSequential *sequential = (VipsSequential *) b;
	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( sequential );
        VipsRect *r = &or->valid;
	VipsRegion *ir = (VipsRegion *) seq;

	VIPS_DEBUG_MSG_GREEN( "thread %p request for line %d, height %d\n", 
		g_thread_self(), r->top, r->height );

	if( sequential->trace )
		vips_info( class->nickname, 
			"request for line %d, height %d", 
			r->top, r->height );

	VIPS_GATE_START( "vips_sequential_generate: wait" );

	g_mutex_lock( sequential->lock );

	VIPS_GATE_STOP( "vips_sequential_generate: wait" );

	VIPS_DEBUG_MSG_GREEN( "thread %p has lock ...\n", g_thread_self() ); 

	/* If we've seen an error, everything must stop.
	 */
	if( sequential->error ) {
		g_mutex_unlock( sequential->lock );
		return( -1 );
	}

	if( r->top > sequential->y_pos &&
		sequential->y_pos > 0 ) {
		/* This request is for stuff beyond the current read position, 
		 * and this is not the first request. We 
		 * stall for a while to give other threads time to catch up.
		 * 
		 * The stall can be cancelled by a signal on @ready.
		 *
		 * We don't stall forever, since an error would be better than
		 * deadlock, and we don't fail on timeout, since the timeout 
		 * may be harmless.
		 */

#ifdef HAVE_COND_INIT
		gint64 time;

		time = g_get_monotonic_time() + 
			STALL_TIME * G_TIME_SPAN_SECOND;
#else
		GTimeVal time;

		g_get_current_time( &time );
		g_time_val_add( &time, STALL_TIME * 1000000 );
#endif

		VIPS_DEBUG_MSG_GREEN( "thread %p stalling for up to %gs ...\n", 
			g_thread_self(), STALL_TIME ); 

		VIPS_GATE_START( "vips_sequential_generate: wait" );

		/* Exit the loop on timeout or condition passes. We have to
		 * be wary of spurious wakeups. 
		 */
		while( r->top > sequential->y_pos ) {
#ifdef HAVE_COND_INIT
			if( !g_cond_wait_until( sequential->ready, 
				sequential->lock, time ) )
				break;
#else
			if( !g_cond_timed_wait( sequential->ready, 
				sequential->lock, &time ) )
				break;
#endif

			/* We may have woken up because of an eval error.
			 */
			if( sequential->error ) {
				g_mutex_unlock( sequential->lock );
				return( -1 );
			}
		}

		VIPS_GATE_STOP( "vips_sequential_generate: wait" );

		VIPS_DEBUG_MSG_GREEN( "thread %p awake again ...\n", 
			g_thread_self() ); 
	}

	if( r->top > sequential->y_pos ) {
		/* This is a request for something some way down the image, 
		 * and we've 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, since it may be useful.
		 */
		VipsRect area;

		VIPS_DEBUG_MSG_GREEN( "thread %p skipping to line %d ...\n", 
			g_thread_self(),
			r->top );

		area.left = 0;
		area.top = sequential->y_pos;
		area.width = 1;
		area.height = r->top - sequential->y_pos;
		if( vips_region_prepare( ir, &area ) ) {
			VIPS_DEBUG_MSG( "thread %p error, unlocking #1 ...\n", 
				g_thread_self() ); 
			sequential->error = -1;
			g_cond_broadcast( sequential->ready );
			g_mutex_unlock( sequential->lock );
			return( -1 );
		}

		sequential->y_pos = VIPS_RECT_BOTTOM( &area );
	}

	/* This is a request for old or present pixels -- serve from cache.
	 * This may trigger further, sequential reads.
	 */
	VIPS_DEBUG_MSG_GREEN( "thread %p reading ...\n", g_thread_self() ); 
	if( vips_region_prepare( ir, r ) ||
		vips_region_region( or, ir, r, r->left, r->top ) ) {
		VIPS_DEBUG_MSG( "thread %p error, unlocking #2 ...\n", 
			g_thread_self() ); 
		sequential->error = -1;
		g_cond_broadcast( sequential->ready );
		g_mutex_unlock( sequential->lock );
		return( -1 );
	}

	if( VIPS_RECT_BOTTOM( r ) > sequential->y_pos ) {
		/* This request has moved the read point. Update it, and wake 
		 * up all stalled threads for a retry.
		 */
		sequential->y_pos = VIPS_RECT_BOTTOM( r );

		VIPS_DEBUG_MSG_GREEN( "thread %p updating y_pos to %d and "
			"waking stalled\n", 
			g_thread_self(),
			sequential->y_pos ); 

		g_cond_broadcast( sequential->ready );
	}

	VIPS_DEBUG_MSG_GREEN( "thread %p unlocking ...\n", g_thread_self() ); 

	g_mutex_unlock( sequential->lock );

	return( 0 );
}
Beispiel #9
0
static int
vips_ifthenelse_gen( VipsRegion *or, void *seq, void *client1, void *client2,
	gboolean *stop )
{
	VipsRegion **ir = (VipsRegion **) seq;
	VipsIfthenelse *ifthenelse = (VipsIfthenelse *) client2;
	VipsRect *r = &or->valid;
	int le = r->left;
	int to = r->top;
	int bo = VIPS_RECT_BOTTOM( r );

	VipsImage *c = ir[2]->im;
	VipsImage *a = ir[0]->im;

	int size, width;
	int i, x, y, z;

	int all0, alln0;

	if( c->Bands == 1 ) {
		/* Copying PEL-sized units with a one-band conditional.
		 */
		size = VIPS_IMAGE_SIZEOF_PEL( a );
		width = r->width;
	}
	else {
		/* Copying ELEMENT sized-units with an n-band conditional.
		 */
		size = VIPS_IMAGE_SIZEOF_ELEMENT( a );
		width = r->width * a->Bands;
	}

	if( vips_region_prepare( ir[2], r ) )
		return( -1 );

	/* Is the conditional all zero or all non-zero? We can avoid asking
	 * for one of the inputs to be calculated.
	 */
	all0 = *((PEL *) VIPS_REGION_ADDR( ir[2], le, to )) == 0;
	alln0 = *((PEL *) VIPS_REGION_ADDR( ir[2], le, to )) != 0;
	for( y = to; y < bo; y++ ) {
		PEL *p = (PEL *) VIPS_REGION_ADDR( ir[2], le, y );

		for( x = 0; x < width; x++ ) {
			all0 &= p[x] == 0;
			alln0 &= p[x] != 0;
		}

		if( !all0 && !alln0 )
			break;
	}

	if( alln0 ) {
		/* All non-zero. Point or at the then image.
		 */
		if( vips_region_prepare( ir[0], r ) ||
			vips_region_region( or, ir[0], r, r->left, r->top ) )
			return( -1 );
	}
	else if( all0 ) {
		/* All zero. Point or at the else image.
		 */
		if( vips_region_prepare( ir[1], r ) ||
			vips_region_region( or, ir[1], r, r->left, r->top ) )
			return( -1 );
	}
	else {
		/* Mix of set and clear ... ask for both then and else parts 
		 * and interleave.
		 */
		if( vips_region_prepare( ir[0], r ) || 
			vips_region_prepare( ir[1], r ) ) 
			return( -1 );

		for( y = to; y < bo; y++ ) {
			PEL *ap = (PEL *) VIPS_REGION_ADDR( ir[0], le, y );
			PEL *bp = (PEL *) VIPS_REGION_ADDR( ir[1], le, y );
			PEL *cp = (PEL *) VIPS_REGION_ADDR( ir[2], le, y );
			PEL *q = (PEL *) VIPS_REGION_ADDR( or, le, y );

			if( ifthenelse->blend ) {
				if( c->Bands == 1 ) 
					vips_blend1_buffer( q, 
						cp, ap, bp, r->width, a );
				else
					vips_blendn_buffer( q, 
						cp, ap, bp, r->width, a );
			}
			else {
				for( x = 0, i = 0; i < width; i++, x += size ) 
					if( cp[i] )
						for( z = x; z < x + size; z++ )
							q[z] = ap[z];
					else
						for( z = x; z < x + size; z++ )
							q[z] = bp[z];
			}
		}
	}

	return( 0 );
}
Beispiel #10
0
static int
vips_sequential_generate( VipsRegion *or, 
	void *seq, void *a, void *b, gboolean *stop )
{
	VipsSequential *sequential = (VipsSequential *) b;
	VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( sequential );
        VipsRect *r = &or->valid;
	VipsRegion *ir = (VipsRegion *) seq;

	VIPS_DEBUG_MSG( "thread %p request for %d lines, start line %d\n", 
		g_thread_self(), r->height, r->top );

	if( sequential->trace )
		vips_diag( class->nickname, 
			"request for %d lines, starting at line %d", 
			r->height, r->top );

	g_mutex_lock( sequential->lock );

	VIPS_DEBUG_MSG( "thread %p has lock ...\n", g_thread_self() ); 

	/* If we've seen an error, everything must stop.
	 */
	if( sequential->error ) {
		g_mutex_unlock( sequential->lock );
		return( -1 );
	}

	if( r->top > sequential->y_pos && 
		sequential->y_pos > 0 ) {
		/* We have started reading (y_pos > 0) and this request is for 
		 * stuff beyond that, stall for a short while to give other
		 * threads time to catch up.
		 *
		 * The stall can be cancelled by a signal on @ready.
		 */
		VIPS_DEBUG_MSG( "thread %p stalling for up to %gs ...\n", 
			g_thread_self(), STALL_TIME ); 
		vips_g_cond_timed_wait( sequential->ready, 
			sequential->lock, STALL_TIME * 1000000 );
		VIPS_DEBUG_MSG( "thread %p awake again ...\n", 
			g_thread_self() ); 
	}

	/* This is a request for something some way down the image, and we've
	 * 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,
	 * since it may be useful.
	 */
	if( r->top > sequential->y_pos ) {
		VipsRect area;

		VIPS_DEBUG_MSG( "thread %p skipping to line %d ...\n", 
			g_thread_self(),
			r->top );

		area.left = 0;
		area.top = sequential->y_pos;
		area.width = 1;
		area.height = r->top - sequential->y_pos;
		if( vips_region_prepare( ir, &area ) ) {
			VIPS_DEBUG_MSG( "thread %p error, unlocking ...\n", 
				g_thread_self() ); 
			sequential->error = -1;
			g_cond_broadcast( sequential->ready );
			g_mutex_unlock( sequential->lock );
			return( -1 );
		}

		sequential->y_pos = VIPS_RECT_BOTTOM( &area );
	}

	/* This is a request for old or present pixels -- serve from cache.
	 * This may trigger further, sequential reads.
	 */
	VIPS_DEBUG_MSG( "thread %p reading ...\n", g_thread_self() ); 
	if( vips_region_prepare( ir, r ) ||
		vips_region_region( or, ir, r, r->left, r->top ) ) {
		VIPS_DEBUG_MSG( "thread %p error, unlocking ...\n", 
			g_thread_self() ); 
		sequential->error = -1;
		g_cond_broadcast( sequential->ready );
		g_mutex_unlock( sequential->lock );
		return( -1 );
	}

	if( VIPS_RECT_BOTTOM( r ) > sequential->y_pos ) {
		/* This request has moved the read point. Update it, and wake 
		 * up all stalled threads for a retry.
		 */
		sequential->y_pos = VIPS_RECT_BOTTOM( r );

		VIPS_DEBUG_MSG( "thread %p updating y_pos to %d and "
			"waking stalled\n", 
			g_thread_self(),
			sequential->y_pos ); 

		g_cond_broadcast( sequential->ready );
	}

	VIPS_DEBUG_MSG( "thread %p unlocking ...\n", g_thread_self() ); 

	g_mutex_unlock( sequential->lock );

	return( 0 );
}
Beispiel #11
0
static int
vips_blend_gen( VipsRegion *or, void *seq, void *client1, void *client2,
	gboolean *stop )
{
	VipsRegion **ir = (VipsRegion **) seq;
	VipsRect *r = &or->valid;
	int le = r->left;
	int to = r->top;
	int bo = VIPS_RECT_BOTTOM( r );

	VipsImage *c = ir[2]->im;
	VipsImage *a = ir[0]->im;

	int x, y;
	int all0, all255;

	if( vips_region_prepare( ir[2], r ) )
		return( -1 );

	/* Is the conditional all zero or all 255? We can avoid asking
	 * for one of the inputs to be calculated.
	 */
	all0 = *VIPS_REGION_ADDR( ir[2], le, to ) == 0;
	all255 = *VIPS_REGION_ADDR( ir[2], le, to ) == 255;
	for( y = to; y < bo; y++ ) {
		VipsPel *p = VIPS_REGION_ADDR( ir[2], le, y );
		int width = r->width * c->Bands;

		for( x = 0; x < width; x++ ) {
			all0 &= p[x] == 0;
			all255 &= p[x] == 255;
		}

		if( !all0 && !all255 )
			break;
	}

	if( all255 ) {
		/* All 255. Point or at the then image.
		 */
		if( vips_region_prepare( ir[0], r ) ||
			vips_region_region( or, ir[0], r, r->left, r->top ) )
			return( -1 );
	}
	else if( all0 ) {
		/* All zero. Point or at the else image.
		 */
		if( vips_region_prepare( ir[1], r ) ||
			vips_region_region( or, ir[1], r, r->left, r->top ) )
			return( -1 );
	}
	else {
		/* Mix of set and clear ... ask for both then and else parts 
		 * and interleave.
		 */
		if( vips_region_prepare( ir[0], r ) || 
			vips_region_prepare( ir[1], r ) ) 
			return( -1 );

		for( y = to; y < bo; y++ ) {
			VipsPel *ap = VIPS_REGION_ADDR( ir[0], le, y );
			VipsPel *bp = VIPS_REGION_ADDR( ir[1], le, y );
			VipsPel *cp = VIPS_REGION_ADDR( ir[2], le, y );
			VipsPel *q = VIPS_REGION_ADDR( or, le, y );

			if( c->Bands == 1 ) 
				vips_blend1_buffer( q, cp, ap, bp, 
					r->width, a );
			else
				vips_blendn_buffer( q, cp, ap, bp, 
					r->width, a );
		}
	}

	return( 0 );
}