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