Exemple #1
0
/* Call a start function if no sequence is running on this VipsRegion.
 */
int
vips__region_start( VipsRegion *region )
{
	VipsImage *image = region->im;

        if( !region->seq && image->start_fn ) {
		VIPS_GATE_START( "vips__region_start: wait" );

                g_mutex_lock( image->sslock );

		VIPS_GATE_STOP( "vips__region_start: wait" );

                region->seq = image->start_fn( image, 
			image->client1, image->client2 );

                g_mutex_unlock( image->sslock );
 
                if( !region->seq ) {
#ifdef DEBUG
                        printf( "vips__region_start: "
				"start function failed for image %s",
                                image->filename );
#endif /*DEBUG*/

                        return( -1 );
                }
        }

        return( 0 );
}
Exemple #2
0
/* Call a stop function if a sequence is running in this VipsRegion. 
 */
void
vips__region_stop( VipsRegion *region )
{
	VipsImage *image = region->im;

        if( region->seq && image->stop_fn ) {
		int result;

		VIPS_GATE_START( "vips__region_stop: wait" );

                g_mutex_lock( image->sslock );

		VIPS_GATE_STOP( "vips__region_stop: wait" );

               	result = image->stop_fn( region->seq, 
			image->client1, image->client2 );

                g_mutex_unlock( image->sslock );

		/* stop function can return an error, but we have nothing we
		 * can really do with it, sadly.
		 */
		if( result )
                        vips_warn( "VipsRegion", 
				"stop callback failed for image %s", 
				image->filename );
 
                region->seq = NULL;
        }
}
Exemple #3
0
static int
vips_region_build( VipsObject *object )
{
	VipsRegion *region = VIPS_REGION( object );
	VipsImage *image = region->im;

	VIPS_DEBUG_MSG( "vips_region_build: %p\n", region );

	if( VIPS_OBJECT_CLASS( vips_region_parent_class )->build( object ) )
		return( -1 );

	vips__region_take_ownership( region );

	/* We're usually inside the ss lock anyway. But be safe ...
	 */
	VIPS_GATE_START( "vips_region_build: wait" );

	g_mutex_lock( image->sslock );

	VIPS_GATE_STOP( "vips_region_build: wait" );

	image->regions = g_slist_prepend( image->regions, region );

	g_mutex_unlock( image->sslock );

	return( 0 );
}
Exemple #4
0
static int
vips_colour_gen( VipsRegion *or, 
	void *seq, void *a, void *b, gboolean *stop )
{
	VipsRegion **ir = (VipsRegion **) seq;
	VipsColour *colour = VIPS_COLOUR( b ); 
	VipsColourClass *class = VIPS_COLOUR_GET_CLASS( colour ); 
	VipsRect *r = &or->valid;

	int i, y;
	VipsPel *p[MAX_INPUT_IMAGES], *q;

	for( i = 0; ir[i]; i++ ) 
		if( vips_region_prepare( ir[i], r ) ) 
			return( -1 );

	VIPS_GATE_START( "vips_colour_gen: work" ); 

	for( y = 0; y < r->height; y++ ) {
		for( i = 0; ir[i]; i++ )
			p[i] = VIPS_REGION_ADDR( ir[i], r->left, r->top + y );
		p[i] = NULL;
		q = VIPS_REGION_ADDR( or, r->left, r->top + y );

		class->process_line( colour, q, p, r->width );
	}

	VIPS_GATE_STOP( "vips_colour_gen: work" ); 

	return( 0 );
}
Exemple #5
0
static int
vips_bandary_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
{
	VipsRegion **ir = (VipsRegion **) seq;
	VipsBandary *bandary = (VipsBandary *) b;
	VipsBandaryClass *class = VIPS_BANDARY_GET_CLASS( bandary ); 
	VipsRect *r = &or->valid;

	VipsPel *p[MAX_INPUT_IMAGES], *q;
	int y, i;

	for( i = 0; i < bandary->n; i++ ) {
		if( vips_region_prepare( ir[i], r ) )
			return( -1 );
		p[i] = VIPS_REGION_ADDR( ir[i], r->left, r->top );
	}
	p[i] = NULL;
	q = VIPS_REGION_ADDR( or, r->left, r->top );

	VIPS_GATE_START( "vips_bandary_gen: work" ); 

	for( y = 0; y < r->height; y++ ) {
		class->process_line( bandary, q, p, r->width );

		for( i = 0; i < bandary->n; i++ )
			p[i] += VIPS_REGION_LSKIP( ir[i] );
		q += VIPS_REGION_LSKIP( or );
	}

	VIPS_GATE_STOP( "vips_bandary_gen: work" ); 

	return( 0 );
}
Exemple #6
0
/* If a region is being created in one thread (eg. the main thread) and then
 * used in another (eg. a worker thread), the new thread needs to tell VIPS
 * to stop sanity g_assert() fails. The previous owner needs to
 * vips__region_no_ownership() before we can call this.
 */
