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