void
vips__region_take_ownership( VipsRegion *region )
{
	/* Lock so that there's a memory barrier with the thread doing the
	 * vips__region_no_ownership() before us.
	 */
	VIPS_GATE_START( "vips__region_take_ownership: wait" );

	g_mutex_lock( region->im->sslock );

	VIPS_GATE_STOP( "vips__region_take_ownership: wait" );

	if( region->thread != g_thread_self() ) {
		g_assert( region->thread == NULL );

		/* We don't want to move shared buffers: the other region 
		 * using this buffer will still be on the other thread. 
		 * Not sure if this will ever happen: if it does, we'll 
		 * need to dup the buffer.
		 */
		g_assert( !region->buffer || 
			region->buffer->ref_count == 1 );

		region->thread = g_thread_self();
	}

	g_mutex_unlock( region->im->sslock );
}
Exemple #7
0
/* Call a start function if no sequence is running on this VipsRegion.
 */
int
vips__region_start( VipsRegion *region )
{
	VipsImage *image = region->im;

        if( !region->seq && image->start_fn ) {
		VIPS_GATE_START( "vips__region_start: wait" );

                g_mutex_lock( image->sslock );

		VIPS_GATE_STOP( "vips__region_start: wait" );

                region->seq = image->start_fn( image, 
			image->client1, image->client2 );

                g_mutex_unlock( image->sslock );
 
                if( !region->seq ) {
                        vips_error( "vips__region_start", 
				_( "start function failed for image %s" ),
                                image->filename );
                        return( -1 );
                }
        }

        return( 0 );
}
Exemple #8
0
static int
vips_shrinkh_gen( VipsRegion *or, void *vseq, 
	void *a, void *b, gboolean *stop )
{
	VipsShrinkhSequence *seq = (VipsShrinkhSequence *) vseq;
	VipsShrinkh *shrink = (VipsShrinkh *) b;
	VipsRegion *ir = seq->ir;
	VipsRect *r = &or->valid;

	int y;

	/* How do we chunk up the image? We don't want to prepare the whole of
	 * the input region corresponding to *r since it could be huge. 
	 *
	 * Request input a line at a time. 
	 *
	 * We don't chunk horizontally. We want "vips shrink x.jpg b.jpg 100
	 * 100" to run sequentially. If we chunk horizontally, we will fetch
	 * 100x100 lines from the top of the image, then 100x100 100 lines
	 * down, etc. for each thread, then when they've finished, fetch
	 * 100x100, 100 pixels across from the top of the image. This will
	 * break sequentiality. 
	 */

#ifdef DEBUG
	printf( "vips_shrinkh_gen: generating %d x %d at %d x %d\n",
		r->width, r->height, r->left, r->top ); 
#endif /*DEBUG*/

	for( y = 0; y < r->height; y ++ ) { 
		VipsRect s;

		s.left = r->left * shrink->xshrink;
		s.top = r->top + y;
		s.width = ceil( r->width * shrink->xshrink );
		s.height = 1;
#ifdef DEBUG
		printf( "shrinkh_gen: requesting line %d\n", s.top ); 
#endif /*DEBUG*/
		if( vips_region_prepare( ir, &s ) )
			return( -1 );

		VIPS_GATE_START( "vips_shrinkh_gen: work" ); 

		vips_shrinkh_gen2( shrink, seq, 
			or, ir, 
			r->left, r->top + y, r->width );

		VIPS_GATE_STOP( "vips_shrinkh_gen: work" ); 
	}

	return( 0 );
}
Exemple #9
0
/* Paint r of region or. It's a border area, lying entirely within 
 * embed->border[i]. p points to the top-left source pixel to fill with. 
 * plsk is the line stride.
 */
static void
vips_embed_paint_edge( VipsEmbed *embed, 
	VipsRegion *or, int i, VipsRect *r, VipsPel *p, int plsk )
{
	const int bs = VIPS_IMAGE_SIZEOF_PEL( embed->in );

	VipsRect todo;
	VipsPel *q;
	int y;

	VIPS_GATE_START( "vips_embed_paint_edge: work" );

	/* Pixels left to paint.
	 */
	todo = *r;

	/* Corner pieces ... copy the single pixel to paint the top line of
	 * todo, then use the line copier below to paint the rest of it.
	 */
	if( i > 3 ) {
		q = VIPS_REGION_ADDR( or, todo.left, todo.top );
		vips_embed_copy_pixel( embed, q, p, todo.width );

		p = q;
		todo.top += 1;
		todo.height -= 1;
	}

	if( i == 1 || i == 3 ) {
		/* Vertical line of pixels to copy.
		 */
		for( y = 0; y < todo.height; y++ ) {
			q = VIPS_REGION_ADDR( or, todo.left, todo.top + y );
			vips_embed_copy_pixel( embed, q, p, todo.width );
			p += plsk;
		}
	}
	else {
		/* Horizontal line of pixels to copy.
		 */
		for( y = 0; y < todo.height; y++ ) {
			q = VIPS_REGION_ADDR( or, todo.left, todo.top + y );
			memcpy( q, p, bs * todo.width );
		}
	}

	VIPS_GATE_STOP( "vips_embed_paint_edge: work" );
}
Exemple #10
0
/* Call this from the relinquishing thread. Removes the buffer (if any) from
 * this thread's buffer cache.
 */
void
vips__region_no_ownership( VipsRegion *region )
{
	VIPS_GATE_START( "vips__region_no_ownership: wait" );

	g_mutex_lock( region->im->sslock );

	VIPS_GATE_STOP( "vips__region_no_ownership: wait" );

	vips__region_check_ownership( region );

	region->thread = NULL;
	if( region->buffer )
		vips_buffer_undone( region->buffer );

	g_mutex_unlock( region->im->sslock );
}
Exemple #11
0
static void
vips_region_dispose( GObject *gobject )
{
	VipsRegion *region = VIPS_REGION( gobject );
	VipsImage *image = region->im;

#ifdef VIPS_DEBUG
	VIPS_DEBUG_MSG( "vips_region_dispose: " );
	vips_object_print_name( VIPS_OBJECT( gobject ) );
	VIPS_DEBUG_MSG( "\n" );
#endif /*VIPS_DEBUG*/

	vips_object_preclose( VIPS_OBJECT( gobject ) );

        /* Stop this sequence.
         */
        vips__region_stop( region );

	/* Free any attached memory.
	 */
	VIPS_FREEF( vips_window_unref, region->window );
	VIPS_FREEF( vips_buffer_unref, region->buffer );

	/* Detach from image. 
	 */
	VIPS_GATE_START( "vips_region_dispose: wait" );

	g_mutex_lock( image->sslock );

	VIPS_GATE_STOP( "vips_region_dispose: wait" );

	image->regions = g_slist_remove( image->regions, region );

	g_mutex_unlock( image->sslock );

	region->im = NULL;

	g_object_unref( image );

	G_OBJECT_CLASS( vips_region_parent_class )->dispose( gobject );
}
Exemple #12
0
static int
vips_arithmetic_gen( VipsRegion *or,
                     void *seq, void *a, void *b, gboolean *stop )
{
    VipsRegion **ir = (VipsRegion **) seq;
    VipsArithmetic *arithmetic = VIPS_ARITHMETIC( b );
    VipsArithmeticClass *class = VIPS_ARITHMETIC_GET_CLASS( arithmetic );
    Rect *r = &or->valid;

    VipsPel *p[MAX_INPUT_IMAGES], *q;
    int i, y;

    /* Prepare all input regions and make buffer pointers.
     */
    for( i = 0; ir[i]; i++ ) {
        if( vips_region_prepare( ir[i], r ) )
            return( -1 );
        p[i] = (VipsPel *) VIPS_REGION_ADDR( ir[i], r->left, r->top );
    }
    p[i] = NULL;
    q = (VipsPel *) VIPS_REGION_ADDR( or, r->left, r->top );

    VIPS_GATE_START( "vips_arithmetic_gen: work" );

    for( y = 0; y < r->height; y++ ) {
        class->process_line( arithmetic, q, p, r->width );

        for( i = 0; ir[i]; i++ )
            p[i] += VIPS_REGION_LSKIP( ir[i] );
        q += VIPS_REGION_LSKIP( or );
    }

    VIPS_GATE_STOP( "vips_arithmetic_gen: work" );

    return( 0 );
}
Exemple #13
0
static int
vips_shrink2_gen( VipsRegion *or, void *vseq, void *a, void *b, gboolean *stop )
{
    VipsShrink2Sequence *seq = (VipsShrink2Sequence *) vseq;
    VipsShrink2 *shrink = (VipsShrink2 *) b;
    VipsRegion *ir = seq->ir;
    VipsRect *r = &or->valid;

    /* How do we chunk up the image? We don't want to prepare the whole of
     * the input region corresponding to *r since it could be huge.
     *
     * Each pixel of *r will depend on roughly mw x mh
     * pixels, so we walk *r in chunks which map to the tile size.
     *
     * Make sure we can't ask for a zero step.
     *
     * We don't chunk horizontally. We want "vips shrink x.jpg b.jpg 100
     * 100" to run sequentially. If we chunk horizontally, we will fetch
     * 100x100 lines from the top of the image, then 100x100 100 lines
     * down, etc. for each thread, then when they've finished, fetch
     * 100x100, 100 pixels across from the top of the image. This will
     * break sequentiality.
     */
    int ystep = shrink->mh > VIPS__TILE_HEIGHT ?
                1 : VIPS__TILE_HEIGHT / shrink->mh;

    int y;

#ifdef DEBUG
    printf( "vips_shrink2_gen: generating %d x %d at %d x %d\n",
            r->width, r->height, r->left, r->top );
#endif /*DEBUG*/

    for( y = 0; y < r->height; y += ystep ) {
        /* Clip the this rect against the demand size.
         */
        int height = VIPS_MIN( ystep, r->height - y );

        VipsRect s;

        s.left = r->left * shrink->xshrink;
        s.top = (r->top + y) * shrink->yshrink;
        s.width = ceil( r->width * shrink->xshrink );
        s.height = ceil( height * shrink->yshrink );
#ifdef DEBUG
        printf( "shrink_gen: requesting %d x %d at %d x %d\n",
                s.width, s.height, s.left, s.top );
#endif /*DEBUG*/
        if( vips_region_prepare( ir, &s ) )
            return( -1 );

        VIPS_GATE_START( "vips_shrink2_gen: work" );

        vips_shrink2_gen2( shrink, seq,
                           or, ir,
                           r->left, r->top + y, r->width, height );

        VIPS_GATE_STOP( "vips_shrink2_gen: work" );
    }

    return( 0 );
}
Exemple #14
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 );
}
Exemple #15
0
static int
read_jpeg_generate( VipsRegion *or, 
	void *seq, void *a, void *b, gboolean *stop )
{
        VipsRect *r = &or->valid;
	ReadJpeg *jpeg = (ReadJpeg *) a;
	struct jpeg_decompress_struct *cinfo = &jpeg->cinfo;
	int sz = cinfo->output_width * cinfo->output_components;

	int y;

#ifdef DEBUG
	printf( "read_jpeg_generate: %p line %d, %d rows\n", 
		g_thread_self(), r->top, r->height );
#endif /*DEBUG*/

	VIPS_GATE_START( "read_jpeg_generate: work" );

	/* 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 );

	/* Tiles should always be on a 8-pixel boundary.
	 */
	g_assert( r->top % 8 == 0 );

	/* Tiles should always be a strip in height, unless it's the final
	 * strip.
	 */
	g_assert( r->height == VIPS_MIN( 8, or->im->Ysize - r->top ) ); 

	/* And check that y_pos is correct. It should be, since we are inside
	 * a vips_sequential().
	 */
	if( r->top != jpeg->y_pos ) {
		vips_error( "VipsJpeg", 
			_( "out of order read at line %d" ), jpeg->y_pos );
		return( -1 );
	}

	/* Here for longjmp() from vips__new_error_exit().
	 */
	if( setjmp( jpeg->eman.jmp ) ) 
		return( -1 );

	for( y = 0; y < r->height; y++ ) {
		JSAMPROW row_pointer[1];

		row_pointer[0] = (JSAMPLE *) 
			VIPS_REGION_ADDR( or, 0, r->top + y );

		jpeg_read_scanlines( cinfo, &row_pointer[0], 1 );

		if( jpeg->invert_pels ) {
			int x;

			for( x = 0; x < sz; x++ )
				row_pointer[0][x] = 255 - row_pointer[0][x];
		}

		jpeg->y_pos += 1; 
	}

	VIPS_GATE_STOP( "read_jpeg_generate: work" );

	return( 0 );
}
Exemple #16
0
static int
vips_affine_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
{
	VipsRegion *ir = (VipsRegion *) seq;
	const VipsAffine *affine = (VipsAffine *) b;
	const VipsImage *in = (VipsImage *) a;
	const int window_size = 
		vips_interpolate_get_window_size( affine->interpolate );
	const int window_offset = 
		vips_interpolate_get_window_offset( affine->interpolate );
	const VipsInterpolateMethod interpolate = 
		vips_interpolate_get_method( affine->interpolate );

	/* Area we generate in the output image.
	 */
	const VipsRect *r = &or->valid;
	const int le = r->left;
	const int ri = VIPS_RECT_RIGHT( r );
	const int to = r->top;
	const int bo = VIPS_RECT_BOTTOM( r );

	const VipsRect *iarea = &affine->trn.iarea;
	const VipsRect *oarea = &affine->trn.oarea;

	int ps = VIPS_IMAGE_SIZEOF_PEL( in );
	int x, y, z;
	
	VipsRect image, want, need, clipped;

#ifdef DEBUG_VERBOSE
	printf( "vips_affine_gen: "
		"generating left=%d, top=%d, width=%d, height=%d\n", 
		r->left,
		r->top,
		r->width,
		r->height );
#endif /*DEBUG_VERBOSE*/

	/* We are generating this chunk of the transformed image. This takes
	 * us to space 4.
	 */
	want = *r;
	want.left += oarea->left;
	want.top += oarea->top;

	/* Find the area of the input image we need. This takes us to space 3. 
	 */
	vips__transform_invert_rect( &affine->trn, &want, &need );

	/* That does round-to-nearest, because it has to stop rounding errors
	 * growing images unexpectedly. We need round-down, so we must
	 * add half a pixel along the left and top. But we are int :( so add 1
	 * pixel. 
	 *
	 * Add an extra line along the right and bottom as well, for rounding.
	 */
	vips_rect_marginadjust( &need, 1 );

	/* We need to fetch a larger area for the interpolator.
	 */
	need.left -= window_offset;
	need.top -= window_offset;
	need.width += window_size - 1;
	need.height += window_size - 1;

	/* Now go to space 2, the expanded input image. This is the one we
	 * read pixels from. 
	 */
	need.left += window_offset;
	need.top += window_offset;

	/* Clip against the size of (2).
	 */
	image.left = 0;
	image.top = 0;
	image.width = in->Xsize;
	image.height = in->Ysize;
	vips_rect_intersectrect( &need, &image, &clipped );

#ifdef DEBUG_VERBOSE
	printf( "vips_affine_gen: "
		"preparing left=%d, top=%d, width=%d, height=%d\n", 
		clipped.left,
		clipped.top,
		clipped.width,
		clipped.height );
#endif /*DEBUG_VERBOSE*/

	if( vips_rect_isempty( &clipped ) ) {
		vips_region_black( or );
		return( 0 );
	}
	if( vips_region_prepare( ir, &clipped ) )
		return( -1 );

	VIPS_GATE_START( "vips_affine_gen: work" ); 

	/* Resample! x/y loop over pixels in the output image (5).
	 */
	for( y = to; y < bo; y++ ) {
		/* Input clipping rectangle. We offset this so we can clip in
		 * space 2. 
		 */
		const int ile = iarea->left + window_offset;
		const int ito = iarea->top + window_offset;
		const int iri = ile + iarea->width;
		const int ibo = ito + iarea->height;

		/* Derivative of matrix.
		 */
		const double ddx = affine->trn.ia;
		const double ddy = affine->trn.ic;

		/* Continuous cods in transformed space.
		 */
		const double ox = le + oarea->left - affine->trn.odx;
		const double oy = y + oarea->top - affine->trn.ody;

		/* Continuous cods in input space.
		 */
		double ix, iy;

		VipsPel *q;

		/* To (3).
		 */
		ix = affine->trn.ia * ox + affine->trn.ib * oy;
		iy = affine->trn.ic * ox + affine->trn.id * oy;

		/* And the input offset in (3). 
		 */
		ix -= affine->trn.idx;
		iy -= affine->trn.idy;

		/* Finally to 2. 
		 */
		ix += window_offset;
		iy += window_offset;

		q = VIPS_REGION_ADDR( or, le, y );

		for( x = le; x < ri; x++ ) {
			int fx, fy; 	

			fx = FAST_PSEUDO_FLOOR( ix );
			fy = FAST_PSEUDO_FLOOR( iy );

			/* Clip against iarea.
			 */
			if( fx >= ile &&
				fx < iri &&
				fy >= ito &&
				fy < ibo ) {
				/* Verify that we can read the whole stencil.
				 * With DEBUG on this will range-check.
				 */
				g_assert( VIPS_REGION_ADDR( ir, 
					(int) ix - window_offset,
					(int) iy - window_offset ) );
				g_assert( VIPS_REGION_ADDR( ir, 
					(int) ix - window_offset + 
						window_size - 1,
					(int) iy - window_offset + 
						window_size - 1 ) );

				interpolate( affine->interpolate, 
					q, ir, ix, iy );
			}
			else {
				for( z = 0; z < ps; z++ ) 
					q[z] = 0;
			}

			ix += ddx;
			iy += ddy;
			q += ps;
		}
	}

	VIPS_GATE_STOP( "vips_affine_gen: work" ); 

	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 );
}
Exemple #18
0
static int
vips_mapim_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
{
	VipsRect *r = &or->valid;
	VipsRegion **ir = (VipsRegion **) seq;
	const VipsImage **in_array = (const VipsImage **) a;
	const VipsMapim *mapim = (VipsMapim *) b; 
	const VipsResample *resample = VIPS_RESAMPLE( mapim );
	const VipsImage *in = in_array[0];
	const int window_size = 
		vips_interpolate_get_window_size( mapim->interpolate );
	const int window_offset = 
		vips_interpolate_get_window_offset( mapim->interpolate );
	const VipsInterpolateMethod interpolate = 
		vips_interpolate_get_method( mapim->interpolate );
	const int ps = VIPS_IMAGE_SIZEOF_PEL( in );
	const int clip_width = resample->in->Xsize;
	const int clip_height = resample->in->Ysize;

	VipsRect bounds, image, clipped;
	int x, y, z;
	
#ifdef DEBUG_VERBOSE
	printf( "vips_mapim_gen: "
		"generating left=%d, top=%d, width=%d, height=%d\n", 
		r->left,
		r->top,
		r->width,
		r->height );
#endif /*DEBUG_VERBOSE*/

	/* Fetch the chunk of the mapim image we need, and find the max/min in
	 * x and y.
	 */
	if( vips_region_prepare( ir[1], r ) )
		return( -1 );

	VIPS_GATE_START( "vips_mapim_gen: work" ); 

	vips_mapim_region_minmax( ir[1], r, &bounds ); 

	VIPS_GATE_STOP( "vips_mapim_gen: work" ); 

	/* The bounding box of that area is what we will need from @in. Add
	 * enough for the interpolation stencil as well.
	 * 
	 */
	bounds.width += window_size - 1;
	bounds.height += window_size - 1;

	/* Clip against the expanded image.
	 */
	image.left = 0;
	image.top = 0;
	image.width = in->Xsize;
	image.height = in->Ysize;
	vips_rect_intersectrect( &bounds, &image, &clipped );

#ifdef DEBUG_VERBOSE
	printf( "vips_mapim_gen: "
		"preparing left=%d, top=%d, width=%d, height=%d\n", 
		clipped.left,
		clipped.top,
		clipped.width,
		clipped.height );
#endif /*DEBUG_VERBOSE*/

	if( vips_rect_isempty( &clipped ) ) {
		vips_region_black( or );
		return( 0 );
	}
	if( vips_region_prepare( ir[0], &clipped ) )
		return( -1 );

	VIPS_GATE_START( "vips_mapim_gen: work" ); 

	/* Resample! x/y loop over pixels in the output image (5).
	 */
	for( y = 0; y < r->height; y++ ) {
		VipsPel * restrict p = 
			VIPS_REGION_ADDR( ir[1], r->left, y + r->top );
		VipsPel * restrict q = 
			VIPS_REGION_ADDR( or, r->left, y + r->top );

		switch( ir[1]->im->BandFmt ) {
		case VIPS_FORMAT_UCHAR: 	
			ULOOKUP( unsigned char ); break; 
		case VIPS_FORMAT_CHAR: 	
			LOOKUP( signed char ); break; 
		case VIPS_FORMAT_USHORT: 
			ULOOKUP( unsigned short ); break; 
		case VIPS_FORMAT_SHORT: 	
			LOOKUP( signed short ); break; 
		case VIPS_FORMAT_UINT: 	
			ULOOKUP( unsigned int ); break; 
		case VIPS_FORMAT_INT: 	
			LOOKUP( signed int ); break; 

		case VIPS_FORMAT_FLOAT: 		
		case VIPS_FORMAT_COMPLEX: 
			LOOKUP( float ); break; 
			break;

		case VIPS_FORMAT_DOUBLE:	
		case VIPS_FORMAT_DPCOMPLEX: 
			LOOKUP( double ); break;

		default:
			g_assert_not_reached();
		}
	}

	VIPS_GATE_STOP( "vips_mapim_gen: work" ); 

	return( 0 );
}